这篇文章上次修改于 185 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

为了更深入地了解计算机程序在运行过程中的各个特点以及为后期解包Galgame提供更多的技术参照,我开始学习程序逆向(反编译)技术。

关于调试器和反汇编工具:

在程序逆向(反编译)过程中,最常用的调试技术是动态分析技术和静态分析技术。

在动态分析技术中,最常用的调试器例如OllyDbg和x64Dbg。但因为OllyDbg调试器不支持64位的调试以及近几年缺乏维护,所以我选择使用x64dbg;在静态分析技术中,最常用的反汇编工具是IDA(pro)。

刚开始学习逆向(反编译)技术,我选择了从动态分析技术学起。

用x64dbg调试程序:

下载安装好x64dbg后,右键想要调试的exe文件使用x64dbg调试,然后就进入了调试界面:

程序逆向(反编译)技术学习日记一1.jpg

但我们注意到调试器左上角的标题,此时的调试器指向的模块并不是我们想要调试的主程序,而是ntdll.dll:

程序逆向(反编译)技术学习日记一2.jpg

关于ntdll.dll,不得不和Windows中特殊的子系统WOW64相联系:

WOW64(Windows-on-Windows 64-bit )是64位Windows操作系统的子系统,可以使大多数32位应用程序在不进行修改的情况下运行在64位操作系统上。

64位的Windows,除了带有64位操作系统应有的系统文件,还带有32位操作系统应有的系统
文件。Windows的64位系统文件都放在一个叫作“System32”的文件夹中,\Windows\System32文件夹中包含原生的64位映像文件。为了兼容32位操作系统,还增加了\Windows\SysWOW64文件夹,其中存储了32位的系统文件。

64位应用程序会加载System32目录下64位的kernel32.dl、user32.dl和ntdll.dl。当32位应用程序加载时,WOW64建立32位ntdll.dll所要求的启动环境,将CPU模式切换至32位,并开始执行32位加载器,就如同该进程运行在原生的32位系统上一样。WOW64会对32位ntdll.dll的调用重定向ntdll.dll(64位),而不是发出原生的32位系统调用指令。WOW64转换到原生的64位模式,捕获与系统调用有关的参数,发出对应的原生64位系统调用。当原生的系统调用返回时,WOW64在返回32位模式之前将所有输出参数从64位转换成32位。

这也就很好的解释了为什么x64dbg在调试程序的时候,第一个系统断点是在ntdll.dll模块里的了。

以上关于WOW64的介绍从《加密与解密(第4版)》第一章中摘录。

我们需要再一步运行(F9),直到左上角标题显示此时的模块是我们要调试的主程序,且入口为00460711h:

程序逆向(反编译)技术学习日记一3.jpg

接下来,我们就可以使用动态跟踪的步进和步过来不断调试程序了:

程序逆向(反编译)技术学习日记一4.jpg

步进(F7),单步步进,遇到汇编指令call的时候进入;

步过(F8),单步步过,遇到汇编指令call的时候跳过。

快捷键 “-“ 可以回看步进或步过的前一步,快捷键 “Shift”+”+” 可以再次回到原来的位置。

例如,我们步进或步过到以下汇编指令call的位置00460737h,该call指令调用了win32的API函数里的GetVersion:

程序逆向(反编译)技术学习日记一5.jpg

此时进行步过则跳过该call指令,步进则进入call指令里:

程序逆向(反编译)技术学习日记一6.jpg

我们发现,该call指令直接跳进了系统模块kernel32.dll中,且因为75022CB0h处的汇编指令为jmp,跳转指令,所以接下来无论是步过还是步进,都会再次跳到指定位置:

程序逆向(反编译)技术学习日记一7.jpg

此时又来到了系统模块kernelbase.dll中。根据虚拟地址76EDD1F0h后面的<kernelbase.GetVersion>以及后面的注释说明,可以判断这就是函数GetVersion的入口。

小试身手:

接下来我们用吾爱破解论坛里一位大佬写的练习程序来练习一下:

玩玩破解,给新人看(第三集) - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

该程序为一个含有固定账号密码的登录程序,我们要做的就是逆向出账号和密码,破解成功,并爆破该程序。

通过对该程序的UI组成分析后,我们可以从查找含有中文字符的字符串去下手。

首先用x64dbg调试该程序(CM1(无壳).exe),然后在反汇编面板中右键查找当前模块的字符串:

程序逆向(反编译)技术学习日记一8.jpg

然后,我们看到了很熟悉的登录提示类中文字符,以及像是账号和密码的字符串:

程序逆向(反编译)技术学习日记一9.jpg

于是,我们右键它们其中任意一行字符跳转到反汇编窗口:

程序逆向(反编译)技术学习日记一10.jpg

我们可以看到,“请输入账号和密码”后面跟着我们前面看到的类似账号和密码的“yyhd”和“521314”的字符串。所以这里我们就可以大体判断,这一段汇编指令应该是该程序检查账号和密码是否正确的核心部分。

然后,我们将得到的账号和密码输入程序中,登录成功!

程序逆向(反编译)技术学习日记一11.jpg

接下来,我们开始尝试爆破该程序。

首先,我们先找到提示破解成功的字符串,然后在前面找到其中一个特殊的跳转指令je,也就是条件跳转指令:

程序逆向(反编译)技术学习日记一12.jpg

根据调试器给出的箭头提示,这里00401255h的跳转指令直接跳过字符串“恭喜你,你破解成功了!”。

通常,当程序检测到输入的账号和密码不正确时,会使用跳转代码,不执行提示破解成功的窗口代码以及后面的相关操作。所以,我们只需要把00401255h的跳转指令无效化,使其直接执行破解成功后面的代码,就能成功爆破了该程序。

这里,我们右键该跳转指令,用二进制的NOP指令(不执行任何操作)覆盖填充:

程序逆向(反编译)技术学习日记一13.jpg

程序逆向(反编译)技术学习日记一14.jpg

填充完成后,我们可以在补丁选项里选中我们刚刚填充的NOP补丁,然后点击修补文件生成新的exe文件。生成后如果想恢复NOP的填充操作,我们可以右键填充区域,点击恢复选区撤销操作:

程序逆向(反编译)技术学习日记一15.jpg

最后,运行刚刚生成的补丁文件,随便输入账号密码,然后提示破解成功,证明爆破成功:

程序逆向(反编译)技术学习日记一16.jpg

自此,我们已经把该程序完全破解,玩弄于手中了。

不过,这个登录程序只是新人学习逆向(反编译)技术的开胃菜,在后面的学习中还会有很多未知的难题等着我去研究和解决。