相信大家都在程序调试或者分析中碰到过自修改代码的情况吧。所谓自修改代码,就是程序自我保护的一种机制。它使我们的反汇编调试器看起来相当地无 助。因为我们看到的所谓的反汇编代码并非执行过程中的代码,它表面上看起来不合逻辑甚至一塌糊涂,但是运行起来却井井有条。因此,这项技术被广泛用在那些 反破解的商业软件中,在试图bypass杀毒软件的黑客 软件中也颇有涉及。在另一方面,cracker初学者们对这类程序大伤脑筋,他们一边诅咒反汇编调试器一边对着一大堆非法指令符莫名其妙。他们必须不停地设置断点,慢慢地让程序在运行过程中将真正的代码暴露出来。
没 错,这就是很多壳的基本运行原理。然而我并不打算在这里教大家怎么脱壳,大部分程序员都已经为我们做好了现成的脱壳程序,剩下的都是一些繁杂的充斥着各种 SEH等保护机制的超级壳,也许强大到作者也没有办法写出脱壳脚本。我写这篇文章的目的就是对代码自修改技术做一点点分析而已。
为了更好地诠释代码自修改,我引用下面段代码。
FE 0D xxxxxx DEC byte ptr DS:[The_fake_jmp] ;将装有The_fake_jmp入口地址的DS进行修改,即将The_fake_jmp的第一个字节内容减一。
...... ;其他的代码
B8 01 000000 MOV eax,1
The_fake_jmp:
75 xx JNZ another_place ;eax非零则跳转
......
如果没有看到前面的代码,也许很多程序员会想当然地认为一定会跳转到another_place,其实程序运行时已经将the_fake_jmp改成了74 xx,即改成了jz。这样程序运行的结果将与程序员所预料的完全相反。
通过这个例子大家也许会发现,存在自修改代码的程序调试起来确实要小心.难道我们必须要一步一步单步调试才能发现隐藏的陷阱吗?当然不是.前面已经提到了,不吝惜自己的断点可以很方便地识破这些小小的破绽。
下面我将通过调试著名木马Bifrost 1.2.1的脱壳服务端(该木马可以在chasenet.org下载到)来观察它的一些自修改代码片断。
传说中Bifrost的原始服务端是加壳的。有人已经做出了所谓的脱壳版本。实际上,我发现两者的区别不是很大。oep都没有经过修改。这里选用的调试器是网络上比较流行的应用层调试器Ollydbg。
载入服务端后我们可以看到OD的提示。说明该服务端存在大量自修改程序。我们随便就能找到一些调试器无法识别的指令。这里我选用00406111处.这里的代码如下:(这些代码也许在其他版本的服务端上是不同的)
00406111 F4 hlt
00406112 A4 movs byte ptr es:[edi], byte ptr [esi>
00406113 64:9A 325427F8 >call far D779:F8275432
0040611B 9B wait
0040611C F1 int1
0040611D 65:338D 355EF3B>xor ecx, dword ptr gs:[ebp+BFF35E35]
00406124 DEBF D977A7B3 fidivr word ptr [edi+B3A777D9]
0040612A A8 62 test al, 62
0040612C 9B wait
0040612D 002A add byte ptr [edx], ch
不 仅仅是这些代码,它的一大段上下文都让人莫名其妙。到这里我们假定它是自修改代码的一部分。我们在00406111点右键-断点-内存写入,然后F9运 行,我们会到达尝试对这段代码进行修改的程序段。仔细看看,呵呵,不错,我们似乎到达了对代码进行修改的函数里。其附近代码如下,我在这里对它们加了一点 简短的注释。
004073C0 55 push ebp
004073C1 8BEC mov ebp, esp
004073C3 56 push esi
004073C4 33F6 xor esi, esi ;清空esi
004073C6 3975 0C cmp dword ptr [ebp+C], esi ;esi在这里做计数器
004073C9 7E 1B jle short 004073E6 ;若解密完毕则跳转
004073CB 8B45 08 mov eax, dword ptr [ebp+8]
004073CE 33D2 xor edx, edx
004073D0 8D0C06 lea ecx, dword ptr [esi+eax] ;解密段指针
004073D3 8BC6 mov eax, esi
004073D5 F775 14 div dword ptr [ebp+14]
004073D8 8B45 10 mov eax, dword ptr [ebp+10]
004073DB 8A0402 mov al, byte ptr [edx+eax] ;我们的密钥
004073DE 3001 xor byte ptr [ecx],