Anti HookAPI学习笔记
Anti HookAPI学习笔记
Hookapi分为2种,ring3 下挂钩 API 基本上也就是修改导入表,和Inline hook 修改前5个字节这几种方法,关于挂钩API 请参见:www.xfocus.net/articles/200403/681.html
部分引用如下:
引用:
| 这里对我们比较重要的是.idata部分的导入地址表(IAT)。这个部分包含了导入的相关信息和导入函数的地址。有一点很重要的是我们必须知道PE文件 是如何创建的。当在编程语言里间接调用任意API(这意味着我们是用函数的名字来调用它,而不是用它的地址),编译器并不直接把调用连接到模块,而是用 jmp指令连接调用到IAT,IAT在系统把进程调入内存时时会由进程载入器填满。这就是我们可以在两个不同版本的Windows里使用相同的二进制代码 的原因,虽然模块可能会加载到不同的地址。进程载入器会在程序代码里调用所使用的IAT里填入直接跳转的jmp指令。所以我们能在IAT里找到我们想要挂 钩的指定函数,我们就能很容易改变那里的jmp指令并重定向代码到我们的地址。完成之后每次调用都会执行我们的代码了。 |
最简单得防护方法 调用下层API
比如RegSetValueA 偶得代码其实就是
引用:
| 77DB6F49 > 8BFF mov edi,edi 77DB6F4B . 55 push ebp 77DB6F4C . 8BEC mov ebp,esp 77DB6F4E . 51 push ecx 77DB6F4F . 53 push ebx 77DB6F50 . 33DB xor ebx,ebx 77DB6F52 . 817D 08 04000080 cmp dword ptr ss:[ebp+8],80000004 77DB6F59 . 895D FC mov dword ptr ss:[ebp-4],ebx 77DB6F5C . 0F84 B7020200 je ADVAPI32.77DD7219 77DB6F62 . 837D 10 01 cmp dword ptr ss:[ebp+10],1 77DB6F66 . 75 78 jnz short ADVAPI32.77DB6FE0 77DB6F68 . 395D 14 cmp dword ptr ss:[ebp+14],ebx 77DB6F6B . 74 73 je short ADVAPI32.77DB6FE0 77DB6F6D . 56 push esi 77DB6F6E . 8D45 FC lea eax,dword ptr ss:[ebp-4] 77DB6F71 . 50 push eax 77DB6F72 . FF75 08 push dword ptr ss:[ebp+8] 77DB6F75 . E8 46F9FEFF call ADVAPI32.77DA68C0 77DB6F7A . 8BF0 mov esi,eax 77DB6F7C . 3BF3 cmp esi,ebx 77DB6F7E . 8975 08 mov dword ptr ss:[ebp+8],esi 77DB6F81 . 0F84 96020200 je ADVAPI32.77DD721D 77DB6F87 . 8B45 14 mov eax,dword ptr ss:[ebp+14] 77DB6F8A . 8D50 01 lea edx,dword ptr ds:[eax+1] 77DB6F8D > 8A08 mov cl,byte ptr ds:[eax] 77DB6F8F . 40 inc eax 77DB6F90 . 3ACB cmp cl,bl 77DB6F92 .^ 75 F9 jnz short ADVAPI32.77DB6F8D 77DB6F94 . 2BC2 sub eax,edx 77DB6F96 . 57 push edi 77DB6F97 . 8D78 01 lea edi,dword ptr ds:[eax+1] 77DB6F9A . 8B45 0C mov eax,dword ptr ss:[ebp+C] 77DB6F9D . 3BC3 cmp eax,ebx 77DB6F9F . 0F85 80020200 jnz ADVAPI32.77DD7225 77DB6FA5 > 8975 10 mov dword ptr ss:[ebp+10],esi 77DB6FA8 > 57 push edi ; /BufSize 77DB6FA9 . FF75 14 push dword ptr ss:[ebp+14] ; |Buffer 77DB6FAC . 6A 01 push 1 ; |ValueType = REG_SZ 77DB6FAE . 53 push ebx ; |Reserved 77DB6FAF . 53 push ebx ; |ValueName 77DB6FB0 . FF75 10 push dword ptr ss:[ebp+10] ; |hKey 77DB6FB3 . E8 2F7CFFFF call ADVAPI32.RegSetValueExA ; \RegSetValueExA 77DB6FB8 . 8BF0 mov esi,eax 77DB6FBA . 8B45 10 mov eax,dword ptr ss:[ebp+10] 77DB6FBD . 3B45 08 cmp eax,dword ptr ss:[ebp+8] 77DB6FC0 . 0F85 7E020200 jnz ADVAPI32.77DD7244 77DB6FC6 > 5F pop edi 77DB6FC7 > 395D FC cmp dword ptr ss:[ebp-4],ebx 77DB6FCA . 0F85 81020200 jnz ADVAPI32.77DD7251 77DB6FD0 > 8BC6 mov eax,esi 77DB6FD2 . 5E pop esi 77DB6FD3 > 5B pop ebx 77DB6FD4 . C9 leave 77DB6FD5 . C2 1400 retn 14 |
最终进入得是RegSetValueExA 其实还会继续进去RegSetValueExW。。。。
2.对于部分API有效得防护方法 使用汇编代码模拟
例如; GetVersion 其实可以自己写汇编代码实现
引用:
| 7C8111DA > 64:A1 18000000 mov eax,dword ptr fs:[18] 7C8111E0 8B48 30 mov ecx,dword ptr ds:[eax+30] 7C8111E3 8B81 B0000000 mov eax,dword ptr ds:[ecx+B0] 7C8111E9 0FB791 AC000000 movzx edx,word ptr ds:[ecx+AC] 7C8111F0 83F0 FE xor eax,FFFFFFFE 7C8111F3 C1E0 0E shl eax,0E 7C8111F6 0BC2 or eax,edx 7C8111F8 C1E0 08 shl eax,8 7C8111FB 0B81 A8000000 or eax,dword ptr ds:[ecx+A8] 7C811201 C1E0 08 shl eax,8 7C811204 0B81 A4000000 or eax,dword ptr ds:[ecx+A4] 7C81120A C3 retn |
引用:
| 7C809920 > 64:A1 18000000 mov eax,dword ptr fs:[18] 7C809926 8B40 20 mov eax,dword ptr ds:[eax+20] 7C809929 C3 retn |
引用:
| 7C813093 > 64:A1 18000000 mov eax,dword ptr fs:[18] 7C813099 8B40 30 mov eax,dword ptr ds:[eax+30] 7C81309C 0FB640 02 movzx eax,byte ptr ds:[eax+2] 7C8130A0 C3 retn |
3.以上所说得方法都是属于治标不知本或者是有局限性得,真正得对付修改IAT地址得手段得方法是自己用GetProcAddress函数获取
注意了!使用GetProcAddress函数获取最好是自己写GetProcAddress函数去获取,而不是直接调用系统得GetProcAddress函数,什么原因呢?因为系统得GetProcAddress函数可是已经杯做过手脚得了
给出一段代码
lifeengines(netsowell)大侠得
引用:
| DWORD GetFunctionAddress( HMODULE phModule,char* pProcName ) { if (!phModule) return 0; PIMAGE_DOS_HEADER pimDH = (PIMAGE_DOS_HEADER)phModule; PIMAGE_NT_HEADERS pimNH = (PIMAGE_NT_HEADERS)((char*)phModule+pimDH->e_lfanew); PIMAGE_EXPORT_DIRECTORY pimED = (PIMAGE_EXPORT_DIRECTORY)((DWORD)phModule+pimNH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); DWORD pExportSize = pimNH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; DWORD pResult = 0; if ((DWORD)pProcName < 0x10000) { if ((DWORD)pProcName >= pimED->NumberOfFunctions+pimED->Base || (DWORD)pProcName < pimED->Base) return 0; pResult = (DWORD)phModule+((DWORD*)((DWORD)phModule+pimED->AddressOfFunctions))[(DWORD)pProcName-pimED->Base]; }else { DWORD* pAddressOfNames = (DWORD*)((DWORD)phModule+pimED->AddressOfNames); for (int i=0;i<pimED->NumberOfNames;i++) { char* pExportName = (char*)(pAddressOfNames[i]+(DWORD)phModule); if (strcmp(pProcName,pExportName) == 0) { WORD* pAddressOfNameOrdinals = (WORD*)((DWORD)phModule+pimED->AddressOfNameOrdinals); pResult = (DWORD)phModule+((DWORD*)((DWORD)phModule+pimED->AddressOfFunctions))[pAddressOfNameOrdinals[i]]; break; } } } if (pResult != 0 && pResult >= (DWORD)pimED && pResult < (DWORD)pimED+pExportSize) { char* pDirectStr = (char*)pResult; bool pstrok = false; while (*pDirectStr) { if (*pDirectStr == '.') { pstrok = true; break; } pDirectStr++; } if (!pstrok) return 0; char pdllname[MAX_PATH]; int pnamelen = pDirectStr-(char*)pResult; if (pnamelen <= 0) return 0; memcpy(pdllname,(char*)pResult,pnamelen); pdllname[pnamelen] = 0; HMODULE phexmodule = GetModuleHandle(pdllname); pResult = GetFunctionAddress(phexmodule,pDirectStr+1); } return pResult; } |
来自winroot大牛得 对付API-splicing的一种简单方法 [PSI_H]得文章中给出了一种方法
相关代码引用如下
引用:
| 首先脑子里想到的是,使用LoadLibrary/GetProcAddress函数来获取被拦截函数的原始代码,之后用它在内存里替换掉以前的代码,这 样就摘掉了对函数的HOOK。因为调用LoadLibrary将返回指向已加载模块的指针,所以必须将文件拷贝并加载此拷贝。下面的代码去除了对 ZwWriteVirtualMemory函数的拦截: 1. // 将NTDLL.DLL文件拷入TEMP文件夹 2. 3. char szTemp[MAX_PATH]; 4. 5. GetTempPath(MAX_PATH, szTemp); 6. 7. strcat(szTemp, "ntdll2.dll"); 8. 9. CopyFile("C:\\Windows\\System32\\ntdll.dll", szTemp, TRUE); 10. 11. // 取得指向原始函数的指针 12. HMODULE hMod = LoadLibrary(szTemp); 13. void* ptr_orig = GetProcAddress(hMod, "ZwWriteVirtualMemory"); 14. 15. // 取得指向当前函数的指针 16. void* ptr_new = GetProcAddress (LoadLibrary("ntdll.dll"), "ZwWriteVirtualMemory"); 17. 18. // 设置内存访问权限 19. DWORD dwOldProtect; 20. VirtualProtect(ptr_new, 10, PAGE_EXECUTE_READWRITE, &dwOldProtect); 21. 22. // 替换函数的前10个(为保险起见)字节 23. memcpy(ptr_new, ptr_orig, 10); 24. 25. FreeLibrary(hMod); 26. DeleteFile(szTemp); 尽管这里给出的摘除HOOK的方法完全奏效,但需要加载新的dll模块,这可能会引起防火墙的暴怒。我所认为的更为优雅的办法就是只需从文件中读取所需要的字节。下面这个函数的代码恢复了API的原始的起始部分。 1. bool RemoveFWHook(char* szDllPath, char* szFuncName) // szDllPath为DLL的完整路径 ! 2. { 3. // 取得指向函数的指针 4. HMODULE lpBase = LoadLibrary(szDllPath); 5. LPVOID lpFunc = GetProcAddress(lpBase, szFuncName); 6. if(!lpFunc) 7. return false; 8. // 取得RVA 9. DWORD dwRVA = (DWORD)lpFunc-(DWORD)lpBase; 10. 11. // 将文件映射入内存 12. HANDLE hFile = CreateFile(szDllPath,GENERIC_READ, FILE_SHARE_READ, 13. NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL); 14. if(INVALID_HANDLE_VALUE == hFile) 15. return false; 16. 17. DWORD dwSize = GetFileSize(hFile, NULL); 18. 19. HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY|SEC_IMAGE, 0, dwSize, NULL); 20. 21. LPVOID lpBaseMap = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwSize); 22. 23. // 指向当前函数的指针 24. LPVOID lpRealFunc = (LPVOID)((DWORD)lpBaseMap+dwRVA); 25. 26. // 修改访问权限并拷贝 27. DWORD dwOldProtect; 28. BOOL bRes=true; 29. if(VirtualProtect(lpFunc, 10, PAGE_EXECUTE_READWRITE, &dwOldProtect)) 30. { 31. memcpy(lpFunc, lpRealFunc, 10); 32. }else{ 33. bRes=false; 34. } 35. 36. UnmapViewOfFile(lpBaseMap); 37. 38. CloseHandle(hMapFile); 39. CloseHandle(hFile); 40. 41. return bRes; 42. 43. } |
看代码 由于偶更加熟悉C代码一点。这里用得是海风月影得Hook api lib 0.2 for C
引用:
| BYTE BeforeStub[94] ={ 0x58, // 0 pop eax 0xEB, 0x08, // 1 jmp short 0040100B 0x00, 0x00, 0x00, 0x00, // 3 dd 00000000 0x00, 0x00, 0x00, 0x00, // 7 dd 00000000 0xE8, 0x00, 0x00, 0x00, 0x00, // 11 call 00401010 0x59, // 16 pop ecx 0x81, 0xE9, 0x10, 0x10, 0x40, 0x00, // 17 sub ecx, 00401010 0x89, 0xA1, 0x03, 0x10, 0x40, 0x00, // 23 mov [ecx+401003], esp 0x89, 0x81, 0x07, 0x10, 0x40, 0x00, // 29 mov [ecx+401007], eax 0xE8, 0x36, 0x01, 0x00, 0x00, // 35 call HookProc 这里动态改变地址 0x8B, 0x44, 0x24, 0xFC, // 40 mov eax, [esp - 4] 0xE8, 0x00, 0x00, 0x00, 0x00, // 44 call 0040102D 0x59, // 49 pop ecx 0x89, 0x44, 0x24, 0xFC, // 50 mov [esp - 4], eax 0x81, 0xE9, 0x31, 0x10, 0x40, 0x00, // 54 sub ecx, 0040102D 0x8B, 0xA1, 0x03, 0x10, 0x40, 0x00, // 60 mov esp, [ecx+401003] 0x8B, 0x81, 0x07, 0x10, 0x40, 0x00, // 66 mov eax, [ecx+401007] 0x50, // 72 push eax 0x90, 0x90, 0x90, 0x90, // 73 保存入口处代码 16字节 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xE9, 0x18, 0x01, 0x00, 0x00 // 89 jmp HookedApi }; |
参考文献:
西裤哥的 Hook api lib 0.2 for C by 海风月影 http://www.unpack.cn/viewthread.php?tid=11189
又一次写GetProcAddress函数 by lifeengines http://www.unpack.cn/thread-14189-1-1.html
