晚上好,编程员和黑客。
我正在尝试二进制修补,更精确:绕过未通过vftable调用的函数。
我正在做的事情
我正在将一个DLL注入正在运行的进程,并通过扫描它的签名来确定函数的起始地址(原始函数)。
一旦找到它,我用自己的shell代码重写前13个字节,将每个对此函数的调用重定向到我的DLL函数( hook函数):
mov rax, <dll_function_address>
jmp rax
ret
钩子函数在删除我的shellcode之后再次调用原始函数的地址,以防止无休止的递归。 一旦原始函数返回,钩子函数就意味着返回它返回的值以保留常规代码流。
问题
您可能已经注意到我的shellcode并不保留任何寄存器(甚至不是RAX,它可能不仅可以被钩子函数操纵,而且已经被我的shellcode操纵了。
因此,当钩子函数调用时,原始函数会失败。我想添加内联汇编以将寄存器作为钩子函数中的第一个动作推送到堆栈,并在将控制传递给原始函数之前将其弹回,但是Visual Studio没有内联汇编的x64支持。
我可以使用操作码来补充我的shellcode,将寄存器推送到堆栈。但是我无法添加操作码来弹出它们,因为我在调用原始函数之前恢复原始代码
我试图规避的解决方案
我实际上知道这个问题最干净的方法是重新定位整个函数。这样,在调用原始函数之前,我不会被迫删除钩子。我可以添加操作码来将寄存器推送到我的jmp shellcode和操作码以在重定位函数的前面弹出它们。我试图绕过这个,因为我不知道如何动态地确定原始函数的结束,所以我无法弄清楚要移动多少字节。
问题
答案 0 :(得分:0)
一种解决方案是,不是从钩子中恢复原始函数,而是调用从钩子中覆盖的相同指令,例如:
原始功能:
<do_stuff>
push rbp
mov rsp, rbp
sub 8, rsp
add rbx, rcx
....
挂钩功能:
<do_stuff>
mov rax, <dll_function_address>
jmp rax
nop
add rbx, rcx
...
你的dll功能:
... // Do your hooked stuff
push rbp // Repeat the code your replaced from the hooked function
mov rsp, rbp
sub 8, rsp
jmp <do_stuff + sizeof(shellcode) + nopsled> // Call it and add the offset of the hook shellcode
如果shell代码与指令不对齐,则可以按照示例中的说明操作。
关于rax问题,我认为你可以这样做:
<do_stuff>
push <dll_function_address>
ret
nop
add rbx, rcx
...
它将你的钩子地址推到堆栈上并调用ret,ret取出堆栈上的第一个地址并跳转到那里。
或许你可以使用通话指令:
<do_stuff>
call <dll_function_address>
nop
add rbx, rcx
...
一旦调用了钩子,就像你已经做的那样替换钩子函数。然后剩下的问题是你的dll函数的ret将继续执行nop指令,所以你需要从堆栈中推送的地址中删除5(调用指令的大小),为此,我发现:{ {3}}