IDA Pro - 如何得到比较清楚的逆向伪代码

Published on with 0 views and 0 comments

简介

这篇文章介绍了如何在不使用插件的IDA Hex-Rays如何得到比较清晰的伪代码。IDA Hex-Rays功能很强大,只要你提供了足够多的信息,它就能产生十分简单明了的代码。

下面我们以下面这个二进制文件为例:

为了方便我直接把exe文件后缀改成jpg,下载下来把文件后缀改回exe就行了

二进制文件下载地址:

步骤

打开IDA Pro加载这个exe文件,先按shitf + F5,添加vc32_14, vc32rtf, vc32ucrt这三个符号签名文件。

这个exe文件的main函数不太好找,我们先定位到exe文件的入口,按F5得到以下结果

signed int __usercall start@<eax>(int a1@<ebp>, int a2@<esi>)
{
  char v2; // bl
  int v4; // ST14_4
  _DWORD *v5; // eax
  _DWORD *v6; // esi
  _DWORD *v7; // eax
  _DWORD *v8; // esi
  const char **v9; // edi
  int *v10; // esi
  const char **v11; // eax

  sub_4018B4();
  if ( !(unsigned __int8)__scrt_initialize_crt(1)
    || (v2 = 0, *(_BYTE *)(a1 - 25) = 0, *(_DWORD *)(a1 - 4) = 0, *(_BYTE *)(a1 - 36) = sub_401631(), dword_41CC40 == 1) )
  {
    __scrt_fastfail(7);
    goto LABEL_20;
  }
  if ( dword_41CC40 )
  {
    v2 = 1;
    *(_BYTE *)(a1 - 25) = 1;
  }
  else
  {
    dword_41CC40 = 1;
    if ( _initterm_e(&unk_415140, &unk_415158) )
    {
      *(_DWORD *)(a1 - 4) = -2;
      return 255;
    }
    _initterm(&unk_415134, &unk_41513C);
    dword_41CC40 = 2;
  }
  __scrt_release_startup_lock(*(_DWORD *)(a1 - 36));
  v5 = (_DWORD *)sub_40196C(v4);
  v6 = v5;
  if ( *v5 )
  {
    if ( (unsigned __int8)__scrt_is_nonwritable_in_current_image(v5) )
      ((void (__thiscall *)(_DWORD, _DWORD, signed int, _DWORD))*v6)(*v6, 0, 2, 0);
  }
  v7 = (_DWORD *)sub_401972();
  v8 = v7;
  if ( *v7 )
  {
    if ( (unsigned __int8)__scrt_is_nonwritable_in_current_image(v7) )
      _register_thread_local_exe_atexit_callback(*v8);
  }
  v9 = *(const char ***)sub_406ACE();
  v10 = (int *)sub_406AC8();
  v11 = (const char **)unknown_libname_31();
  a2 = main(*v10, v9, v11);
  if ( !(unsigned __int8)sub_401A94() )
LABEL_20:
    exit(a2);
  if ( !v2 )
    _cexit();
  __scrt_uninitialize_crt(1, 0);
  *(_DWORD *)(a1 - 4) = -2;
  return a2;
}

注意下面的代码

  a2 = main(*v10, v9, v11);
  if ( !(unsigned __int8)sub_401A94() )
LABEL_20:
    exit(a2);

exit函数的参数应该就是主函数的返回值。

定位到到main函数

.text:00401390                 mov     esi, eax
.text:00401392                 call    sub_406312
.text:00401392
.text:00401397                 push    eax             ; envp
.text:00401398                 push    edi             ; argv
.text:00401399                 push    dword ptr [esi] ; argc
.text:0040139B                 call    main

