​CTF逆向——常规逆向篇(下)

CTF逆向——常规逆向篇(下)

题目:

  1. CrackMe.exe(NSCTF reverse第一题)
  2. WHCTF2017 reverse
  3. HCTF reverse(第一题)

CrackMe.exe(NSCTF reverse)

首先查壳

并没有壳,IDA打开,找到main函数,如下所示:

这里可以看到,只要sub_401005函数返回1,说明输入的flag正确,点进入查看

很简单,flag已经在这里给出了,

flag: flag{wow_flag_is_here}

WHCTF2017 reverse

首先查壳

并没有壳,拖去IDA分析,可以看到main函数如下

很简单的一个逻辑,首先将judge的每一位数据和0xC进行异或得到judge函数的原本形式,然后开始要求用户输入flag,输入的flag限制条件为:

(1)长度为14

(2)满足Judge函数的返回值为真

接下来在hex view中找到judge的十六进制数据,复制下来,写个脚本分别和0xC进行异或,再F2复制回去。

修改前

修改后

接下来就可以去查看judge的函数了,如下所示:

signed __int64 __fastcall judge(__int64 a1)
{
  char v2; // [rsp+8h] [rbp-20h]
  char v3; // [rsp+9h] [rbp-1Fh]
  char v4; // [rsp+Ah] [rbp-1Eh]
  char v5; // [rsp+Bh] [rbp-1Dh]
  char v6; // [rsp+Ch] [rbp-1Ch]
  char v7; // [rsp+Dh] [rbp-1Bh]
  char v8; // [rsp+Eh] [rbp-1Ah]
  char v9; // [rsp+Fh] [rbp-19h]
  char v10; // [rsp+10h] [rbp-18h]
  char v11; // [rsp+11h] [rbp-17h]
  char v12; // [rsp+12h] [rbp-16h]
  char v13; // [rsp+13h] [rbp-15h]
  char v14; // [rsp+14h] [rbp-14h]
  char v15; // [rsp+15h] [rbp-13h]
  int i; // [rsp+24h] [rbp-4h]
  v2 = 102;
  v3 = 109;
  v4 = 99;
  v5 = 100;
  v6 = 127;
  v7 = 107;
  v8 = 55;
  v9 = 100;
  v10 = 59;
  v11 = 86;
  v12 = 96;
  v13 = 59;
  v14 = 110;
  v15 = 112;
  for ( i = 0; i <= 13; ++i )
    *(i + a1) ^= i;
  for ( i = 0; i <= 13; ++i )
  {
    if ( *(i + a1) != *(&v2 + i) )
      return 0LL;
  }
  return 1LL;
}

可以看到要令其返回1,只需要将用户输入的数据和处理后的数组一致即可,而处理数组的过程实际上就是将v2-v15分别与0-13进行异或,接下来就可以写脚本取得flag

flag = ''
 c = [0x66,0x6d,0x63,0x64,0x7f,0x6b,0x37,0x64,0x3b,0x56,0x60,0x3b,0x6e,0x70]
 for i in range(len(c)):
     flag += chr(c[i]^i)
 print flag
Flag:flag{n1c3_j0b}

HCTF reverse

首先查壳

没壳,拖进IDA,打开main函数,如图所示

跟进main_0函数,但是报错

Ok,在汇编界面找到main_0函数

选中整个main_0区域,点击u,再点击确定

再点击p,令IDA重新生成函数,接下来就可F5查看了,如下所示:

