inline hook的原理及实现

来源:互联网 发布:linux命令 chmod 编辑:程序博客网 时间:2024/05/17 06:58
网上没有找到多少关于inline hook的文章,感觉这方面资料不是很完整,所以决定自己写一份存档,重点讲述inline hook的实现。

inline hook的核心思想是:通过替换目标函数头部指令实现在函数执行之前跳转到其他的指令区域,执行完毕跳转回到原来的函数,跳转到的指令区域通常是我们自己编写的函数。inline hook技术对于编写外挂和外挂式补丁意义重大。

步骤:

1.使用VirtualAllocEx函数在目标进程创建一块内存区域,用于保存我们将要执行的指令。

VirtualAllocEx申请指令空间的一般用法如下:VirtualAllocEx(hProcess, NULL,len, MEM_COMMIT, PAGE_EXECUTE);

2.使用WriteProcessMemory函数将指令写入步骤一申请的内存区域。例如:WriteProcessMemory(hProcess,(void*)addr,(void*)val,len,NULL);

3.在目标函数头部替换指令,保存原来的指令,并且获取下一个完整指令的地址。这里我们必须说明要损坏的指令长度,因为jmp指令占用5个字节,假设目标函数头部第一条指令长度不足5字节,那么就需要破坏第二条指令,下一条完整的指令就是第三条指令。反之如果目标函数头部第一条指令长度大于5字节,下一条完整的指令就是第二条指令。

4.等待函数执行完毕。

5.回复目标函数头部被损坏的指令,使用VirtualFreeEx清理申请的内存区域。

具体实现如下:

typedef struct{    byte* HOOK_CODE;//指令数组指针    DWORD HOOK_CODE_LENGTH;//指令占的字节数    void* HOOK_PATH;//将要hook的目标函数地址    void* ASM_PATH;//被跳转的函数地址,即申请的内存空间地址} HOOK_INFO,*PHOOK_INFO;/*hook一个进程的某个地址hProcess:目标进程句柄path:目标函数地址dstlen:将要损坏的指令总长度,如果要损坏两条指令,需要两条指令的总长,用于计算下一条完整指令的地址code:将要执行的自定指令codelen:将要执行的自定指令的字节数hi:保存hook的信息*/void inline_hook(HANDLE hProcess,void*path,int dstlen,byte *code,int codelen,PHOOK_INFO hi){    byte *ASM_CODE=(byte*)malloc(codelen+5);//自定函数结束需要跳回原函数,所以结尾需要5字节长度的jmp,故申请内存长度需要+5    memcpy(ASM_CODE,code,codelen);//将指令复制    ASM_CODE[codelen]=0xe9;//0xE9是jmp指令    DWORD*retjmp=(DWORD *)&ASM_CODE[codelen+1];    PWSTR pCode=(PWSTR)VirtualAllocEx(hProcess, NULL, sizeof(codelen+5), MEM_COMMIT, PAGE_EXECUTE);//在目标进程申请内存    *retjmp=(DWORD)path+dstlen-((DWORD)pCode+codelen+5);    //上面这句是计算跳转的长度,path+dstlen即下一条完整指令的地址,减去自定函数结尾jmp的地址即跳转长度,+5是因为jmp指令本身有长度5    WriteProcessMemory(hProcess,(void*)pCode,(void*)&ASM_CODE[0],codelen+5,NULL);//把自定函数写入内存    byte HookCode[5]= {0xE9};//将要替换至目标函数头部的jmp指令,长度5    DWORD*jmp=(DWORD*)&HookCode[1];    *jmp=(DWORD)pCode-(DWORD)path-5;//计算跳转的长度,自定函数地址减去目标函数地址得到长度,但是jmp本身也有5的长度,故再减5        hi->HOOK_CODE=malloc(dstlen);    hi->HOOK_CODE_LENGTH=dstlen;    ReadProcessMemory(hProcess,path,hi->HOOK_CODE,dstlen,NULL);    WriteProcessMemory(hProcess,path,HookCode,5,NULL);    //    hi->HOOK_PATH=path;    hi->ASM_PATH=pCode;    Sleep(200);//等待执行,自定函数体积太大需要更长时间,安全的做法是在自定函数中写入通信的方式,例如使用一个内存数据作为标号,0代表没结束,1代表结束。}/*卸载hookhProcess:目标进程hi:从inline_hook得到的hook信息*/void un_inline_hook(HANDLE hProcess,PHOOK_INFO hi){    WriteProcessMemory(hProcess,hi->HOOK_PATH,hi->HOOK_CODE,hi->HOOK_CODE_LENGTH,NULL);    VirtualFreeEx(hProcess,hi->ASM_PATH,0,MEM_RELEASE);}//具体的使用方法//hook一段特征码取得esi寄存器的值int main(){    int r;    int path=aobscan("8BBE2C030000897B04");//aobscan的实现可以参考我的另一篇文章。    if(path<=0)    {        return 0;    }    byte ASM_CODE[]= {0x89,0x35,0x00,0x00,0x00,0x00,//mov [0],esi                      0x8B,0x86,0x2c,0x03,0x00,0x00//jmp 0                     };    //hProcess:目标进程    PWSTR pData=(PWSTR)VirtualAllocEx(hProcess, NULL, 4, MEM_COMMIT, PAGE_READWRITE);    *(DWORD*)&ASM_CODE[2]=(DWORD)pData;    HOOK_INFO hi;    inline_hook(hProcess,(void*)path,6,ASM_CODE,sizeof(ASM_CODE),&hi);    ReadProcessMemory(hProcess,(void*)pData,&r,4,NULL);    un_inline_hook(hProcess,&hi);    VirtualFreeEx(hProcess,(void*)pData,0,MEM_RELEASE);    printf("%x",r);    return 0;}

上面代码除了main函数外,其他的可以照搬使用,转载请注明出处。

2 1