按F5,我们会得到以下代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  HMODULE v3; // esi
  CHAR v4; // al
  int v5; // ecx
  unsigned int v6; // esi
  char v7; // bl
  __int128 v9; // [esp+4h] [ebp-22Ch]
  int v10; // [esp+14h] [ebp-21Ch]
  int v11; // [esp+18h] [ebp-218h]
  __int16 v12; // [esp+1Ch] [ebp-214h]
  char v13; // [esp+1Eh] [ebp-212h]
  int v14; // [esp+20h] [ebp-210h]
  char v15; // [esp+24h] [ebp-20Ch]
  CHAR Buffer[256]; // [esp+124h] [ebp-10Ch]
  int v17; // [esp+224h] [ebp-Ch]
  char v18; // [esp+228h] [ebp-8h]

  v3 = GetModuleHandleA(0);
  memset(Buffer, 0, 0xFFu);
  memset(&v15, 0, 0xFFu);
  if ( !LoadStringA(v3, 0x539u, Buffer, 255) )
    return -1;
  v4 = Buffer[0];
  if ( Buffer[0] )
  {
    v5 = 0;
    do
    {
      *((_BYTE *)&v14 + ++v5 + 3) = v4 ^ 0x30;
      v4 = Buffer[v5];
    }
    while ( v4 );
  }
  memset(Buffer, 0, 0xFFu);
  if ( !LoadStringA(v3, 0x29Au, Buffer, 255) )
    return -1;
  v17 = 0;
  v18 = 0;
  v14 = 5;
  if ( RegGetValueA(-2147483647, &v15, Buffer, 0xFFFF, 0, &v17, &v14) )
    return -1;
  v6 = 0;
  v9 = xmmword_4194E0;
  v10 = 55858812;
  v7 = 114;
  v11 = 1157851502;
  v12 = 20051;
  v13 = 0;
  do
  {
    sub_401010((const char *)&unk_4194D0, v7 ^ *((_BYTE *)&v17 + v6 % (v14 - 1)));
    v7 = *((_BYTE *)&v9 + v6++ + 1);
  }
  while ( v7 );
  return 0;
}

看起来确实很乱,但我们可以帮助反汇编器给出一个稍好一点的代码

首先我们有两个memsets

  memset(Buffer, 0, 0xFFu);
  memset(&v15, 0, 0xFFu);

Buffer和v15的原型应该是 char name[0xFF]。点击 Buffer然后按y键,把Buffer的类型改成 char name[0xFF],对v15做同样的事即可。

此外我们可以给这些变量一个有意义一点的名字,例如buffer_1和buffer_2。点击变量,按n键即可修改变量的名字。

通过查看 unk_4194D0我们会发现它是 %c,那么 sub_401010这个函数应该就是printf函数了,重命名 sub_401010printf,把 printf的函数原型改成 int printf(const char *format, ...)

LoadStringARegGetValueA里的数字通过按H键转换成十六进制,这样子看起来方便些,然后把其他的一些变量名也改成有意义的字符。

这样子我们就得到下面的代码了:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  HMODULE cur_proc; // esi
  char byte; // al
  int i; // ecx
  unsigned int cou; // esi
  char xor_byte; // bl
  __int128 xored_flag; // [esp+4h] [ebp-22Ch]
  int part_of_xored_flag; // [esp+14h] [ebp-21Ch]
  int part_of_xored_flag_1; // [esp+18h] [ebp-218h]
  __int16 part_of_xored_flag_2; // [esp+1Ch] [ebp-214h]
  char part_of_xored_flag_3; // [esp+1Eh] [ebp-212h]
  int some_buff; // [esp+20h] [ebp-210h]
  char buff_2[255]; // [esp+24h] [ebp-20Ch]
  char buff_1[255]; // [esp+124h] [ebp-10Ch]
  int xor_key_buff; // [esp+224h] [ebp-Ch]
  char v18; // [esp+228h] [ebp-8h]

  cur_proc = GetModuleHandleA(0);
  memset(buff_1, 0, 0xFFu);
  memset(buff_2, 0, 0xFFu);
  if ( !LoadStringA(cur_proc, 1337u, buff_1, 255) )
    return -1;
  byte = buff_1[0];
  if ( buff_1[0] )
  {
    i = 0;
    do
    {
      *((_BYTE *)&some_buff + ++i + 3) = byte ^ 0x30;
      byte = buff_1[i];
    }
    while ( byte );
  }
  memset(buff_1, 0, 0xFFu);
  if ( !LoadStringA(cur_proc, 666u, buff_1, 255) )
    return -1;
  xor_key_buff = 0;
  v18 = 0;
  some_buff = 5;
  if ( RegGetValueA(0x80000001, buff_2, buff_1, 0xFFFF, 0, &xor_key_buff, &some_buff) )
    return -1;
  cou = 0;
  xored_flag = xmmword_4194E0;
  part_of_xored_flag = 0x354567C;
  xor_byte = 0x72;
  part_of_xored_flag_1 = 0x4503696E;
  part_of_xored_flag_2 = 0x4E53;
  part_of_xored_flag_3 = 0;
  do
  {
    printf("%c", xor_byte ^ *((char *)&xor_key_buff + cou % (some_buff - 1)));
    xor_byte = *((_BYTE *)&xored_flag + cou++ + 1);
  }
  while ( xor_byte );
  return 0;
}