__int64 main_0()
{
  int v0; // edx
  __int64 v1; // ST08_8
  char v3; // [esp+0h] [ebp-11Ch]
  bool v4; // [esp+Fh] [ebp-10Dh]
  char v5; // [esp+D7h] [ebp-45h]
  int i; // [esp+E0h] [ebp-3Ch]
  bool v7; // [esp+EFh] [ebp-2Dh]
  bool v8; // [esp+FBh] [ebp-21h]
  bool v9; // [esp+107h] [ebp-15h]
  HMODULE v10; // [esp+110h] [ebp-Ch]
  v10 = GetModuleHandleW(0);
  sub_4113F7("Welcome to HCTF 2017\n\n", v3);
  sub_4113F7("Mark.09 is hijacking Shinji Ikari now...\n\n", v3);
  sub_4113F7("Check User: \n", v3);
  sub_411154("%s", (unsigned int)&Str);
  if ( !sub_411316() )
  {
    sub_41114A();
    exit(0);
  }
  sub_4111F9();
  sub_4113F7("Check Start Code: \n", v3);
  sub_411154("%s", byte_41B4F0, 128);
  while ( getchar() != 10 )
    ;
  if ( j_strlen(byte_41B4F0) != 35 )
  {
    sub_411398();
    sub_4110FF();
    exit(0);
  }
  sub_41114F(&unk_41B570, byte_41B4F0);
  sub_4110EB(sub_41105A, &sub_4111EA, dword_41B780, dword_41B784);
  if ( sub_411361(1) )
  {
    sub_411398();
    sub_4110FF();
    exit(0);
  }
  v4 = sub_4110EB(sub_411023, sub_41139D, dword_41B770, dword_41B774) != 0;
  v9 = v4;
  sub_411023(byte_41B5F0, &unk_41B570);
  sub_411258(dword_41B770, dword_41B774, 204);
  if ( sub_411361(2) )
  {
    sub_411398();
    sub_4110FF();
    exit(0);
  }
  v4 = sub_4110EB(sub_41106E, &sub_411046, dword_41B778, dword_41B77C) != 0;
  v8 = v4;
  sub_41106E(byte_41B670, &unk_41B570);
  sub_411258(dword_41B778, dword_41B77C, 205);
  if ( sub_411361(3) )
  {
    sub_411398();
    sub_4110FF();
    exit(0);
  }
  v4 = sub_4110EB(sub_41105A, &sub_4111EA, dword_41B780, dword_41B784) != 0;
  v7 = v4;
  sub_41105A(byte_41B6F0, &unk_41B570);
  sub_411258(dword_41B780, dword_41B784, 221);
  for ( i = 0; i < 7; ++i )
  {
    byte_41B577[i] = byte_41B5F0[i];
    byte_41B57E[i] = byte_41B670[i];
    byte_41B585[i] = byte_41B6F0[i];
  }
  if ( sub_411447(&unk_41B570, &unk_41B0DC) )
  {
    MessageBoxA(0, "> DETONATION FUNCTION\n    READY", "WILLE", 0);
    sub_4113F7("[Y/N]?\n", v3);
    sub_411154("%c", &v5, 1);
    if ( v5 != 89 && v5 != 121 )
    {
      sub_411398();
      sub_4110FF();
    }
    else
    {
      sub_411082();
      sub_4113F7("Prevent IMPACT success\n", v3);
    }
  }
  else
  {
    sub_411398();
    sub_4110FF();
  }
  system("pause");
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}

首先我们看到检查用户输入的user是否满足函数sub_411316,若满足,则继续,否则退出。点进该函数查看,如下所示:

第一个for循环的作用实际上就是将用户输入的user前后颠倒,下面是证明过程

A = A^B
B = B^A = B^(A^B) = A
A = A^B = (A^B)^A = B

然后第二个for循环对字符串进行一定的操作,然后再和v4-v14进行比较,若都一致,则说明用户输入的user正确,下面为求user的脚本

user_check = [164,169,170,190,188,185,179,169,190,216,190]
 user_name = ""
 for i in range(len(user_check)):
     user_name += chr(user_check[i]^((((i^0x76)-52)^0x80)+43))
 print user_name[::-1]

User为M.KATSURAGI

接下来回到主函数,我们看到sub_411398函数和sub_4110FF两个函数出现的特别多次,查看一下两个函数,可以知道这两个函数的作用是当用户输入错误时就通过这两个函数来弹窗,告诉用户输入错误。所以我们就可以通过避开这两个函数,找出一条通往正确结果的逻辑路径。

接下来我们来分析一下用户输入flag会经过什么操作,首先查看输入的长度是否为35

接下来查看sub_4114f函数,因为用户输入的字符串作为其参数传进去,然后它又传到了以下函数中:

可以看到它将用户输入的每一个字符的ascii码值与0x76异或,传给了地址为unk_41B570的字符串处,后面的处理都是对这个地址的字符串进行处理,所以不用再关心保存用户输入字符串的地址了。接下来紧跟着的sub_4110EB的参数中并没有unk_41B570,所以暂时不管它,我们继续跟进有使用unk_41B570这个地址的操作。

接下来,我们能看到三个几乎完全一样的部分

而且这三个部分也对地址unk_41B570处的字符串进行操作,所以我们点进去查看,首先是sub_411023函数,将unk_41B570处的字符串第7位开始后的七个字符进行一定的处理,保存在数组byte_41B5F0处

sub_41106E函数,将unk_41B570处的字符串第14位开始后的七个字符进行一定的处理,保存在数组byte_41B670处

Sub_41105A函数,将unk_41B570处的字符串第21位开始后的七个字符进行一定的处理,保存在数组byte_41B6F0处

接下来再查看下面代码

刚才的三个数组都保存到另外三个数组处,这个地址看着很熟悉,我们发现41B570刚好就是我们用户输入字符串的地址,所以上面我们分析的代码就是将用户输入字符串中第7位到第27位的字符进行一定的操作。最后再与unk_41B0DC处的字符比较是否相等,若相等,则输入的flag正确,因此我们就可以根据以上的分析,写出获取flag的脚本

