《C++反汇编与逆向分析技术揭秘》学习之第18章 反汇编代码的重建与编译

📅 2026/7/10 22:37:14 👁️ 阅读次数 📝 编程学习
《C++反汇编与逆向分析技术揭秘》学习之第18章 反汇编代码的重建与编译

在逆向分析的基础上,如何将目标分析程序中的反汇编代码提取出来重建并编译呢?本章将带领读者解决这个问题,找到目标程序中的关键代码并提取出来,将其编译成一个新的程序。

18.1 重建反汇编代码

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

图18-1 示例程序

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

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

代码清单18-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

笔记--------------------------------------------start

笔记--------------------------------------------end

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

代码清单18-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

笔记--------------------------------------------start

笔记--------------------------------------------end

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

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

.486 ; 根据代码清单18-1与代码清单18-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

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

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

18.2 编译重建后的反汇编代码

18.1节介绍了简单的反汇编代码的重建过程,如何对提取出的反汇编代码进行编译并与VS2019中的程序进行链接呢?不管是汇编编译器还是C语言编辑器,在编译的过程中都会生成通用的obj格式文件,有了这个共同点,就可以将汇编代码与C\C++代码进行联合编译。首先利用RadASM对代码清单18-3进行编译,生成obj文件,如图18-2所示。

图18-2 生成obj文件的汇编文件

使汇编文件生成对应的obj文件后,将图18-2中的MyToUpper.obj文件复制到VS2019的工程目录下。在VS2019的文件视图中,将复制到目录的obj文件添加到当前工程中,如图18-3所示。

图18-3 载入obj文件

这样一来,函数的实现就被加载到了当前工程中,如何调用obj文件中的ToUpper函数呢?需要对该函数进行声明,如图18-4所示。

图18-4 函数接口声明在声明的过程中需要注意函数的调用约定以及加入“extern "C"”的说明,防止函数被名词粉碎。现在万事俱备,可以调用ToUpper函数了,如图18-5所示。

图18-5 接口调用

运行程序,输入字符串,得到的结果如图18-6所示。

查看图18-6的显示结果,是不是成功地将字符串helloworld由小写字符转换为大写字符了呢?这表示从图18-1所示的程序中提取出来的代码被成功执行了。

图18-6 结果显示

笔记--------------------------------------------start

用VS2019编译生成的obj文件MyToUpper.obj,添加到项目中

编译运行

笔记--------------------------------------------end

18.3 本章小结

本章主要讲解了汇编环境和C语言环境的联合编译。读者需要具备一定的逆向分析基础,否则无法定位到关键的代码处,联合编译也就成了一句空话。如果觉得多个obj文件的静态编译方式不方便,也可以参考对应的编译链接选项,做成dll的共享编译方式。

很多时候,有些反汇编代码无法直接编译,比如异常处理、函数的嵌套等,这就需要读者先分析目标程序的功能结构,然后酌情对代码做出修改,使其满足当前环境。如果需要重建的代码中存在C++的异常处理部分,那么将异常分配过程全部照搬过来明显不合适。这种情况的处理办法有多种,可以先分析异常处理中是否有关键的、不可或缺的代码,如果有,则修改流程使得关键代码一定被执行,否则可以考虑将异常的注册函数替换为自己的异常函数,或者直接去掉异常的注册和处理代码,然后在上级调用函数中补充实现更优的异常处理过程。