C 挂钩-热插接

C 挂钩-热插接,c,windows,assembly,hook,hotpatching,C,Windows,Assembly,Hook,Hotpatching,我正在尝试钩住Windows API函数FindWindowA()。我成功地用下面的代码完成了它,而没有“热补丁”:我已经覆盖了函数开头的字节。调用myHook(),调用FindWindowA()时会显示一个消息框 user32.dll已启用热修补,我希望在实际函数之前覆盖NOPs,而不是覆盖函数本身。但是,当我将hotpatching设置为TRUE时,下面的代码将不起作用。当FindWindowA()被执行时,它什么也不做 #include <stdio.h> #include &

我正在尝试钩住Windows API函数FindWindowA()。我成功地用下面的代码完成了它,而没有“热补丁”:我已经覆盖了函数开头的字节。调用myHook(),调用FindWindowA()时会显示一个消息框

user32.dll已启用热修补,我希望在实际函数之前覆盖NOPs,而不是覆盖函数本身。但是,当我将hotpatching设置为TRUE时,下面的代码将不起作用。当FindWindowA()被执行时,它什么也不做

#include <stdio.h>
#include <windows.h>

void myHook()
{
    MessageBoxA(NULL, "Hooked", "Hook", MB_ICONINFORMATION);
}

int main(int argc, char *argv[])
{
    BOOLEAN hotpatching = FALSE;

    LPVOID fwAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "FindWindowA");
    LPVOID fwHotpatchingAddress = (LPVOID)((DWORD)fwAddress - 5);
    LPVOID myHookAddress = &myHook;

    DWORD jmpOffset = (DWORD)&myHook - (DWORD)(!hotpatching ? fwAddress : fwHotpatchingAddress) - 5; // -5 because "JMP offset" = 5 bytes (1 + 4)

    printf("fwAddress: %X\n", fwAddress);
    printf("fwHotpatchingAddress: %X\n", fwHotpatchingAddress);
    printf("myHookAddress: %X\n", myHookAddress);
    printf("jmpOffset: %X\n", jmpOffset);
    printf("Ready?\n\n");
    getchar();


    char JMP[1] = {0xE9};
    char RETN[1] = {0xC3};

    LPVOID offset0 = NULL;
    LPVOID offset1 = NULL;
    LPVOID offset2 = NULL;

    if (!hotpatching)
        offset0 = fwAddress;
    else
        offset0 = fwHotpatchingAddress;

    offset1 = (LPVOID)((DWORD)offset0 + 1);
    offset2 = (LPVOID)((DWORD)offset1 + 4);


    DWORD oldProtect = 0;
    VirtualProtect(offset0, 6, PAGE_EXECUTE_READWRITE, &oldProtect);

    memcpy(fwAddress, JMP, 1);
    memcpy(offset1, &jmpOffset, 4);
    memcpy(offset2, RETN, 1);

    VirtualProtect(offset0, 6, oldProtect, &oldProtect);


    printf("FindWindowA() Patched");
    getchar();


    FindWindowA(NULL, "Test");
    getchar();


    return 0;
}
#包括
#包括
void myHook()
{
MessageBoxA(空,“钩住”,“钩住”,MB_图标信息);
}
int main(int argc,char*argv[])
{
布尔热修补=假;
LPVOID fwAddress=GetProcAddress(GetModuleHandleA(“user32.dll”),“FindWindowA”);
LPVOID-fwHotpatchingAddress=(LPVOID)((DWORD)fwAddress-5);
LPVOID myHookAddress=&myHook;
DWORD jmpOffset=(DWORD)和myHook-(DWORD)(!hotpatching?fwAddress:fwatpatchingaddress)-5;//-5,因为“JMP偏移量”=5字节(1+4)
printf(“fwAddress:%X\n”,fwAddress);
printf(“fwHotpatchingAddress:%X\n”,fwHotpatchingAddress);
printf(“myHookAddress:%X\n”,myHookAddress);
printf(“jmpOffset:%X\n”,jmpOffset);
printf(“准备好了吗?\n\n”);
getchar();
char JMP[1]={0xE9};
char-RETN[1]={0xC3};
LPVOID offset0=NULL;
LPVOID offset1=NULL;
LPVOID offset2=NULL;
如果(!热补丁)
offset0=地址;
其他的
偏移量0=fwHotpatchingAddress;
偏移量1=(LPVOID)((DWORD)偏移量0+1);
偏移量2=(LPVOID)((DWORD)偏移量1+4);
DWORD-oldProtect=0;
虚拟保护(偏移量0、6、页面执行、读写和旧保护);
memcpy(fwAddress,JMP,1);
memcpy(抵销1和jmpOffset,4);
memcpy(抵销2,RETN,1);
虚拟保护(偏移量0、6、oldProtect和oldProtect);
printf(“FindWindowA()补丁”);
getchar();
FindWindowA(空,“测试”);
getchar();
返回0;
}
你能告诉我怎么了吗


谢谢。

启用热修补的可执行映像由编译器和链接器准备,以允许在使用时替换映像。将应用以下两个更改(x86):

  • 函数入口点设置为2字节的no op
    mov edi,edi
    ()
  • 每个函数入口点()前面都有五个连续的nop
  • 为了说明这一点,下面是启用热打包功能的典型反汇编列表:

    (2) 768C8D66 90                   nop  
        768C8D67 90                   nop  
        768C8D68 90                   nop  
        768C8D69 90                   nop  
        768C8D6A 90                   nop  
    (1) 768C8D6B 8B FF                mov         edi,edi
    (3) 768C8D6D 55                   push        ebp  
        768C8D6E 8B EC                mov         ebp,esp  
    
    (1)
    用2字节no-op指定函数入口点。
    (2)
    是链接器提供的填充,而
    (3)
    是非平凡函数实现的开始

    要钩住一个函数,您必须通过跳转到钩子函数
    jmp myHook
    来覆盖
    (2)
    ,并通过用相对跳转
    jmp$-5
    替换
    (1)
    来访问此代码


    钩子函数必须使堆栈保持一致状态。它应该声明为防止编译器生成函数prolog和epilog代码。最后一条指令必须按照钩子函数的调用约定执行堆栈清理,或者跳回由
    (3)

    指定的地址处的钩子函数。如果未对函数进行热修补,则无法对函数进行热修补。未执行函数入口点之前的5个NOP。它们只是为动态创建的JMP指令留出空间。要获得此代码,您必须在函数入口点用相对跳转覆盖2字节NOP(
    mov edi,edi
    )。好的,我知道了!我覆盖NOP,但它们从未执行,因为它们在实际函数开始之前(
    mov edi,edi
    )。我必须将
    mov-edi,edi
    替换为向后跳转5个字节的JMP指令(或者7个字节,因为
    mov-edi,edi
    有2个字节长)。非常感谢。