这个算是之前关于外挂框架的一个继续,但主要是说程序的内存补丁的,适用于无法获取到 源代码而又想拦截或者调用目标程序中的相关指令或函数的方法。
一般情况下,我们使用长跳转指令0xe9(JMP XXXXXX)的方式跳转到我们自己的dll函数中去 执行代码,不同于很早很早之前的外挂教程使用的是消息通知的方法,这里其实也算是使用 跳转,但区别在于我们不用计算跳转的偏移。
先说下一般的跳转方式,这里是原始的一段代码的汇编语句:
0048DAEC |. E8 BBEFFFFF CALL test.0048CAAC 0048DAF1 |. 84C0 TEST AL,AL 0048DAF3 |. 74 61 JE SHORT test.0048DB56 0048DAF5 |. 8D4D FC LEA ECX,DWORD PTR SS:[EBP-4]
我们发现,在调用0x0048DAEC的时候,我们想要的数据的地址保存在eax的寄存器中,因此, 我们需要将这个数据拦截下来并保存到其他地方。
因此,我们需要代码做如下的执行方式:
0048DAEC |. E9 XXXXXX jmp hook.test 0048DAF1 |. 84C0 TEST AL,AL 0048DAF3 |. 74 61 JE SHORT test.0048DB56 0048DAF5 |. 8D4D FC LEA ECX,DWORD PTR SS:[EBP-4]
XXXXXX 的值需要计算, 可以使用下面的代码来进行:
m_realProc = (DWORD)(test) - 0x0048DAED -5;
然后,将m_realProc的值写入0x0048DAED(即0x0048DAEC+1)的位置即可。 下面是test指令部分的操作(也是汇编代码)
.code32 .section .text .global _test _test: .byte 0x60 push %eax push $_keep_buf call _strcpy add $0x8, %esp .byte 0x61 call $0x0048CAAC test %al, %al jmp $0x0048DAF3 ret
上面的代码中,0x60表示pushad,0x61表示popad,用来保存和恢复寄存器的,上面的代码 并不能正确的执行,因为call和jmp都是相对地址的,在目标程序中,这个地址是有一定的 偏差的,然后,你需要对这两个位置重新做计算,上面的代码也并不能编译,语法错误, call和jmp并不允许这样子来写,只是一个示意。
操作实在麻烦,虽然调试正确之后就不用再弄了。但调试也是一种痛苦啊。问题的关键在于 无法直接进行绝对地址的操作,既然不能直接,那就间接的来了,我想到了call操作的方式, push eip,jmp;而ret其实就是pop eip,因此,对于jmp指令,我们可以使用push+ret来间接 实现,关键在于,使用push可以操作立即数。因此,原来的代码打过内存补丁之后,就变成 了下面的样子:
0048DAEC |. 68 XXXXXXXX push hook.test 0048DAF1 |. c3 ret 0048DAF2 |. 90 nop 0048DAF3 |. 74 61 JE SHORT test.0048DB56 0048DAF5 |. 8D4D FC LEA ECX,DWORD PTR SS:[EBP-4]
写内存的差不多是7个字节(push 5字节+ret 1字节+nop 1字节),而关键的是,push后面 跟的是绝对地址。写入0x0048DAEC的内容可以这样子来生成:
char buf[7]; buf[0] = 0x68; *((DWORD*)(buf+1)) = (DWORD)test; buf[5] = 0xc3; buf[6] = 0x90;
简单而且通用,可以封装成函数,直接调用函数即可。对于我们的拦截的代码,是否也可以 这样子来进行,当然没问题:
.code32 .section .text .global _test _test: .byte 0x60 push %eax push $_keep_buf call _strcpy add $0x8, %esp .byte 0x61 push $jmp_addr push $0x0048CAAC ret jmp_addr: test %al, %al push $0x0048DAF3 ret
原来的call 0x0048CAAC变成了push操作,并且在之前多了一次push,即push $jmp_addr, 这样的操作是因为call 0x0048CAAC之后还有指令,为了让0x0048CAAC函数返回时直接返回 正确的地址,多了一次push操作,这样函数返回之后,就自动跳转到了jmp_addr了。
这种写法的好处就是写patch可以不用去处心积虑的计算了,可以更安全,更快。