取PE文件的引入表和导出表

时间:2024-06-21 20:07:26

直接上代码(这里列出C++和Delphi的代码),Delphi代码中包含导入及导出文件和函数列表,PE结构可参阅资料,很多很详细,需要注意的是,本例中是映射到内存,不是通过PE装载器装入的,所以对于节的RVA地址需要转换成为文件偏移地址。

Delphi代码

  1. unit Unit1;
  2. interface
  3. uses
  4. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5. Dialogs, StdCtrls, ComCtrls;
  6. type
  7. //导入表元素结构
  8. TImageImportDiscriptor = packed record
  9. OriginalFirstThunk: DWORD;
  10. DataTimpStamp: DWORD;
  11. ForwardChain: DWORD;
  12. DLLName: DWORD;
  13. FirstThunk: DWORD;
  14. end;
  15. PImageImportDiscriptor = ^TImageImportDiscriptor;
  16. //导出表元素结构
  17. PImageExportDirectory = ^TImageExportDirectory;
  18. TImageExportDirectory = packed record
  19. Characteristics: DWORD;
  20. TimeDateStamp: DWORD;
  21. MajorVersion: WORD;
  22. MinorVersion: WORD;
  23. Name: DWORD;
  24. Base: DWORD;
  25. NumberOfFunctions: DWORD;
  26. NumberOfNames: DWORD;
  27. AddressOfFunctions: DWORD;
  28. AddressOfNames: DWORD;
  29. AddressOfNameOrdinals: DWORD;
  30. end;
  31. //函数名结构
  32. TImportByName = packed record
  33. proHint: Word;
  34. proName: array [0..1] of char;
  35. end;
  36. PImportByName = ^TImportByName;
  37. TForm1 = class(TForm)
  38. OpenDialog1: TOpenDialog;
  39. Button1: TButton;
  40. TreeView1: TTreeView;
  41. Label1: TLabel;
  42. procedure Button1Click(Sender: TObject);
  43. private
  44. { Private declarations }
  45. procedure GetList(filename:string);
  46. {导入列表}
  47. procedure GetImportList(pBaseAddress:Pointer;ntHeader:PImageNtHeaders);
  48. {导出列表}
  49. procedure GetExportList(pBaseAddress:Pointer;ntHeader:PImageNtHeaders);
  50. public
  51. { Public declarations }
  52. end;
  53. var
  54. Form1: TForm1;
  55. implementation
  56. {$R *.dfm}
  57. { TForm1 }
  58. procedure TForm1.GetList(filename: string);
  59. var
  60. fileHandle:THandle;
  61. fileMap:THandle;
  62. pBaseAddress:Pointer;
  63. dosHeader: PImageDosHeader;
  64. ntHeader: PImageNtHeaders;
  65. begin
  66. TreeView1.Items.Clear;
  67. try
  68. //打开文件
  69. fileHandle := CreateFile(PChar(filename),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  70. if fileHandle = INVALID_HANDLE_VALUE then
  71. begin
  72. ShowMessage('文件打开失败!');
  73. Exit;
  74. end;
  75. //创建内存映射
  76. fileMap := CreateFileMapping(fileHandle,nil,PAGE_READONLY,0,0,nil);
  77. if fileMap = 0 then
  78. begin
  79. ShowMessage('创建内存映射失败!');
  80. Exit;
  81. end;
  82. //映射到当前进程,pBaseAddress是基址
  83. pBaseAddress := MapViewOfFile(fileMap,FILE_MAP_READ,0,0,0);
  84. if pBaseAddress = nil then
  85. begin
  86. ShowMessage('获取地址失败!');
  87. Exit;
  88. end;
  89. //获取Dos信息头部结构数据
  90. dosHeader := pImageDosHeader(LongInt(pBaseAddress));
  91. //判断Dos标识
  92. if dosHeader.e_magic <> IMAGE_DOS_SIGNATURE then
  93. begin
  94. ShowMessage('不可识别的文件格式!');
  95. Exit;
  96. end;
  97. //获取NT信息头部结构数据,IsBadReadPtr判断指针是否可读,ntHeader.Signature是NT标识
  98. ntHeader := pImageNtHeaders(LongInt(pBaseAddress)+dosHeader._lfanew);
  99. if (IsBadReadPtr(ntHeader,SizeOf(TImageNtHeaders))) or
  100. (ntHeader.Signature <> IMAGE_NT_SIGNATURE) then
  101. begin
  102. ShowMessage('不是有效地Win32程序!');
  103. Exit;
  104. end;
  105. GetImportList(pBaseAddress,ntHeader);
  106. GetExportList(pBaseAddress,ntHeader);
  107. finally
  108. UnmapViewOfFile(pBaseAddress);
  109. CloseHandle(fileMap);
  110. CloseHandle(fileHandle);
  111. end;
  112. end;
  113. procedure TForm1.Button1Click(Sender: TObject);
  114. begin
  115. if OpenDialog1.Execute then
  116. begin
  117. Label1.Caption := '以下是'+OpenDialog1.FileName+'的导入及导出表';
  118. GetList(OpenDialog1.FileName);
  119. end;
  120. end;
  121. procedure TForm1.GetExportList(pBaseAddress: Pointer; ntHeader: PImageNtHeaders);
  122. var
  123. imageEntry: PImageExportDirectory;
  124. sectionHeader: PImageSectionHeader;
  125. importbyname: PImportByName;
  126. proEntry:PDWORD;
  127. proTemp:PWORD;
  128. rva,frva: DWORD;
  129. dllname: string;
  130. i,j:integer;
  131. node:TTreeNode;
  132. s:string;
  133. pname:PChar;
  134. begin
  135. rva := ntHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
  136. if rva = 0 then Exit;
  137. //定位到第一个节的地址
  138. sectionHeader := PImageSectionHeader(LongInt(ntHeader)+SizeOf(TImageNtHeaders));
  139. //ntHeader^.FileHeader.NumberOfSections为节的数量,此处循环,找到引入表的节
  140. for i := 0 to ntHeader^.FileHeader.NumberOfSections - 1 do
  141. begin
  142. //IMAGE_DIRECTORY_ENTRY_IMPORT,引入表,检查rva是否落在节内
  143. if ( rva >= LongInt(sectionHeader.VirtualAddress)) and (rva<LongInt(sectionHeader.VirtualAddress+sectionHeader.Misc.VirtualSize)) then
  144. begin
  145. Break;
  146. end;
  147. //没找到,那么增加SizeOf(TImageSectionHeader)字节数,指向下一个节
  148. Inc(sectionHeader);
  149. end;
  150. node := TreeView1.Items.Add(nil,'导出函数表');
  151. frva := sectionHeader.VirtualAddress - sectionHeader.PointerToRawData;
  152. //导出表入口
  153. imageEntry := PImageExportDirectory(LongInt(pBaseAddress)+rva-frva);
  154. proEntry := PDWord(LongInt(pBaseAddress)+imageEntry.AddressOfFunctions-frva);
  155. pname := PChar(LongInt(pBaseAddress)+imageEntry.Name-frva);
  156. for i := 0 to imageEntry.NumberOfFunctions - 1 do
  157. begin
  158. if proEntry^ = 0 then Continue;
  159. proTemp := PWORD(LongInt(pBaseAddress)+LongInt(imageEntry.AddressOfNameOrdinals)-frva);
  160. for j := 0 to imageEntry.NumberOfNames -  1 do
  161. begin
  162. if proTemp^ = i then
  163. begin
  164. s := '';
  165. while True do
  166. begin
  167. if pname^=#0 then Break;
  168. Inc(pname);
  169. end;
  170. while True do
  171. begin
  172. if (pname-1)^=#0 then
  173. begin
  174. s:=Format('%s', [pname]);
  175. Break;
  176. end;
  177. Inc(pname);
  178. end;
  179. end;
  180. Inc(proTemp);
  181. end;
  182. TreeView1.Items.AddChild(node,s);
  183. Inc(proEntry);
  184. end;
  185. end;
  186. procedure TForm1.GetImportList(pBaseAddress: Pointer; ntHeader: PImageNtHeaders);
  187. var
  188. imageEntry: PImageImportDiscriptor;
  189. sectionHeader: PImageSectionHeader;
  190. importbyname: PImportByName;
  191. proEntry:PDWORD;
  192. rva,frva: DWORD;
  193. dllname: string;
  194. i:integer;
  195. node:TTreeNode;
  196. begin
  197. rva := ntHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
  198. if rva = 0 then Exit;
  199. //定位到第一个节的地址
  200. sectionHeader := PImageSectionHeader(LongInt(ntHeader)+SizeOf(TImageNtHeaders));
  201. //ntHeader^.FileHeader.NumberOfSections为节的数量,此处循环,找到引入表的节
  202. for i := 0 to ntHeader^.FileHeader.NumberOfSections - 1 do
  203. begin
  204. //IMAGE_DIRECTORY_ENTRY_IMPORT,引入表,检查rva是否落在节内
  205. if ( rva >= LongInt(sectionHeader.VirtualAddress)) and (rva<LongInt(sectionHeader.VirtualAddress+sectionHeader.Misc.VirtualSize)) then
  206. begin
  207. Break;
  208. end;
  209. //没找到,那么增加SizeOf(TImageSectionHeader)字节数,指向下一个节
  210. Inc(sectionHeader);
  211. end;
  212. frva := sectionHeader.VirtualAddress - sectionHeader.PointerToRawData;
  213. TreeView1.Items.Add(nil,'导入函数表');
  214. //引入表入口
  215. imageEntry := PImageImportDiscriptor(LongInt(pBaseAddress)+rva-frva);
  216. //加载的DLL的名称,这里是RVA地址,需要转换成文件偏移地址,因为我们不是通过PE加载器加载,而是映射到内存
  217. while imageEntry.DLLName <> 0 do
  218. begin
  219. dllname := PChar(LongInt(pBaseAddress)+imageEntry.DLLName-frva);
  220. node := TreeView1.Items.AddChild(TreeView1.Items[0],dllname);
  221. if imageEntry.OriginalFirstThunk <> 0 then
  222. proEntry := PDWord(LongInt(pBaseAddress)+imageEntry.OriginalFirstThunk-frva)
  223. else
  224. proEntry := PDWord(LongInt(pBaseAddress)+imageEntry.FirstThunk-frva);
  225. while proEntry^ <> 0 do
  226. begin
  227. if (proEntry^ and $80000000) <> 0 then
  228. TreeView1.Items.AddChild(node,Format('函数编号:%-15d',[proEntry^ and $7FFFFFFF]))
  229. else
  230. begin
  231. importbyname := PImportByName(LongInt(pBaseAddress)+proEntry^-frva);
  232. TreeView1.Items.AddChild(node,Format('函数名称:%-15s',[importbyname.proName]));
  233. end;
  234. Inc(proEntry);
  235. end;
  236. //继续读取
  237. Inc(imageEntry);
  238. end;
  239. end;
  240. end.

C++代码

  1. #include<string>
  2. #include<windows.h>
  3. void GetImportDllList(void)
  4. {
  5. IMAGE_IMPORT_DESCRIPTOR* importEntry = 0;
  6. IMAGE_SECTION_HEADER* sectionHeader = 0;
  7. char* filename = "c://test.exe";
  8. //打开文件
  9. HANDLE hfile = CreateFileA(filename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  10. if (hfile == INVALID_HANDLE_VALUE)
  11. {
  12. printf("%s","文件打开失败!");
  13. return;
  14. }
  15. //创建内存映射
  16. HANDLE hmap = CreateFileMapping(hfile,NULL,PAGE_READONLY,0,0,NULL);
  17. if (hmap == 0)
  18. {
  19. printf("%s","创建内存映射失败!");
  20. return;
  21. }
  22. //映射到当前进程,pBaseAddress是基址
  23. HANDLE pBaseAddress = MapViewOfFile(hmap,FILE_MAP_READ,0,0,0);
  24. if (!pBaseAddress)
  25. {
  26. printf("%s","获取地址失败!");
  27. return;
  28. }
  29. //获取Dos信息头部结构数据
  30. IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)pBaseAddress;
  31. //判断Dos标识
  32. if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
  33. {
  34. printf("%s","不可识别的文件格式!");
  35. return;
  36. }
  37. //获取NT信息头部结构数据,IsBadReadPtr判断指针是否可读,ntHeader->Signature是NT标识
  38. IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)((DWORD)pBaseAddress+dosHeader->e_lfanew);
  39. if (ntHeader->Signature != IMAGE_NT_SIGNATURE)
  40. {
  41. printf("%s","不是有效地Win32程序!");
  42. return;
  43. }
  44. //定位到第一个节的地址
  45. sectionHeader = (IMAGE_SECTION_HEADER*)((DWORD)ntHeader+sizeof(IMAGE_NT_HEADERS));
  46. //ntHeader->FileHeader.NumberOfSections为节的数量,此处循环,找到引入表的节
  47. for (int i=0;i<ntHeader->FileHeader.NumberOfSections;i++)
  48. {
  49. //IMAGE_DIRECTORY_ENTRY_IMPORT,引入表
  50. if (sectionHeader->VirtualAddress == ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
  51. {
  52. break;
  53. }
  54. //没找到,那么增加SizeOf(IMAGE_SECTION_HEADER)字节数,指向下一个节
  55. sectionHeader++;
  56. }
  57. //引入表入口
  58. importEntry = (IMAGE_IMPORT_DESCRIPTOR*)((DWORD)pBaseAddress+sectionHeader->PointerToRawData);
  59. printf("下面是%s的引入文件/n",filename);
  60. //加载的DLL的名称,这里是RVA地址,需要转换成文件偏移地址,因为我们不是通过PE加载器加载,而是映射到内存
  61. while (importEntry->Name != 0)
  62. {
  63. DWORD offset = (DWORD)pBaseAddress+importEntry->Name-(sectionHeader->VirtualAddress - sectionHeader->PointerToRawData);
  64. char* s = (char*)offset;
  65. printf("%s/n",s);
  66. //继续读取
  67. importEntry++;
  68. }
  69. UnmapViewOfFile(pBaseAddress);
  70. CloseHandle(hmap);
  71. CloseHandle(hfile);
  72. }
  73. int main()
  74. {
  75. GetImportDllList();
  76. return 0;
  77. }

取PE文件的引入表和导出表取PE文件的引入表和导出表

http://blog.****.net/bdmh/article/details/6100745