Ring3 下 API Inline Hook 优化方案探索与实现
2016-05-03 09:30
531 查看
本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:/article/9672570.html
以前写过两篇“[Win32] API Hook(1)在32/64位系统上的实现”博客,介绍并给出了 API inline hook 代码,如下:
blog.csdn.net/zuishikonghuan/article/details/47976067
blog.csdn.net/zuishikonghuan/article/details/47979603
在这两篇博文中,我简要说了API inline hook的缺陷:
API inline hook的方法是修改API函数的前几个字节,让这几个字节执行无条件跳转指令,跳转到我们自己的函数里,这时候我们就可以根据参数进行一些判断,如果我们愿意放行,还可以恢复函数的前几字节并重新调用原函数,不愿意放行,就直接返回错误,Ring3级的进程保护就是这样实现的。
API inline hook在16位的系统上确实可以非常正常的工作,因为16位系统是单任务的!而如果是在32位的多任务抢占式操作系统上,系统可能会随时切换线程上下文,如果我们刚把函数前几个字节恢复了,系统突然把线程切换到了另一个线程,而那个线程又无巧不成书的调用了我们hook的这个函数,那么后果就是,出现了漏网之鱼!
而IAThook可以克服这个问题,但是IAThook的缺陷更是灾难性的!因为IAThook是通过修改导入地址表实现的,如果动态调用API,IAThook根本无法拦截。
当时我说这是一个小瑕疵,是的,在一些老系统(例如XP)上的确可以算是比较好的工作,但是现在看来,这个问题已经非常严重了,在Win8、8.1、10系统上,如果在explorer里Hook,会造成explorer特别容易崩溃!!(特别是 Hook NT Native API)
我希望找到新的解决方案,IAT HOOK 首先被否决了,因为 IAT HOOK 拦截不住 API 动态调用,在 Ring 0 中 Hook 更是行不通,因为在 amd64 平台上是有驱动程序强制签名的,买签名是很贵的,而且还不能干坏事。
没错,原理就是修改要 Hook 函数的前几个字节,问题关键就在于,如何能避免还原时恢复函数的这几个字节呢?
Google、Bing 后无果,于是只好自己想办法,我想,我们的目的是要对目标 API 进行过滤,对于同意放行的,我们可以再调用原函数,而程序载入内存后就是数据,这些 API 自然也不例外,而且这些 API 都是在 DLL 中,这些 DLL 需要映射(载入)到进程的线性地址空间(虚拟内存)中,那么,我们只需要将整个模块复制一遍,然后把不需要过滤的调用统统跳转到复制的区域不就好了。
那么,在我们的函数中:
而之前呢,手动恢复原函数,再去 Call 原函数,在多任务的环境下的确很不好。
这一步,我们需要注意的是:
1.要使我们复制的 kernel32 (或其他模块)具有可执行权限,比如
2.我们应该从内存中复制,而不是从磁盘上把 DLL 读进内存!同时,我们必须计算模块在内存中的大小,而不是去计算在磁盘中 DLL 文件的大小!(原因是:PE文件在磁盘上是连续存储的,但载入内存后每个节是按页面对齐的)(计算大小可以用 VirtualQueryEx 、ToolHelp API 或 读取PE头部(在PE文件头的0x50偏移处存放),这里我使用是 VirtualQueryEx ,如果用第三种那种方法可移植性就不好了,万一PE结构变了呢,而且32位和64位的PE结构应该不一样呢)
现在,我们已经实现了我们的目的!事实上他已经非常健壮且稳定的工作了!经过测试,注入 explorer 长时间后已经不会造成崩溃,也不会造成性能明显下降,用户已经感觉不出什么不同了。
但是,还能更好一点吗?
其实,这一套机制有一个非常不友好的行为,那就是
于是,我首先想到了共享内存,既然每个进程都复制一份太浪费了,那么我就创建一个 Daemon 进程,专门负责这件事,即创建映射、复制模块,然后其他进程直接把这块数据映射到自己的线性地址空间(虚拟内存)中就好了,就不会出现重复复制浪费内存的问题了。但是经过不断测试研究才发现,这样根本行不通。
原因有两个:
1. 你怎么保证每个进程中的 kernel32 (或其他模块)中的这个函数都一样?
2. 你怎么保证每个进程都能将 Daemon 创建的共享内存映射到自己的线性地址空间?
这两个问题,咋一听有点无稽之谈,难道还会不一样?其实,如果此时有其他程序(比如杀软等软件) Hook 了这个函数,那么他也需要把自己 DLL 注入到每个进程中,问题来了,他的 DLL 能载入到他预期的位置吗?答案是不确定的,那么重定位是很有可能发生的,那么也就意味着他修改后的代码是不一样的!那么如果我们再次 Hook 后,创建的共享内存里肯定是 Daemon 的代码吧,里面的指令是跳转到上一个 Hook 函数里,这时,如果其他进程将这块共享内存映射到自己的线性地址空间中,这个跳转的地址还会是上一个 Hook 的地址吗?此时我们的 Hook 跳转到原函数时,就会跳转到一个非法的地址,结果是什么很难预料,不过崩溃的可能性是很大的,不是吗?
你可以说“那我们可以再把每个进程中的这几个字节复制进去吗,几个字节而已了”但是 Inline Hook 的方法千奇百怪,不一定都是头部的字节,字节大小也不一定一样,而且还有在函数内部 Inline Hook 的,所有这样弄成本太高。
第二个问题就是,其他程序不一定能映射我们创建的共享内存。第一个是权限的问题,第二个是完整性的问题,权限还好解决,我们的 Daemon 可以以低权限运行,并且还可以创建共享内存时创建一个允许任何人访问的“ACL”(访问控制列表)(虽然很麻烦),但是还有完整性呢,比如 IE 就是第完整性的进程,进程还可以降低完整性创建进程,低完整性的程序不能访问更高完整性的进程,额。。
话说回来,这两点有点吹毛求疵了,不过,我们要把用户体验做到极致,不是吗?
原因是操作系统映射 DLL 到进程内存时,其实是共享的,他只是载入一份,然后每个进程要求载入时直接映射到目标进程的线性地址空间(虚拟内存)中,如果程序修改了这部分虚拟内存,会触发“写时复制”机制,使修改局限于一个进程,因此我们也这样做就OK了。
这是最终的代码,既解决了 Inline Hook 的稳定性问题、可能出现漏网之鱼的问题,也解决了上文中第一个解决方案的不必要资源浪费的问题。对了,这个代码兼容 x86 和 amd64 哦!
其实请大家放心,我们只是将 kernel32 (或其他模块)以可执行映像的形式载入到进程的线性地址空间(虚拟内存)中,实际上并没有完成 DLL 的载入过程,也就是说,不会影响 GetModuleHandle 和 LoadLibrary 的执行结果,更不会去调用 DLL 的 DllEntry(废话我们只是映射进来又没有调用),并且这一点我已经经过了测试。
然后继续想了想,发现这里面还有文章可做呢!
用这种思路,可以 Anti Ring3 Hook !我们可以自己载入一份模块,然后找到 API 的位置,直接调用,即可绕过 Ring 3 下的钩子,其实这种方法并不新鲜了,但网上的说法大多是复制一份 DLL 再 Load,其实没有必要,按照上文的方法重新映射一遍就好了。
但是,我想说的是,Hook 者要如何防范这个问题呢?最好不要动 CreateFileMapping 、ZwCreateSection,因为这可能造成程序工作不正常,或者造成你 Hook 了让另一个软件的这种 Hook 无法工作,因此,我们可以 Hook MapViewOfFile 或更低的接口,先自己映射一遍,然后对新映射的这一部分代码再进行 Hook (即修改新映射的内存中 API 的前几个字节),然后再把地址告诉应用程序,嘿嘿,进程根本不知道得到的新映射的模块映像已经是动过手脚的了。
这篇博文到这里就结束了,记录了我的这次Ring3 下 Inline Hook 优化方案探索过程,结果也很完美。
以前写过两篇“[Win32] API Hook(1)在32/64位系统上的实现”博客,介绍并给出了 API inline hook 代码,如下:
blog.csdn.net/zuishikonghuan/article/details/47976067
blog.csdn.net/zuishikonghuan/article/details/47979603
在这两篇博文中,我简要说了API inline hook的缺陷:
API inline hook的方法是修改API函数的前几个字节,让这几个字节执行无条件跳转指令,跳转到我们自己的函数里,这时候我们就可以根据参数进行一些判断,如果我们愿意放行,还可以恢复函数的前几字节并重新调用原函数,不愿意放行,就直接返回错误,Ring3级的进程保护就是这样实现的。
API inline hook在16位的系统上确实可以非常正常的工作,因为16位系统是单任务的!而如果是在32位的多任务抢占式操作系统上,系统可能会随时切换线程上下文,如果我们刚把函数前几个字节恢复了,系统突然把线程切换到了另一个线程,而那个线程又无巧不成书的调用了我们hook的这个函数,那么后果就是,出现了漏网之鱼!
而IAThook可以克服这个问题,但是IAThook的缺陷更是灾难性的!因为IAThook是通过修改导入地址表实现的,如果动态调用API,IAThook根本无法拦截。
当时我说这是一个小瑕疵,是的,在一些老系统(例如XP)上的确可以算是比较好的工作,但是现在看来,这个问题已经非常严重了,在Win8、8.1、10系统上,如果在explorer里Hook,会造成explorer特别容易崩溃!!(特别是 Hook NT Native API)
我希望找到新的解决方案,IAT HOOK 首先被否决了,因为 IAT HOOK 拦截不住 API 动态调用,在 Ring 0 中 Hook 更是行不通,因为在 amd64 平台上是有驱动程序强制签名的,买签名是很贵的,而且还不能干坏事。
那么我们就要认命吗?
于是我试图从 Inline Hook 的实现方法上找原因,这是我写的 Inline Hook 代码(x86 和 amd 64)没错,原理就是修改要 Hook 函数的前几个字节,问题关键就在于,如何能避免还原时恢复函数的这几个字节呢?
//进行 Inline Hook HMODULE hdll = LoadLibrary(TEXT("kernel32.dll")); addr = GetProcAddress(hdll, "OpenProcess"); if (addr){ #ifdef _M_IX86 code[0] = 0xe9; aint a = (aint)MyOpenProcess - (aint)addr - 5; RtlMoveMemory(code + 1, &a, 4); DWORD old; if (VirtualProtectEx(GetCurrentProcess(), addr, 5, PAGE_EXECUTE_READWRITE, &old)){ RtlMoveMemory(oldcode, addr, 5); WriteProcessMemory(GetCurrentProcess(), addr, code, 5, NULL); VirtualProtectEx(GetCurrentProcess(), addr, 5, old, &old); } #elif _M_X64 code[0] = 0x48; code[1] = 0xB8; code[10] = 0x50; code[11] = 0xC3; aint a = (aint)MyOpenProcess; RtlMoveMemory(code + 2, &a, 8); DWORD old; if (VirtualProtectEx(GetCurrentProcess(), addr, 12, PAGE_EXECUTE_READWRITE, &old)){ RtlMoveMemory(oldcode, addr, 12); WriteProcessMemory(GetCurrentProcess(), addr, code, 12, NULL); VirtualProtectEx(GetCurrentProcess(), addr, 12, old, &old); } #endif }
Google、Bing 后无果,于是只好自己想办法,我想,我们的目的是要对目标 API 进行过滤,对于同意放行的,我们可以再调用原函数,而程序载入内存后就是数据,这些 API 自然也不例外,而且这些 API 都是在 DLL 中,这些 DLL 需要映射(载入)到进程的线性地址空间(虚拟内存)中,那么,我们只需要将整个模块复制一遍,然后把不需要过滤的调用统统跳转到复制的区域不就好了。
第一次探索:将函数所在模块复制一份
#ifdef _M_IX86 #define aint DWORD #elif _M_X64 #define aint unsigned long long #endif //模块基地址 HMODULE BaseAddr = GetModuleHandle(TEXT("kernel32.dll")); //计算模块在内存中的大小 MEMORY_BASIC_INFORMATION MemoryBasicInfomation; Size = 0; VirtualQueryEx(GetCurrentProcess(), BaseAddr, &MemoryBasicInfomation, sizeof(MEMORY_BASIC_INFORMATION)); PVOID BaseAddress = MemoryBasicInfomation.AllocationBase; while (MemoryBasicInfomation.AllocationBase == BaseAddress) { Size += MemoryBasicInfomation.RegionSize; VirtualQueryEx(GetCurrentProcess(), BaseAddr + Size, &MemoryBasicInfomation, sizeof(MEMORY_BASIC_INFORMATION)); } //分配内存并复制 Buf = VirtualAlloc(NULL, Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); RtlMoveMemory(Buf, BaseAddr, Size); //进行 Inline Hook HMODULE hdll = LoadLibrary(TEXT("kernel32.dll")); addr = GetProcAddress(hdll, "OpenProcess"); if (addr){ #ifdef _M_IX86 code[0] = 0xe9; aint a = (aint)MyOpenProcess - (aint)addr - 5; RtlMoveMemory(code + 1, &a, 4); DWORD old; if (VirtualProtectEx(GetCurrentProcess(), addr, 5, PAGE_EXECUTE_READWRITE, &old)){ RtlMoveMemory(oldcode, addr, 5); WriteProcessMemory(GetCurrentProcess(), addr, code, 5, NULL); VirtualProtectEx(GetCurrentProcess(), addr, 5, old, &old); } #elif _M_X64 code[0] = 0x48; code[1] = 0xB8; code[10] = 0x50; code[11] = 0xC3; aint a = (aint)MyOpenProcess; RtlMoveMemory(code + 2, &a, 8); DWORD old; if (VirtualProtectEx(GetCurrentProcess(), addr, 12, PAGE_EXECUTE_READWRITE, &old)){ RtlMoveMemory(oldcode, addr, 12); WriteProcessMemory(GetCurrentProcess(), addr, code, 12, NULL); VirtualProtectEx(GetCurrentProcess(), addr, 12, old, &old); } #endif } //记录偏移量 offset = (aint)addr - (aint)BaseAddr; DWORD old2; VirtualProtectEx(GetCurrentProcess(), Buf, Size, PAGE_EXECUTE_READWRITE, &old2);
那么,在我们的函数中:
#ifdef _M_IX86 #define aint DWORD #elif _M_X64 #define aint unsigned long long #endif HANDLE WINAPI MyOpenProcess( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId ){ HANDLE handle = 0; //保护我们的进程,发现被 Hook 的进程试图访问我们的进程时返回“拒绝访问” if (getpid() == dwProcessId){ SetLastError(5); return NULL; } //如果不是访问我们的进程则去 Call 复制的 kernel32 typedef HANDLE(WINAPI * OpenProcess_t)(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId); OpenProcess_t RealOpenProcess = (OpenProcess_t)((aint)Buf + offset); handle = RealOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); return handle; }
而之前呢,手动恢复原函数,再去 Call 原函数,在多任务的环境下的确很不好。
DWORD old; if (VirtualProtectEx(GetCurrentProcess(), addr, 5, PAGE_EXECUTE_READWRITE, &old)){ WriteProcessMemory(GetCurrentProcess(), addr, oldcode, 5, NULL); VirtualProtectEx(GetCurrentProcess(), addr, 5, old, &old); } handle = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); if (VirtualProtectEx(GetCurrentProcess(), addr, 5, PAGE_EXECUTE_READWRITE, &old)){ WriteProcessMemory(GetCurrentProcess(), addr, code, 5, NULL); VirtualProtectEx(GetCurrentProcess(), addr, 5, old, &old); }
这一步,我们需要注意的是:
1.要使我们复制的 kernel32 (或其他模块)具有可执行权限,比如
PAGE_EXECUTE_READWRITE
2.我们应该从内存中复制,而不是从磁盘上把 DLL 读进内存!同时,我们必须计算模块在内存中的大小,而不是去计算在磁盘中 DLL 文件的大小!(原因是:PE文件在磁盘上是连续存储的,但载入内存后每个节是按页面对齐的)(计算大小可以用 VirtualQueryEx 、ToolHelp API 或 读取PE头部(在PE文件头的0x50偏移处存放),这里我使用是 VirtualQueryEx ,如果用第三种那种方法可移植性就不好了,万一PE结构变了呢,而且32位和64位的PE结构应该不一样呢)
现在,我们已经实现了我们的目的!事实上他已经非常健壮且稳定的工作了!经过测试,注入 explorer 长时间后已经不会造成崩溃,也不会造成性能明显下降,用户已经感觉不出什么不同了。
但是,还能更好一点吗?
其实,这一套机制有一个非常不友好的行为,那就是
Buf = VirtualAlloc(NULL, Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);他会向操作系统申请内存,用于存放复制的 kernel32.dll ,而在 Ring 3 下 Hook ,需要注入到每一个进程,那么我们在每一个进程中都执行一遍复制,那么会浪费太多的内存,而且这种浪费是完全没有必要的。
第二次探索:使用共享内存
其实,如果只是为了 Hook 一个进程,上面的那一套已经非常成熟了。换句话说,上面的一套机制保证了稳定性,但是如果要 Hook 多个进程或全局 Hook ,则会造成一定的资源浪费,这是不能容忍的。于是,我首先想到了共享内存,既然每个进程都复制一份太浪费了,那么我就创建一个 Daemon 进程,专门负责这件事,即创建映射、复制模块,然后其他进程直接把这块数据映射到自己的线性地址空间(虚拟内存)中就好了,就不会出现重复复制浪费内存的问题了。但是经过不断测试研究才发现,这样根本行不通。
原因有两个:
1. 你怎么保证每个进程中的 kernel32 (或其他模块)中的这个函数都一样?
2. 你怎么保证每个进程都能将 Daemon 创建的共享内存映射到自己的线性地址空间?
这两个问题,咋一听有点无稽之谈,难道还会不一样?其实,如果此时有其他程序(比如杀软等软件) Hook 了这个函数,那么他也需要把自己 DLL 注入到每个进程中,问题来了,他的 DLL 能载入到他预期的位置吗?答案是不确定的,那么重定位是很有可能发生的,那么也就意味着他修改后的代码是不一样的!那么如果我们再次 Hook 后,创建的共享内存里肯定是 Daemon 的代码吧,里面的指令是跳转到上一个 Hook 函数里,这时,如果其他进程将这块共享内存映射到自己的线性地址空间中,这个跳转的地址还会是上一个 Hook 的地址吗?此时我们的 Hook 跳转到原函数时,就会跳转到一个非法的地址,结果是什么很难预料,不过崩溃的可能性是很大的,不是吗?
你可以说“那我们可以再把每个进程中的这几个字节复制进去吗,几个字节而已了”但是 Inline Hook 的方法千奇百怪,不一定都是头部的字节,字节大小也不一定一样,而且还有在函数内部 Inline Hook 的,所有这样弄成本太高。
第二个问题就是,其他程序不一定能映射我们创建的共享内存。第一个是权限的问题,第二个是完整性的问题,权限还好解决,我们的 Daemon 可以以低权限运行,并且还可以创建共享内存时创建一个允许任何人访问的“ACL”(访问控制列表)(虽然很麻烦),但是还有完整性呢,比如 IE 就是第完整性的进程,进程还可以降低完整性创建进程,低完整性的程序不能访问更高完整性的进程,额。。
话说回来,这两点有点吹毛求疵了,不过,我们要把用户体验做到极致,不是吗?
第三次探索:那么自己再次载入一份要 Hook 的函数所在的模块呢
没错,我们自己模拟系统再次载入一个 kernel32 (或其他模块),不就完事大吉了吗。CreateFileMapping 一定要是SEC_IMAGE | PAGE_READONLY,即载入的是一个可执行文件映像,不然操作系统不会帮你把每个节是按页面对齐的。
原因是操作系统映射 DLL 到进程内存时,其实是共享的,他只是载入一份,然后每个进程要求载入时直接映射到目标进程的线性地址空间(虚拟内存)中,如果程序修改了这部分虚拟内存,会触发“写时复制”机制,使修改局限于一个进程,因此我们也这样做就OK了。
这是最终的代码,既解决了 Inline Hook 的稳定性问题、可能出现漏网之鱼的问题,也解决了上文中第一个解决方案的不必要资源浪费的问题。对了,这个代码兼容 x86 和 amd64 哦!
#ifdef _M_IX86 #define aint DWORD #elif _M_X64 #define aint unsigned long long #endif case DLL_PROCESS_ATTACH:{ TCHAR WinDir[MAX_PATH]; GetWindowsDirectory(WinDir, MAX_PATH); std::wstring dir = WinDir; if ((wchar_t*)dir.at(dir.size() - 1) != L"\\") dir += L"\\"; std::wstring dll = dir + L"system32\\kernel32.dll"; hFile = CreateFile(dll.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE){ //如果进程调用了 Wow64DisableWow64FsRedirection 那么必定无法加载 system32\\kernel32.dll dll = dir + L"SysWOW64\\kernel32.dll"; HANDLE hFile = CreateFile(dll.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE){ goto cleanup; } } dir.clear(); dll.clear(); hMapFile = 0; Buf = 0; if ((hMapFile = CreateFileMapping(hFile, NULL, SEC_IMAGE | PAGE_READONLY, 0, 0, NULL)) != NULL){ if ((Buf = MapViewOfFile(hMapFile, FILE_MAP_COPY, 0, 0, 0)) == 0) goto cleanup; } else goto cleanup; //进行 Inline Hook addr = 0; HMODULE hdll = LoadLibrary(TEXT("kernel32.dll")); addr = GetProcAddress(hdll, "OpenProcess"); if (addr){ #ifdef _M_IX86 code[0] = 0xe9; aint a = (aint)MyOpenProcess - (aint)addr - 5; RtlMoveMemory(code + 1, &a, 4); DWORD old; if (VirtualProtectEx(GetCurrentProcess(), addr, 5, PAGE_EXECUTE_READWRITE, &old)){ RtlMoveMemory(oldcode, addr, 5); WriteProcessMemory(GetCurrentProcess(), addr, code, 5, NULL); VirtualProtectEx(GetCurrentProcess(), addr, 5, old, &old); } else goto cleanup; #elif _M_X64 code[0] = 0x48; code[1] = 0xB8; code[10] = 0x50; code[11] = 0xC3; aint a = (aint)MyOpenProcess; RtlMoveMemory(code + 2, &a, 8); DWORD old; if (VirtualProtectEx(GetCurrentProcess(), addr, 12, PAGE_EXECUTE_READWRITE, &old)){ RtlMoveMemory(oldcode, addr, 12); WriteProcessMemory(GetCurrentProcess(), addr, code, 12, NULL); VirtualProtectEx(GetCurrentProcess(), addr, 12, old, &old); } else goto cleanup; #endif } HMODULE BaseAddr = GetModuleHandle(TEXT("kernel32.dll")); //记录偏移量 offset = (aint)addr - (aint)BaseAddr; DWORD old2; VirtualProtectEx(GetCurrentProcess(), Buf, Size, PAGE_EXECUTE_READWRITE, &old2); return TRUE; cleanup: if (Buf) UnmapViewOfFile(Buf); if (hMapFile) CloseHandle(hMapFile); if (hFile) CloseHandle(hFile); return FALSE; break; }
会不会带来什么影响呢?
从 Process Explorer 中会看到被 Hook 进程加载了两个 Kernel32.dll (或其他模块)如图:其实请大家放心,我们只是将 kernel32 (或其他模块)以可执行映像的形式载入到进程的线性地址空间(虚拟内存)中,实际上并没有完成 DLL 的载入过程,也就是说,不会影响 GetModuleHandle 和 LoadLibrary 的执行结果,更不会去调用 DLL 的 DllEntry(废话我们只是映射进来又没有调用),并且这一点我已经经过了测试。
继续拓宽思维
嘿嘿,自己改进了 Ring3 下的 Inline Hook ,克服了持续多年的 Inline Hook 的问题,心情简直 exciting 。然后继续想了想,发现这里面还有文章可做呢!
用这种思路,可以 Anti Ring3 Hook !我们可以自己载入一份模块,然后找到 API 的位置,直接调用,即可绕过 Ring 3 下的钩子,其实这种方法并不新鲜了,但网上的说法大多是复制一份 DLL 再 Load,其实没有必要,按照上文的方法重新映射一遍就好了。
但是,我想说的是,Hook 者要如何防范这个问题呢?最好不要动 CreateFileMapping 、ZwCreateSection,因为这可能造成程序工作不正常,或者造成你 Hook 了让另一个软件的这种 Hook 无法工作,因此,我们可以 Hook MapViewOfFile 或更低的接口,先自己映射一遍,然后对新映射的这一部分代码再进行 Hook (即修改新映射的内存中 API 的前几个字节),然后再把地址告诉应用程序,嘿嘿,进程根本不知道得到的新映射的模块映像已经是动过手脚的了。
这篇博文到这里就结束了,记录了我的这次Ring3 下 Inline Hook 优化方案探索过程,结果也很完美。
相关文章推荐
- JavaScript arguments 对象
- copypng emitted errors but did not return a nonzero exit code to错误
- 采购订单审批BAPI ---BAPI_PO_RELEASE
- PHP实现的进度条效果详解
- 今日BBC
- 如何根据事物代码查找相应BAPI
- elasticsearch远程代码执行漏洞告警
- 关于iOS和OS X废弃的API你需要知道的一切
- Activity的创建和显示以及源码分析记录
- form表单ajaxSubmit提交并验证
- elasticsearch远程代码执行漏洞告警
- iOS开发中常用到的加密方式
- abap webservice创建和删除
- [BZOJ1657][Usaco2006 Mar]Mooo 奶牛的歌声(单调栈)
- 指向运算符会移动指针吗?
- linux环境下如何快速升级你的Node.js
- 大津法二值化 linux c语言代码
- Javascript验证和限制文本框只能输入数字或者小数
- Android中的Rect类
- html5 基本语法详解