一个古老软件工具的逆向重生

一个古老软件工具的逆向重生

2018/7/9

1. 背景

XXX是一个用于收集和分析映像文件信息的小工具,其开发时间大约在2001年,现在不能在WIN7下运行。

但是,当前项目仍需要使用该工具。因此,试图通过逆向分析后使之重生。

XXX的运行环境是win32。

2. 逆向分析

用IDA生成XXX的C程序,基本情况如下:

• 原始编译器是GNU C++。

• 12348物理行C代码。

• 共有252个函数(其中2函数反编译失败),其中绝大部分是C库函数。

• 所有函数复原了原始的符号名称。

• 大部分全局变量复原了原始的符号名称。

• 使用了lex生成词法分析代码,但没有lex的源文件。

总之,该工具的代码不难理解。

3. 修改

3.1. 预处理

用自己开发的IDA_Assistant对IDA生成的C程序进行修剪处理。

3.2. 删除C库函数

开始只删除熟知的库函数,如printf等。后来发现,在main函数后面的都是库函数,就一起删除了。

库函数的全局变量与应用程序的全局变量混在一起,只能经过确认后予以删除。

3.3. 复原lex代码

YY_BUFFER_STATE是lex中的一个重要数据结构,但IDA的反编译结果中没有这个结构,对这个数据结构的操作变成了意义不明的数组操作或指针操作。以下是函数yy_scan_buffer的反编译结果:

_DWORD *__cdecl yy_scan_buffer(int a1, unsigned int a2)

{

_DWORD *v2; // ebx

if ( a2 <= 1 || *(_BYTE *)(a2 + a1 - 2) || *(_BYTE *)(a2 + a1 - 1) )

return 0;

v2 = yy_flex_alloc(0x28u);

if ( !v2 )

yy_fatal_error(8148);

v2[1] = a1;

v2[3] = a2 - 2;

v2[2] = a1;

v2[5] = 0;

*v2 = 0;

v2[4] = a2 - 2;

v2[6] = 0;

v2[7] = 1;

v2[8] = 0;

v2[9] = 0;

yy_switch_to_buffer((int)v2);

return v2;

}

变量v2的类型应是YY_BUFFER_STATE,

对v2的赋值应是对结构分量的赋值,

因此要修改为:

v2->yy_buf_pos = a1;

v2->yy_buf_size = a2 - 2;

v2->yy_ch_buf =a1;

v2->yy_is_our_buffer = 0;

v2->yy_input_file = 0;

v2->yy_n_chars = a2 - 2;

v2->yy_is_interactive = 0;

v2->yy_at_bol = 1;

v2->yy_fill_buffer = 0;

v2->yy_buffer_status = 0;

3.4. 复原lex数据

lex生成的代码中有大量数据,其类型是short或int数据,但IDA把它们都翻译为int变量或int数组。例如:

int yy_chk = 0; // weak

int yy_nxt[40] =

{

2293760,

786452,

//以下略

};

实际应该是:

short int yy_chk[80] = {

0, 0, 0x20, 0x2B, 0x20, 2, 2, 0xD, 0x13, 2, 2, 2, 0x15, 0x16, 0x17, 0x13,

0x18, 0x16, 0xD, 0x15, 0x17, 0x19, 0x1C, 0x18, 0x1A, 0x1B, 0x1D, 0x1E, 0x2A, 0x29, 0x28, 0x1F,

0x27, 0x1C, 0x26, 0x19, 0x1E, 0x1A, 0x1D, 0x1F, 0x1B, 0x24, 0x24, 0x24, 0x2C, 0x25, 0x2C, 0x21,

0x12, 0x11, 0x0F, 0x0E, 0x0C, 0x03, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,

0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0xB48D, 0x26, 0x0, 0x8D00, 0x27BC, 0, 0,

};

short int yy_nxt[80] =

{

0, 0x23, 0x14, 0xC, 0x21, 5, 5, 0xE, 0x14, 5, 5, 5, 0x14, 0x14, 0x14, 0x15,

0x14, 0x17, 0x0F, 0x16, 0x18, 0x14, 0x14, 0x19, 0x14, 0x14, 0x14, 0x14, 0xB, 0xA, 9, 0x14,

8, 0x1D, 7, 0x1A, 0x1F, 0x1B, 0x1E, 0x20, 0x1C, 4, 4, 4, 0x12, 6, 0x12, 0x22,

0x14, 0x13, 0x11, 0x10, 0x0D, 0x23, 3, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,

0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x9090, 0x9090, 0x9090, 0x9090, 0x9090, 0x9090, 0x9090,

};

3.5. 辨别字符串

IDA的反编译结果中有许多如下语句:

yy_fatal_error(8148);

yy_fatal_error(8332);

从函数yy_fatal_error的定义获悉其输入参数应是字符串指针。经分析发现8148、8332实际是内存地址,8148对应16进制1FD4,在list文件中找到:

.text:00001FD4 aOutOfDynamicMe db 'out of dynamic memory in yy_scan_bu'

.text:00001FD4 db 'ffer()',0

.text:00001FFE align 10h

因此要改为:

yy_fatal_error(“out of dynamic memory in yy_scan_buffer()”);

3.6. 推导数据结构

根据XXX中的函数addFunc、getFunc、checkFunc、newFunc等,推导出以下数据结构定义:

typedef struct _func_info

{

int func_addr;

int func_kind;

char *func_name;

struct _func_info *next_func;

} func_info_t;

上述定义中的分量名根据代码中的全局变量名和代码的操作拟定的。例如,程序中有全局变量:

int address

int kind;

这两个变量出现在以下函数调用语句中:

v12 = newFunc(address, kind, v11);

在函数newFunc中address和kind赋给func_info_t的分量,因此可拟定分量名称。

3.7. 修改代码

IDA的反编译结果中有些代码令人费解,有些实际是错的,因此要予以纠正。

例如,反编译中有以下代码:

char byte_1129F[]; // weak

char nm_dir[]; // idb

if ( byte_1129F[strlen(nm_dir)] == 92 )

数组byte_1129F和nm_dir的定义中没有给出尺寸。在list文件中有:

.bss:0001129F ; char byte_1129F[]

.bss:0001129F byte_1129F db ?

.bss:000112A0 public _nm_dir

.bss:000112A0 ; char nm_dir[]

.bss:000112A0 _nm_dir dd ?

根据相邻变量的地址,可推导出byte_1129F有一个字节、nm_dir有272个字节,由此导出那行if语句应是:

if ( nm_dir[strlen(nm_dir)-1] == '\\' )

并且byte_1129F是不存在的。

3.8. 小结

完成上述工作后,源程序物理行数为1300。

然后在VS2010中编译、链接、生成可执行文件。

用这个逆向重生的可执行文件处理样例输入,产生与原始工具相同的输出。

本项任务历时两天,约10小时。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值