C++ x64功能绕行,无内联装配

C++ x64功能绕行,无内联装配,c++,assembly,visual-studio-2013,64-bit,hook,C++,Assembly,Visual Studio 2013,64 Bit,Hook,晚上好,各位程序员和黑客们 我正在试验二进制补丁,更精确地说:绕过不通过vftable调用的函数 我正在做的详细工作 我将一个DLL注入到一个正在运行的进程中,并通过扫描它的签名来确定一个函数(原始函数)的起始地址 一旦找到它,我就用自己的shell代码重写前13个字节,将对该函数的每次调用重定向到我的DLL函数(hook函数): mov-rax, jmp-rax ret 在删除外壳代码后,hook函数再次调用原始函数的地址,以防止无休止的递归。 一旦原始函数返回,钩子函数将返回它已返回的值以

晚上好,各位程序员和黑客们

我正在试验二进制补丁,更精确地说:绕过不通过vftable调用的函数

我正在做的详细工作

我将一个DLL注入到一个正在运行的进程中,并通过扫描它的签名来确定一个函数(原始函数)的起始地址

一旦找到它,我就用自己的shell代码重写前13个字节,将对该函数的每次调用重定向到我的DLL函数(hook函数):

mov-rax,
jmp-rax
ret
在删除外壳代码后,hook函数再次调用原始函数的地址,以防止无休止的递归。 一旦原始函数返回,钩子函数将返回它已返回的值以保留常规代码流

问题

您可能已经注意到,我的外壳代码绝不保留任何寄存器(甚至不保留RAX),它可能不仅由钩子函数操作,而且已经由我的外壳代码操作

因此,原始函数在被钩子函数调用时失败。我想添加内联汇编,作为钩子函数中的第一个操作,将寄存器推送到堆栈中,并在将控制传递给原始函数之前将其弹出,但Visual Studio不支持x64内联汇编

我可以用操作码来补充外壳代码,将寄存器推送到堆栈中。但是我不能添加操作码来弹出它们,因为我在调用原始函数之前还原了原始代码

我试图规避的解决方案

事实上,我知道解决这个问题最干净的方法是重新定位整个函数。这样,在调用原始函数之前,我就不会被迫移除钩子。我可以添加操作码,将寄存器推送到jmp外壳代码中,并将操作码弹出到重新定位的函数前面。我正试图避免这种情况,因为我不知道如何动态确定原始函数的结尾,因此我无法计算要移动多少字节

问题

  • 是否存在某种调用约定或其他我还不知道的东西,允许我以一种强制编译器不使用寄存器而只使用堆栈的方式声明钩子函数
  • 有人知道我如何判断0xC3(ret)字节是否实际上是函数的结尾吗
  • 我是否可以将Visual Studio 2013配置为使用具有x64内联程序集支持的替代编译器

  • 一种解决方案是,不从钩子恢复原始函数,而是调用从钩子覆盖的相同指令,例如:

    原始功能:

    <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
      ...
    
      ... // 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
    
    
    推动rbp
    mov rsp,rbp
    第8分队,rsp
    添加rbx、rcx
    ....
    
    钩形函数:

    <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
      ...
    
      ... // 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
    
    
    莫夫·拉克斯,
    jmp-rax
    不
    添加rbx、rcx
    ...
    
    您的dll函数:

    <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
      ...
    
      ... // 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
    
    ..//做你喜欢的事
    按rbp//重复钩住函数中替换的代码
    mov rsp,rbp
    第8分队,rsp
    jmp//调用它并添加钩子外壳代码的偏移量
    
    如果shell代码与指令不一致,则可以nop sled,如示例所示

    至于rax问题,我认为你可以这样做:

    <do_stuff>
      push <dll_function_address>
      ret
      nop
      add rbx, rcx
      ...
    
    
    推
    ret
    不
    添加rbx、rcx
    ...
    
    它将钩子地址推到堆栈上并调用ret,ret获取堆栈上的第一个地址并跳到那里

    或者您可以使用call指令:

    <do_stuff>
      call <dll_function_address>
      nop
      add rbx, rcx
      ...
    
    
    呼叫
    不
    添加rbx、rcx
    ...
    

    一旦调用了钩子,就要像以前一样替换钩子函数。然后剩下的问题是,dll函数中的ret将在nop指令上继续,因此需要删除5(调用指令的大小)从堆栈中推送的地址中,我发现:。

    我没有尝试,但我认为
    nm-S
    可以提供原始函数的大小。或者看,它可能有帮助,我不能调用“nm”我猜是在windows进程的运行时。我不知道我要绕道的函数后面的函数,所以我不能用这种方法确定长度。哦,我以为你只在运行时替换函数,但知道你在之前搜索什么函数。我知道,但我不知道后面的函数。6中只使用了一种调用约定4位代码,但寄存器RAX、R10和R11在函数调用时可用,而R10和R11在退出时可用。一个函数可能有多条ret指令,或者没有ret指令,或者不同的指令。并且任何异常处理代码都是独立的(可能不相邻)并且可以利用绝对地址。没有内联汇编?我增加了另一种可能性