第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的基础上修正了语法错误,这样一来,一段简单的反汇编代码的重建就完成了。
读者在重建反汇编代码时应注意所分析代码的调用约定,对函数的调用约定判断是重建反汇编代码的重点。一旦判断错误,使用了错误的调用约定,极有可能影响运行结果,以及破坏栈的平衡,从而使程序无法运行。