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

时间:2022-04-27
本文章向大家介绍​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 2017nn", v3);
  sub_4113F7("Mark.09 is hijacking Shinji Ikari now...nn", 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 FUNCTIONn    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 successn", 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}