PUSH 参数地址
CALL 函数入口地址(这里为一个偏移地址)
执行这2条指令就能跳转到你要拦截的函数了,但是我们该修改成什么呢。首先,我们需要知道这2条指令的长度和具体的机器代码的值。其中PUSH对应0x68,而CALL指令对应的机器码为0xE8,而后面的则分别对应拦截函数的参数地址和函数的地址。注意第一个是一个直接的地址,而第二个则是一个相对地址。当然你也可以使用0xFF0x15这个CALL指令来进行直接地址的跳转。
下面就是计算这2个地址的值了,
对于参数和函数体的地址,要分情况而定,对于对本进程中api的拦截,则直接取地址就可以了。对于参数,可以先定义一个参数变量,然后取变量地址就ok了。
如果是想拦截其他进程中的api,则必须使用其他一些方法,最典型的方法是利用VirtualAllocEx函数来在其他进程中申请和提交内存空间。然后用WriteProcessMemory来分别把函数体和参数分别写入申请和分配的内存空间中去。然后再生成要修改的数据,最后用WriteProcessMemory来修改api入口,把入口的前10字节修改为刚刚生成的跳转数据。比如在远程进程中你写入的参数和函数体的内存地址分别为0x00010000和0x00011000,则生成的跳转数据为 68 00 00 01 00 E8 00 10 01 00(PUSH 00010000 CALL 00011000),这样程序运行createfile函数的时候将会先运行PUSH 00010000 CALL 00011000,这样就达到了跳转的目的。此刻我们应该时刻注意堆栈的状态,对于CreateFile有
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
可以看到其有7个参数,于是在调用之前,堆栈应该已经被压入了这7个参数,堆栈的样子:
|.| <---ESP
|createfile执行后的下一条指令地址|
|参数1|
|参数2|
|参数3|
|参数4|
|参数5|
|参数6|
|参数7|
|..|
这是执行到我们的跳转语句:PUSH 00010000,于是堆栈又变了:
|.| <---ESP
|00010000|
|createfile执行后的下一条指令地址|
|参数1|
|参数2|
|参数3|
|参数4|
|参数5|
|参数6|
|参数7|
|..|
接着执行CALL 00011000,堆栈变为:
|| <---ESP
|api入口之后的第11个字节的指令的地址|
|00010000|
|createfile执行后的下一条指令地址|
|参数1|
|参数2|
|参数3|
|参数4|
|参数5|
|参数6|
|参数7|
|..|
接下来就到了我们的拦截函数中拉,当然,函数肯定也会做一些类似动作,把EBP压栈,为局部变量分配空间等。这时候堆栈的样子又变了:
|EDI| <---ESP
|ESI|
|EBX|
|局部变量|
|EBP| <---EBP
|api入口之后的第11个字节的指令的地址|
|00010000|
|createfile执行后的下一条指令地址|
|参数1|
|参数2|
|参数3|
|参数4|
|参数5|
|参数6|
|参数7|
|..|
这时候,你想做什么就尽情地做吧,获取参数信息,延缓执行CreateFile函数等等。拿获取打开文件句柄的名字来说吧,文件名是第一个参数,前面说过我们可以用[EBP+8]来获取参数,但是对照上面的堆栈形