根据PE文件格式从导入表中获取加载的DLL并遍历导入函数名称和地址

Badguy

发布日期: 2018-12-24 09:14:38 浏览量: 1303
评分:
star star star star star star star star star_border star_border
*转载请注明来自write-bug.com

背景

了解 PE 文件格式,对于做一些数据分析都是比较重要的基础。在 PE 文件格式中,理解导入表以及导出表的工作原理,又是重中之重。理解了 PE 格式的导入表,就可以修改 PE 格式进行 DLL 注入,也可以修改导入表实现 API HOOK 等。理解了 PE 格式的导出表,可以不需要 WIN32 API 函数就可以根据 DLL 加载基址定位出导出函数的名称和导出函数的地址,这在 SHELLCODE 的编写中,比较重要。

本文主要介绍导入表。给出一个 DLL 的加载基址,然后我们根据导入表获取它需要加载的 DLL 以及遍历所有导入函数名称及其函数地址。现在,我把实现过程整理成文档,分享给大家。

实现原理

我们根据 PE 文件格式结构中的导入表工作原理来进行实现,实现原理如下所示:

  • 首先,我们根据 DLL 加载基址,也就是 PE 文件结构的起始地址,从 DOS 头获取 NT 头,并根据 NT 头中的 OptionalHeader.DataDirectory 的导入表选项,获取导入表的偏移位置以及数据大小

  • 我们来到导入表的数据偏移处,获取导入的 DLL 名称偏移 Name,并显示 DLL 名称

  • 接着,根据导入表中导入函数名称的地址偏移 OriginalFirstThunk,并根据 IMAGE_IMPORT_BY_NAME 数据结构从中获取导入函数名称及其函数索引并显示。同时,也从 FirstThunk 导入函数地址列表中获取对应位置的导入函数地址并显示。继续循环获取下一个函数名称,直到遍历完毕

  • 继续获取下一个 DLL,重复上诉第 3 步,直到 DLL 获取完毕

其中,在导入表中,OriginalFirstThunk 的导入函数名称列表和 FirstThunk 导入函数地址列表一一对应。

编码实现

  1. // 遍历导入表中的DLL、导入函数及其函数地址
  2. BOOL GetProcessDllName(PVOID lpBaseAddress)
  3. {
  4. LPVOID lpFunc = NULL;
  5. // 获取导入表
  6. PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress;
  7. PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE *)pDosHeader + pDosHeader->e_lfanew);
  8. PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE *)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
  9. // 获取导入表的数据
  10. char *pszDllName = NULL;
  11. PIMAGE_THUNK_DATA pThunkData = NULL;
  12. PIMAGE_IMPORT_BY_NAME pImportByName = NULL;
  13. PIMAGE_THUNK_DATA pImportFuncAddr = NULL;
  14. while (0 != pImportTable->Name)
  15. {
  16. // 获取DLL的名称
  17. pszDllName = (char *)((BYTE *)pDosHeader + pImportTable->Name);
  18. printf("---------- DLL Name = %s ----------\n", pszDllName);
  19. // 遍历 DLL 中导入函数的名称
  20. pThunkData = (PIMAGE_THUNK_DATA)((BYTE *)pDosHeader + pImportTable->OriginalFirstThunk);
  21. pImportFuncAddr = (PIMAGE_THUNK_DATA)((BYTE *)pDosHeader + pImportTable->FirstThunk);
  22. while (TRUE)
  23. {
  24. if (0 == pThunkData->u1.AddressOfData)
  25. {
  26. break;
  27. }
  28. // 获取导入函数名称和序号
  29. pImportByName = (PIMAGE_IMPORT_BY_NAME)((BYTE *)pDosHeader + pThunkData->u1.AddressOfData);
  30. printf("[%d]\t%s\t", pImportByName->Hint, pImportByName->Name);
  31. // 获取导入函数地址
  32. printf("[0x%p]\n", (PVOID)((BYTE *)pDosHeader + pImportFuncAddr->u1.Function));
  33. // 获取下一个函数名称和地址
  34. pThunkData++;
  35. pImportFuncAddr++;
  36. }
  37. // 获取下一个DLL
  38. pImportTable++;
  39. }
  40. return TRUE;
  41. }

程序测试

我们直接运行程序,程序正确列出导入的DLL及遍历出导入函数名称和地址:

总结

这个程序不是很复杂,但是关键是要理解 PE 文件结构的导入表,要理解清楚导入表的工作原理。

参考

参考自《Windows黑客编程技术详解》一书

上传的附件 cloud_download PE_ImportTable_Test.7z ( 144.50kb, 8次下载 )

发送私信

日久不一定生情,但必定见人心

9
文章数
17
评论数
最近文章
eject