原文地址:Question about disassembler

简介

这篇文章介绍了如何在不使用插件的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_401010为printf,把printf的函数原型改成int printf(const char *format, ...)。

把LoadStringA和RegGetValueA里的数字通过按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;
}

IDA Pro - 如何得到比较清楚的逆向伪代码的更多相关文章

  1. 使用IDA Pro逆向C++程序

    使用IDA Pro逆向C++程序 附:中科院李_硕博 : IDA用来做二进制分析还是很强大的 .lib程序是不是很容易分析出源码? 这个得看编译选项是怎么设置的 如果没混淆 没太过优化 大体能恢复源码 ...

  2. IDA Pro - 使用IDA Pro逆向C++程序

    原文地址:Reversing C++ programs with IDA pro and Hex-rays 简介 在假期期间,我花了很多时间学习和逆向用C++写的程序.这是我第一次学习C++逆向,并且 ...

  3. 使用IDA pro逆向ARM M系核心的Bin固件

    使用IDA pro逆向ARM M系核心的Bin固件 ​ 物联网和智能设备这两年还是比较火的,我们的手中或多或少都有了几个智能设备,比如手环,智能手表,或者门锁什么之类的东西,但是同学们在做逆向的时候, ...

  4. 路由器逆向分析------在Linux上安装IDA Pro

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/69665905 01.在Linux系统上安装Linux版本的IDA Pro Linu ...

  5. android调试系列--使用ida pro调试原生程序

    1.工具介绍 IDA pro: 反汇编神器,可静态分析和动态调试. 模拟机或者真机:运行要调试的程序. 样本:自己编写NDK demo程序进行调试 2.前期准备 2.1  准备样本程序(假设已经配置好 ...

  6. IDA Pro使用技巧

    DA Pro基本简介 IDA加载完程序后,3个立即可见的窗口分别为IDA-View,Named,和消息输出窗口(output Window). IDA图形视图会有执行流,Yes箭头默认为绿色,No箭头 ...

  7. IDA Pro使用(静态分析+动态调试)

    链接:http://skysider.com/?p=458 IDA Pro使用(静态分析+动态调试) 1.静态分析 IDA FLIRT Signature Database —— 用于识别静态编译的可 ...

  8. IDA Pro基本简介

    IDA Pro基本简介 IDA加载完程序后,3个立即可见的窗口分别为IDA-View,Named,和消息输出窗口(output Window). IDA图形视图会有执行流,Yes箭头默认为绿色,No箭 ...

  9. 关于ida pro的插件keypatch

    关于ida pro的插件keypatch 来源 https://blog.csdn.net/fjh658/article/details/52268907 关于ida pro的牛逼插件keypatch ...

随机推荐

  1. postgresql主从同步配置

    前言 不久前,公司的一台物理机器硬件坏了,导致运行在其上的虚拟机都挂了.很不凑巧的是,我负责的那台虚拟机的系统盘坏了(ps:感觉老天在玩我),导致里面的数据永远的离我而去(ps:当时我的内心是崩溃的) ...

  2. HelloServlet类继承HttpServlet利用HttpServletResponse对象

    HelloServlet类继承HttpServlet利用HttpServletResponse对象 HelloServlet类的doGet()方法先得到username请求参数,对其进行中文字符编码转 ...

  3. selenium中get_cookies()和add_cookie()的用法

    在用selenium爬取网页的时候,有时候需要登陆,这时候用selenium获取cookie和携带cookie是很方便的,获取cookie可以通过内置的函数get_cookies(),它得到的是一组c ...

  4. 洛谷P1441 砝码称重(搜索,dfs+bitset优化)

    洛谷P1441 砝码称重 \(n\) 的范围为 \(n \le 20\) ,\(m\) 的范围为 \(m \le 4\) . 暴力遍历每一种砝码去除情况,共有 \(n^m\) 种情况. 对于剩余砝码求 ...

  5. TP5内部异常API数据输出的自定义方法编写

    需求:利用postman进行请求api接口过程中 关于一些数据输出异常的情况下 我们希望通过自己编写一些类和方法 实现便于后端人员进行根据提示进行调试处理! 以下测试的时候 请设置 app_debug ...

  6. VS code 同步设置与插件

    准备工作:拥有一个github账户,电脑上需安装VSCode.实现同步的功能主要依赖于VSCode插件 "Settings Sync"第一步:安装同步插件Settings Sync ...

  7. jQuery中的serializer序列化—炒鸡好用

    jQuery.serializer()序列化 serialize()函数用于序列化一组表单元素,将表单内容编码为用于提交的字符串. serialize()函数常用于将表单内容序列化,以便用于AJAX提 ...

  8. 《图解设计模式》读书笔记3-2 Prototype模式

    目录 Prototype(原型)模式的由来 类图 代码 角色 我的理解 Prototype(原型)模式的由来 创建一个实例,可以关键字new创建.但有时候,我们需要在不指定类名的前提下生成实例,比如: ...

  9. 16/7/11_PHP-图形图像操作

    GD库简介 GD指的是Graphic Device,PHP的GD库是用来处理图形的扩展库,通过GD库提供的一系列API,可以对图像进行处理或者直接生成新的图片. PHP除了能进行文本处理以外,通过GD ...

  10. C# DataTable删除行Delete与Remove的问题

    DataTable删除行使用Delete后,只是该行被标记为deleted,但是还存在,用Rows.Count来获取行数时,还是删除之前的行数,需要使用datatable.AcceptChanges( ...