a1 = [0x1E ,0x15 ,0x02 ,0x10,0x0D ,0x48 ,0x48 ]
 a2 = [0x6F ,0xDD ,0xDD ,0x48,0x64 ,0x63 ,0xD7 ]
 a3 = [0x2E ,0x2C ,0xFE ,0x6A,0x6D ,0x2A ,0xF2 ]
 a4 = [0x6F ,0x9A ,0x4D ,0x8B,0x4B ,0x1A ,0xEA ]
 a5 = [0x43 ,0x42 ,0x42 ,0x42 ,0x44 ,0x47 ,0x0B]
 flag = ""
 
 for i in range(7):
         a1[i] = a1[i]^0x76
         a5[i] = a5[i] ^0x76
 
 for i in range(7):
     for j in range(33, 127):
         k1 = j ^ 0xad ^0x76
         k1 = 2 * k1 & 0xaa | ((k1 & 0xaa) >> 1)
         if k1 == a2[i]:
             a2[i] = j
             break
 
 for i in range(7):
     for j in range(33, 127):
         k2 = j ^ 0xbe^0x76
         k2 = (4 * k2 & 0xcc) | ((k2 & 0xcc) >> 2)
         if k2 == a3[i]:
             a3[i] = j
             break
 
 for i in range(7):
     for j in range(33, 127):
         k3 = j ^ 0xef^0x76
         k3 = (16 * k3 & 0xf0) | ((k3 & 0xf0) >> 4)
         if k3 == a4[i]:
             a4[i] = j
             break
 
 
 for i in a1:
     flag += chr(i)
 for i in a2:
     flag += chr(i)
 for i in a3:
     flag += chr(i)
 for i in a4:
     flag += chr(i)
 for i in a5:
     flag += chr(i)
 
 print flag

这里将字符串分成了5组,其中第一和第五组与0x76异或,其它的通过爆破的方式得到正确的字符串。

Flag: hctf{>>D55_CH0CK3R_B0o0M!-87544421}

原文发布于微信公众号 - 安恒网络空间安全讲武堂(gh_fa1e45032807)

原文发表时间:2018-01-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员互动联盟

【专业技术】C++ RTTI及“反射”技术

RTTI   RTTI(Run-Time Type Information)运行时类型检查的英文缩写,它提供了运行时确定对象类型的方法。面向对象的编程语言,象C...

33650
来自专栏Kirito的技术分享

警惕不规范的变量命名

就在最近,项目组开始强调开发规范了,今天分享一个变量名命名不规范的小案例,强调一下规范的重要性。 Boolean变量名命名规范 16年底,阿里公开了《Java...

35390
来自专栏编程

享学课堂谈-Python程序员的常见错误

这些是Python初学者活生生犯的错,千百次的错。事实上,这些错误实在是太普遍了以至于我敢保证你刚开始学的时候是一定会犯的。 “那么是什么呢?”你会问,“你也会...

20690
来自专栏大内老A

关于CLR内存管理一些深层次的讨论[上篇]

半年之前,PM让我在部门内部进行一次关于“内存泄露”的专题分享,我为此准备了一份PPT。今天无意中将其翻出来,觉得里面提到的关于CLR下关于内存管理部分的内存还...

20580
来自专栏闻道于事

Java之初识

今天开始学习Java 1.什么是Java?   Java是1995年由sun公司推出的一门极富创造力的面向对象编程语言,是由Java之父詹姆斯格斯林博士设计的。...

35280
来自专栏hbbliyong

C++为啥要使用new

1.为什么要有new? 为什么要有new?为什么要动态创建对象?为什么有时候不用new,有时候又用new,比如: // Cocos2d-x3.x的Value类,...

448120
来自专栏思考的代码世界

Python基础学习08天

15840
来自专栏令仔很忙

javascript变量:全局?还是局部?这个得注意

        如果有Var,在函数内部声明变量是局部变量,如下例,读取不到name的数据。

11330
来自专栏大闲人柴毛毛

Java异常体系中的秘密

相信大家每天都在使用Java异常机制,也相信大家对try-catch-finally执行流程烂熟于胸。本文将介绍Java异常机制的一些细节问题,这些问题虽然很...

382100
来自专栏西枫里博客

PHP7新特性之两个小小语法糖。

想起写下这篇原本是因为群里龙大佬说PHP7下count有问题,顺道就讽了他一句。其实我自己也没有详细了解下PHP7到底在哪些方面做了修改。所以空了就翻了翻手册,...

17610

扫码关注云+社区

领取腾讯云代金券