《Windows核心编程》之“API Hooking”(二)
来源:互联网 发布:常见的编程语言 编辑:程序博客网 时间:2024/06/11 09:04
前一篇主要讲“API Hooking”的原理——修改IAT,这一篇主要讲代码实现和调试。
一、修改IAT
我们要修改IAT,首先要了解导入段的数据结构和操作API。
1,IMAGE_IMPORT_DESCRIPTOR
在winnt.h文件中,定义了这么一个数据结构来描述导入段中的信息单元
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) } DUMMYUNIONNAME; DWORD TimeDateStamp; // 0 if not bound, // -1 if bound, and real date\time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) DWORD ForwarderChain; // -1 if no forwarders DWORD Name; DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)} IMAGE_IMPORT_DESCRIPTOR;typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
其中,“Name”表示的是DLL名,如:kernel32.dll。“FirstThunk”指向IAT的第一个项。我们只需要了解这两个成员即可。
如上图,INT是指“Import Name Table”,IAT是指“Import Address Table”。新系统中,一般用IAT即可,老的用INT。
2,ImageDirectoryEntryToDataEx或ImageDirectoryEntryToData
Microsoft提供了这么一个API来获取Image文件的导入段的信息单位。
Locates a directory entry within the image header and returns the address of the data for the directory entry. This function returns the section header for the data located, if one exists.
Syntax
PVOID WINAPI ImageDirectoryEntryToDataEx( _In_ PVOID Base, _In_ BOOLEAN MappedAsImage, _In_ USHORT DirectoryEntry, _Out_ PULONG Size, _Out_opt_ PIMAGE_SECTION_HEADER *FoundHeader);
3,ReplaceIATEntryInOneMod
Jeffrey提供了一个函数,封装了修改一个模块的某个导出函数的功能。
void CAPIHook::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) { // Get the address of the module's import section ULONG ulSize; // An exception was triggered by Explorer (when browsing the content of // a folder) into imagehlp.dll. It looks like one module was unloaded... // Maybe some threading problem: the list of modules from Toolhelp might // not be accurate if FreeLibrary is called during the enumeration. PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL; __try { pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData( hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize); } __except (InvalidReadExceptionFilter(GetExceptionInformation())) { // Nothing to do in here, thread continues to run normally // with NULL for pImportDesc } if (pImportDesc == NULL) return; // This module has no import section or is no longer loaded // Find the import descriptor containing references to callee's functions for (; pImportDesc->Name; pImportDesc++) { PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name); if (lstrcmpiA(pszModName, pszCalleeModName) == 0) { // Get caller's import address table (IAT) for the callee's functions PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) ((PBYTE) hmodCaller + pImportDesc->FirstThunk); // Replace current function address with new function address for (; pThunk->u1.Function; pThunk++) { // Get the address of the function address PROC* ppfn = (PROC*) &pThunk->u1.Function; // Is this the function we're looking for? BOOL bFound = (*ppfn == pfnCurrent); if (bFound) { if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError())) { DWORD dwOldProtect; if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_WRITECOPY, &dwOldProtect)) { WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, sizeof(pfnNew), NULL); VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect, &dwOldProtect); } } return; // We did it, get out } } } // Each import section is parsed until the right entry is found and patched }}
1)它首先获得导入段的头指针,再通过“pImportDesc++”指针移动来遍历导入段。由此可知,导入段中保存的各模块信息是顺序排列(数组),并用IMAGE_IMPORT_DESCRIPTOR结构体封装好的。
2)找到对应的DLL名后,再匹配函数地址。
3)通过WriteProcessMemory修改IAT。如果修改失败,可以试着调整“页保护”属性。
注:上述函数中,在两个for循环内部,都要加一个“hmodCaller”,这个是EXE文件的基地址。
关于PE文件和导入段操作可以参考网文:
https://msdn.microsoft.com/en-us/library/ms809762.aspx
http://stackoverflow.com/questions/15960437/how-to-read-import-directory-table-in-c
二、LastMessageBox示例的代码亮点
1,获取DLL句柄
在“Windows Hook”博文中,我们知道了利用DllMain函数的参数来获取DLL的句柄。而在LastMessageBoxLib中,作者又向我们展示了一个新的方法:
// Returns the HMODULE that contains the specified memory addressstatic HMODULE ModuleFromAddress(PVOID pv) { MEMORY_BASIC_INFORMATION mbi; return((VirtualQuery(pv, &mbi, sizeof(mbi)) != 0) ? (HMODULE) mbi.AllocationBase : NULL);}
2,进程间通信
// Send the string to the main dialog box COPYDATASTRUCT cds = { 0, ((DWORD)wcslen(sz) + 1) * sizeof(wchar_t), sz }; FORWARD_WM_COPYDATA(FindWindow(NULL, TEXT("Last MessageBox Info")), NULL, &cds, SendMessage);
作者利用SendMessage发送WM_COPYDATA类型的消息,实现进程间通信。
参考:http://blog.csdn.net/morewindows/article/details/6804157
三、调试问题
一开始,我按照《Windows核心编程》的方法,先用VS管理员模式运行LastMessageBox,出现等待对话框,然后再双机运行(普通模式)"DelayLoadApp.exe",并没有受到任何消息。我通过在LastMessageBoxLib的相关函数中添加Log打印的方式,发现Windows Hook和API Hooking都设置成功,但是SendMessage时失败了。(我把宏FORWARD_WM_COPYDATA展开了),通过GetLastError获得错误码为5。查看MSDN关于SendMessage的文档,有如下一句话:
When a message is blocked by UIPI the last error, retrieved with GetLastError, is set to 5 (access denied).
然后,我再搜UIPI,获知:https://blogs.msdn.microsoft.com/vishalsi/2006/11/30/what-is-user-interface-privilege-isolation-uipi-on-vista/UIPI - User Interface Priviledge Isolation,UI特权隔离。它是指Windows两个进程之间通过SendMessage发信息,系统会拦截低等级的进程向高等级的进程发的消息。
综上,我将LastMessageBox.exe和DelayLoadApp.exe都改为普通模式运行,实验成功。
四、Detours库
Microsoft提供了一个detours库,封装了API Hooking的相关功能,用户可以直接用,省去不少代码编写。
原库地址:
https://www.microsoft.com/en-us/research/project/detours/
https://www.microsoft.com/en-us/download/details.aspx?id=52586
相关介绍博文:
http://blog.csdn.net/evi10r/article/details/6659354
http://blog.csdn.net/oneVs1/article/details/4704767
http://www.cnblogs.com/dayw/p/3289443.html
- 《Windows核心编程》之“API Hooking”(二)
- 《Windows核心编程》之“API Hooking”(一)
- Windows核心编程——》第二十二章 DLL 注入和API钩 (DLL Injection and API Hooking)
- [DOC]Hooking Windows api
- Windows核心编程(二十二)API拦截
- 《Windows核心编程》之“Windows挂钩”(二)
- windows核心编程之API拦截
- 插入DLL和挂接API——Windows核心编程学习手札之二十二
- 《Windows核心编程》之”DLL注入“(二)
- WINDOWS核心编程之进程(二)
- WINDOWS核心编程之进程(二)
- 《Windows核心编程》读书笔记(二)
- 《windows核心编程》笔记(二)
- Windows核心编程:(二)进程
- EasyHook - The reinvention of Windows API Hooking
- windows下的api hooking技术
- Windows核心编程笔记(二)
- windows核心编程(笔记二)
- 顺序锁(seqlock)
- 第一个jsp文档,hello world
- MySQL实例汇集
- bzoj3172: [Tjoi2013]单词
- android实现断点续传
- 《Windows核心编程》之“API Hooking”(二)
- 连通分量个数(dfs)
- 【JAVA WEB】学习笔记——Servlet入门
- 数据结构实验之查找七:线性之哈希表
- ISC大会的几点遗憾
- 百度地图定位定位失败得解决
- python+lxml解析大XML文件(100M+)
- BGP协议测试套开发及使用指导书
- VM12 安装 OS X 10.11