看起来清爽了一点,但还是有点乱。

下面我们看看能对```some_buff 做些什么,双击some_buff`我们会跳转到栈视图。

-0000022C xored_flag      db ?
-0000022B                 db ? ; undefined
-0000022A                 db ? ; undefined
-00000229                 db ? ; undefined
-00000228                 db ? ; undefined
-00000227                 db ? ; undefined
-00000226                 db ? ; undefined
-00000225                 db ? ; undefined
-00000224                 db ? ; undefined
-00000223                 db ? ; undefined
-00000222                 db ? ; undefined
-00000221                 db ? ; undefined
-00000220                 db ? ; undefined
-0000021F                 db ? ; undefined
-0000021E                 db ? ; undefined
-0000021D                 db ? ; undefined
-0000021C                 db ? ; undefined
-0000021B                 db ? ; undefined
-0000021A                 db ? ; undefined
-00000219                 db ? ; undefined
-00000218                 db ? ; undefined
-00000217                 db ? ; undefined
-00000216                 db ? ; undefined
-00000215                 db ? ; undefined
-00000214                 db ? ; undefined
-00000213                 db ? ; undefined
-00000212                 db ? ; undefined
-00000211                 db ? ; undefined
-00000210 some_buff       dd ?            ; <<<<< our variable
-0000020C buff_2          db 255 dup(?)
-0000010D                 db ? ; undefined
-0000010C buff_1          db 255 dup(?)
-0000000D                 db ? ; undefined
-0000000C xor_key_buff    db ?
-0000000B                 db ? ; undefined
-0000000A                 db ? ; undefined
-00000009                 db ? ; undefined

some_buff是dword或者四个字节,那他应该是一个字符buff,把它的类型改成 char a[4]

然后把 xor_key_buff的类型设置为 char a[4]xored_flag设置为 char a[28]buff_1 and buff_2设置为 char a[256]

我们就有下面的栈视图了

-0000022C xored_flag      db 28 dup(?)
-00000210 some_buff       db 4 dup(?)
-0000020C buff_2          db 256 dup(?)
-0000010C buff_1          db 256 dup(?)
-0000000C xor_key_buff    db 4 dup(?)

