第17章 反汇编代码的重建与编译

在逆向分析的基础上,如何将目标分析程序中的反汇编代码提取出来重建并进行编译呢?这将是本章要讲解的内容,先找到目标程序中的关键代码并提取出来,然后将其编译成一个新的程序。

17.1 重建反汇编代码

重建反汇编代码,是先将目标程序中的关键代码提取出来,然后将其组建成汇编代码。利用IDA这个强大的分析工具,要做到这一点非常容易。先准备好示例程序,如图17-1所示。

图 17-1 示例程序

这是一个由VC++6.0编写的控制台程序,为了便于学习,程序的功能非常简单,只是将输入的字符串中的小写字符转换成大写字符。本节的任务是将这段转换字符的代码提取出来,然后组建成可编译的汇编代码。

首先使用IDA打开分析文件ToUpper.exe(文件在随书文件中),查看功能代码所在位置,具体如代码清单17-1所示。

代码清单17-1 分析ToUpper程序—IDA分析


00401030_main proc near;main函数入口

00401030

00401030 Text=byte ptr-100h;局部变量以及参数标号定义

00401030 var_FF=byte ptr-0FFh

00401030 argc=dword ptr 4

00401030 argv=dword ptr 8

00401030 envp=dword ptr 0Ch

00401030 sub esp,100h;局部变量空间申请

00401036 push edi

00401037 mov ecx,3Fh

0040103C xor eax, eax

0040103E lea edi,[esp+104h+var_FF]

00401042 mov[esp+104h+Text],0;初始化数组为0

00401047 push offset aIFIOg;"请输入字符串:\n"

0040104C rep stosd

0040104E stosw

00401050 stosb

00401051 call_printf

00401056 lea eax,[esp+108h+Text]

0040105A push eax

0040105B push offset Format;"%255s"

00401060 call_scanf

00401065 lea ecx,[esp+110h+Text]

00401069 push ecx

0040106A call sub_401000;转换函数

0040106F add esp,10h

00401072 lea edx,[esp+104h+Text]

00401076 push 0;uType

00401078 push offset Caption;"转换结果"

0040107D push edx;lpText

0040107E push 0;hWnd

00401080 call ds:MessageBoxA

;部分代码分析略

0040108F_main endp


通过分析代码清单17-1,我们找到了要提取的代码的首地址,它在地址标号sub_401000处。进入此地址中进一步进行分析,如代码清单17-2所示。

代码清单17-2 sub_401000处的分析—IDA分析


sub_401000 proc near;函数入口

00401000 arg_0=dword ptr 4;参数标号定义,此函数只有一个参数

00401000

00401000 push esi

00401001 mov esi,[esp+4+arg_0]

00401005 push edi

00401006 mov edi, esi

00401008 or ecx,0FFFFFFFFh

0040100B xor eax, eax

0040100D repne scasb

0040100F not ecx

00401011 dec ecx;这里是一段内联函数strlen

00401012 xor edx, edx

00401014 test ecx, ecx

00401016 jle short loc_40102D

00401018

loc_401018:;地址标号

00401018 mov al,[edx+esi]

0040101B cmp al,61h

0040101D jl short loc_401028

0040101F cmp al,7Ah

00401021 jg short loc_401028

00401023 sub al,20h

00401025 mov[edx+esi],al

loc_401028:;地址标号

00401028 inc edx

00401029 cmp edx, ecx

0040102B jl short loc_401018

loc_40102D:;地址标号

0040102D pop edi

0040102E pop esi

0040102F retn

0040102F sub_401000 endp


代码清单17-2为小写字符转换大写字符的实现部分,由于本节要讲解的内容为反汇编代码的重建,因此不再对比代码清单进行深入分析。直接复制并粘贴代码清单17-2中的反汇编代码,将其修改为合法的汇编代码,如代码清单17-3所示。

代码清单17-3 重建后的汇编代码—汇编源码


.486

;根据对代码清单17-1和代码清单17-2的分析,函数的调用方式为C中的调用方式,对调用约定的

;判断非常重要,这会直接影响程序的运行结果,以及栈的平衡

.model flat, c

option casemap:none

.code;代码段定义

ToUpper proc arg_0:DWORD;函数入口

push esi

mov esi, arg_0;语法修正

push edi

mov edi, esi

or ecx,0FFFFFFFFh

xor eax, eax

repne scasb

not ecx

dec ecx

xor edx, edx

test ecx, ecx

jle short loc_40102D

loc_401018:;地址标号

mov al,[edx+esi]

cmp al,61h

jl short loc_401028

cmp al,7Ah

jg short loc_401028

sub al,20h

mov[edx+esi],al

loc_401028:;地址标号

inc edx

cmp edx, ecx

jl short loc_401018

loc_40102D:;地址标号

pop edi

pop esi

ret

ToUpper endp

end


代码清单17-3在代码清单17-2的基础上修正了语法错误,这样一来,一段简单的反汇编代码的重建就完成了。

读者在重建反汇编代码时应注意所分析代码的调用约定,对函数的调用约定判断是重建反汇编代码的重点。一旦判断错误,使用了错误的调用约定,极有可能影响运行结果,以及破坏栈的平衡,从而使程序无法运行。