分享

[Win32] API Hook(1)在32位系统上的实现

 黑犬黑犬_924 2017-11-21

API Hook技术,虽然很老了,但是依旧是很有用的技术,同时网上的资料往往不能直接拿过来用,c语言的太少。这回呢,我要用C语言来完整的编写一个Win32SDK编程的API Hook源码,但是我还是用cpp保存编译,为哈?因为微软对c语言标准支持的很不好,我不知道vs2013支持c99了吗,用cpp只是为了弥补c89不支持在函数中间定义变量等一系列问题,闲话不多说,进入正题,API hook的方法有很多种,比较常见的有:API inline hook、IAT hook等。

API inline hook的方法是修改API函数的前几个字节,让这几个字节执行无条件跳转指令,跳转到我们自己的函数里,这时候我们就可以根据参数进行一些判断,如果我们愿意放行,还可以恢复函数的前几字节并重新调用原函数,不愿意放行,就直接返回错误,

“Ring3级的进程保护”就是这样实现的。

API inline hook在16位的系统上确实可以非常正常的工作,因为16位系统是单任务的!而如果是在32位的多任务抢占式操作系统上,系统可能会随时切换线程上下文,如果我们刚把函数前几个字节恢复了,系统突然把线程切换到了另一个线程,而那个线程又无巧不成书的调用了我们hook的这个函数,那么后果就是,出现了漏网之鱼!

而IAThook可以克服这个问题,但是IAThook的缺陷更是灾难性的!因为IAThook是通过修改导入地址表实现的,如果动态调用API,IAThook根本无法拦截。

所以,API inline hook虽然有一些小瑕疵,但是绝对是Rin3级hook最好的选择之一!

使用API inline hook需要注意的地方:

1。hook的函数和你的函数必须用一样的形参表

2。hook的函数和你的函数必须用一样的调用约定(我上回就是没有想到这个,导致hook的函数一调用就崩溃)

原因很简单,如果不着么做,后果就是函数调用前后堆栈不平衡,引发程序异常。

3。API inline hook需要在目标进程的地址空间内进行,需要DLL注入。

好了,上DLL的代码

  1. #include <stdio.h>  
  2. #include <windows.h>  
  3.   
  4. unsigned char code[5];  
  5. unsigned char oldcode[5];  
  6. FARPROC addr;  
  7. DWORD pid;  
  8.   
  9. int getpid()  
  10. {  
  11.     char buffer[255];  
  12.     DWORD get = 255;  
  13.     //判断环境是否为WOW64  
  14.     BOOL isWOW64;  
  15.     REGSAM p = KEY_READ;  
  16.     IsWow64Process(GetCurrentProcess(), &isWOW64);  
  17.     if (isWOW64)p |= KEY_WOW64_64KEY;  
  18.   
  19.     HKEY hKey;  
  20.     if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\测试"), 0, NULL, 0, p, NULL, &hKey, NULL) != ERROR_SUCCESS){  
  21.         return 0;  
  22.     }  
  23.     if (RegQueryValueExA(hKey, "Main_PID", 0, NULL, (BYTE*)buffer, &get) != ERROR_SUCCESS){  
  24.         return 0;  
  25.     }  
  26.     return atoi(buffer);  
  27. }  
  28.   
  29. HANDLE WINAPI MyOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId){  
  30.     HANDLE handle;  
  31.     if (getpid() == dwProcessId){  
  32.         SetLastError(5);  
  33.         return NULL;  
  34.     }  
  35.   
  36.     DWORD old;  
  37.     if (VirtualProtectEx(GetCurrentProcess(), addr, 5, PAGE_EXECUTE_READWRITE, &old)){  
  38.         WriteProcessMemory(GetCurrentProcess(), addr, oldcode, 5, NULL);  
  39.         VirtualProtectEx(GetCurrentProcess(), addr, 5, old, &old);  
  40.     }  
  41.     handle = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);  
  42.     if (VirtualProtectEx(GetCurrentProcess(), addr, 5, PAGE_EXECUTE_READWRITE, &old)){  
  43.         WriteProcessMemory(GetCurrentProcess(), addr, code, 5, NULL);  
  44.         VirtualProtectEx(GetCurrentProcess(), addr, 5, old, &old);  
  45.     }  
  46.   
  47.     return handle;  
  48. }  
  49.   
  50. BOOL APIENTRY DllMain(HMODULE hModule,  
  51.     DWORD  ul_reason_for_call,  
  52.     LPVOID lpReserved  
  53.     )  
  54. {  
  55.     switch (ul_reason_for_call)  
  56.     {  
  57.     case DLL_PROCESS_ATTACH:  
  58.         addr = 0;  
  59.         HMODULE hdll; hdll = LoadLibrary(TEXT("Kernel32.dll"));  
  60.         addr = GetProcAddress(hdll, "OpenProcess");  
  61.         if (addr){  
  62.             code[0] = 0xe9;  
  63.             DWORD a = (DWORD)MyOpenProcess - (DWORD)addr - 5;  
  64.             RtlMoveMemory(code + 1, &a, 4);  
  65.   
  66.             DWORD old;  
  67.             if (VirtualProtectEx(GetCurrentProcess(), addr, 5, PAGE_EXECUTE_READWRITE, &old)){  
  68.                 RtlMoveMemory(oldcode, addr, 5);  
  69.                 WriteProcessMemory(GetCurrentProcess(), addr, code, 5, NULL);  
  70.                 VirtualProtectEx(GetCurrentProcess(), addr, 5, old, &old);  
  71.             }  
  72.         }  
  73.     case DLL_THREAD_ATTACH:  
  74.     case DLL_THREAD_DETACH:  
  75.     case DLL_PROCESS_DETACH:  
  76.         break;  
  77.     }  
  78.     return TRUE;  
  79. }  


有一点需要注意一下,这个代码只对32位有效,64位的见下一篇。

下面来简单解释一下这个代码

DLL_PROCESS_ATTACH里,dll加载进目标程序,用GetProcAddress函数得到OpenProcess函数在内存中的位置,code[0]里存储一个jmp指令,jmp指令是根据位移无条件跳转指令,具体可查询win32汇编的资料。

随后计算我们的函数和OpenProcess之间的位移,并存储在code[1-4]里

VirtualProtectEx修改虚拟内存页面保护,如果不修改会引发虚拟内存访问违规。

之后,RtlMoveMemory(oldcode, addr, 5);将原函数的前五个字节保存在oldcode里面,WriteProcessMemory修改前五个字节

MyOpenProcess里面,读取了一个注册表键值,如果相等,则将错误码设置为“拒绝访问”,然后返回NULL,如果不是我们要保护的程序,就暂时恢复OpenProcess,并调用,返回结果。

效果图:

我用的是64位系统,因此我需要启动32位的任务管理器,在这里:C:\Windows\SysWOW64\Taskmgr.exe,32位系统启动普通的任务管理器即可




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多