再点下F5刷新伪代码视图,我们会得到以下主函数伪代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  HMODULE cur_proc; // esi
  char byte; // al
  int i; // ecx
  unsigned int cou; // esi
  char xor_byte; // bl
  char xored_flag[28]; // [esp+4h] [ebp-22Ch]
  char some_buff[4]; // [esp+20h] [ebp-210h]
  char buff_2[256]; // [esp+24h] [ebp-20Ch]
  char buff_1[256]; // [esp+124h] [ebp-10Ch]
  char xor_key_buff[4]; // [esp+224h] [ebp-Ch]
  char v14; // [esp+228h] [ebp-8h]

  cur_proc = GetModuleHandleA(0);
  memset(buff_1, 0, 0xFFu);
  memset(buff_2, 0, 0xFFu);
  if ( !LoadStringA(cur_proc, 1337u, buff_1, 255) )
    return -1;
  byte = buff_1[0];
  if ( buff_1[0] )
  {
    i = 0;
    do
    {
      some_buff[++i + 3] = byte ^ 0x30;
      byte = buff_1[i];
    }
    while ( byte );
  }
  memset(buff_1, 0, 0xFFu);
  if ( !LoadStringA(cur_proc, 666u, buff_1, 255) )
    return -1;
  *(_DWORD *)xor_key_buff = 0;
  v14 = 0;
  *(_DWORD *)some_buff = 5;
  if ( RegGetValueA(0x80000001, buff_2, buff_1, 0xFFFF, 0, xor_key_buff, some_buff) )
    return -1;
  cou = 0;
  *(_OWORD *)xored_flag = xmmword_4194E0;
  *(_DWORD *)&xored_flag[16] = 0x354567C;
  xor_byte = 0x72;
  *(_DWORD *)&xored_flag[20] = 0x4503696E;
  *(_WORD *)&xored_flag[24] = 0x4E53;
  xored_flag[26] = 0;
  do
  {
    printf("%c", xor_byte ^ xor_key_buff[cou % (*(_DWORD *)some_buff - 1)]);
    xor_byte = xored_flag[cou++ + 1];
  }
  while ( xor_byte );
  return 0;
}

现在点击 RegGetValueA里的 0x80000001

if ( RegGetValueA(0x80000001, buff_2, buff_1, 0xFFFF, 0, xor_key_buff, some_buff) )

按M选择枚举值 HKEY_CURRENT_USER

最后的代码就是这个样子了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  HMODULE cur_proc; // esi
  char byte; // al
  int i; // ecx
  unsigned int cou; // esi
  char xor_byte; // bl
  char xored_flag[28]; // [esp+4h] [ebp-22Ch]
  char some_buff[4]; // [esp+20h] [ebp-210h]
  char buff_2[256]; // [esp+24h] [ebp-20Ch]
  char buff_1[256]; // [esp+124h] [ebp-10Ch]
  char xor_key_buff[4]; // [esp+224h] [ebp-Ch]
  char v14; // [esp+228h] [ebp-8h]

  cur_proc = GetModuleHandleA(0);
  memset(buff_1, 0, 0xFFu);
  memset(buff_2, 0, 0xFFu);
  if ( !LoadStringA(cur_proc, 1337u, buff_1, 255) )
    return -1;
  byte = buff_1[0];
  if ( buff_1[0] )
  {
    i = 0;
    do
    {
      some_buff[++i + 3] = byte ^ 0x30;
      byte = buff_1[i];
    }
    while ( byte );
  }
  memset(buff_1, 0, 0xFFu);
  if ( !LoadStringA(cur_proc, 666u, buff_1, 255) )
    return -1;
  *xor_key_buff = 0;
  v14 = 0;
  *some_buff = 5;
  if ( RegGetValueA(HKEY_CURRENT_USER, buff_2, buff_1, 0xFFFF, 0, xor_key_buff, some_buff) )
    return -1;
  cou = 0;
  *xored_flag = g_xored_flag;
  *&xored_flag[16] = 0x354567C;
  xor_byte = 0x72;
  *&xored_flag[20] = 0x4503696E;
  *&xored_flag[24] = 0x4E53;
  xored_flag[26] = 0;
  do
  {
    printf("%c", xor_byte ^ xor_key_buff[cou % (*some_buff - 1)]);
    xor_byte = xored_flag[cou++ + 1];
  }
  while ( xor_byte );
  return 0;
}
说你懂得生之微末,我便做了这壮大与你看,你说再热闹也终需离散,我便做了这一辈子与你看,你说冷暖自知,我便做了这冬花夏雪与你看,你说恋恋旧日好时光,我便做了这描金绣凤的浮世绘与你看。你说应愁高处不胜寒,我便拱手河山,讨你欢。