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

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

Delphi代码

[delphi] view plaincopy

  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++代码

[cpp] view plaincopy

  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. }

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

时间: 2024-12-28 00:00:32

取PE文件的引入表和导出表的相关文章

获取PE文件的输入表信息

输入表是PE文件结构中不可或缺的部分,输入表也称之为"导入表". 要想了解输入表,首先还得先从DLL文件入手.日常生活中我们会看见一些大型软件有很多的DLL格式的文件,这些文件中有很多的导入函数,这些函数不会直接被执行.当一个程序(EXE)运行时,导入函数是被程序调用执行的,其执行的代码是不在主程序(EXE)中的一小部分函数,其真正的代码却在DLL文件中.这时我们就会想,那么EXE主程序是如何找到这些需要导入的函数呢,这就要归结于“输入表”了,输入表就相当于EXE文件与DLL文件沟通的

小甲鱼PE详解之输入表(导出表)详解(PE详解09)

小甲鱼PE详解之输出表(导出表)详解(PE详解09) 当PE 文件被执行的时候,Windows 加载器将文件装入内存并将导入表(Export Table) 登记的动态链接库(一般是DLL 格式)文件一并装入地址空间,再根据DLL 文件中的函数导出信息对被执行文件的IAT 进行修正. ( 基础补充:很多朋友可能看到这里会有点懵,各位看官请允许小甲鱼啰嗦一下,照顾初学者.我们都明白Windows 在加载一个程序后就在内存中为该程序开辟一个单独的虚拟地址空间,这样的话在各个程序自己看来,自己就拥有几乎

win32下PE文件分析之节表

接上一篇的win32下PE文件分析之NT头 (一).FileBuffer与ImageBuffer (1).FileBuffer是将文件原原本本的读入申请的内存区域中,那部分区域就是FileBuffer,里面的内容与磁盘中的文件一模一样.如下图: (2).ImageBuffer是按照一定规则加载到内存中的某个区域,并且通过一定的处理,能立刻执行的区域,那部分区域叫做ImageBuffer.其大小就是可选PE头中的SizeOfImage.结构如下图: (3).二者之间的关系: ImageBuffer

深入学习PE文件(转)

PE文件是Win32的原生文件格式.每一个Win32可执行文件都遵循PE文件格式.对PE文件格式的了解可以加深你对Win32系统的深入理解. 一. 基本结构. 上图便是PE文件的基本结构.(注意:DOS MZ Header和部分PE header的大小是不变的:DOS stub部分的大小是可变的.) 一个PE文件至少需要两个Section,一个是存放代码,一个存放数据.NT上的PE文件基本上有9个预定义的Section.分别是:.text, .bss, .rdata, .data, .rsrc,

深入剖析PE文件

不赖猴的笔记,转载请注明出处. 深入剖析PE文件 PE文件是Win32的原生文件格式.每一个Win32可执行文件都遵循PE文件格式.对PE文件格式的了解可以加深你对Win32系统的深入理解. 一.        基本结构. 上图便是PE文件的基本结构.(注意:DOS MZ Header和部分PE header的大小是不变的:DOS stub部分的大小是可变的.) 一个PE文件至少需要两个Section,一个是存放代码,一个存放数据.NT上的PE文件基本上有9个预定义的Section.分别是:.t

解析PE文件

最近在自学解析PE文件,根据小辣椒(CFF Explorer)以及各论坛上大佬的帖子,做了个黑屏打印PE文件的,历时7天完成,在此想跟有相关需要的同学们分享下思路,有不足之处也希望大家不吝赐教,指点出来.谢谢. PE文件主要有DOS头,NT头(包含PE头,可选PE头,可选PE头中又包含着一个数据目录,里面包含了其他表单的RVA和大小),节表 表单主要有导出表,导入表,重定位表,资源表.除了资源表外,其他表单的各项数据都用代码实现了打印. 首先通过CreateFile打开文件,获得文件句柄,随后调

破解软件感悟-PE文件格式之Import Table(引入表)(四)

先来看一个可执行文件的实例:本例程打开一PE文件,将所有引入dll和对应的函数名读入一编辑控件,同时显示 IMAGE_IMPORT_DESCRIPTOR 结构各域值. C:\QQDownload\blah.EXE ================[ IMAGE_IMPORT_DESCRIPTOR ]============= OriginalFirstThunk  =  303C TimeDateStamp  =  0 ForwarderChain = 0 Name = KERNEL32.dll

利用PE数据目录的导入表获取函数名及其地址

PE文件是以64字节的DOS文件头开始的(IMAGE_DOS_HEADER),接着是一段小DOS程序,然后是248字节的 NT文件头(IMAGE_NT_HEADERS),NT的文件头位置由IMAGE_DOS_HEADER的e_lfanew给出! NT文件头的前4个字节是文件签名(“PE00"字符串),紧接着是20字节的IMAGE_FILE_HEADER结构,它的 后面是224字节的IMAGE_OPTIONAL_HEADER结构,而就在这个结构里,里面有模块基地址,代码和数据大 小和基地址.线程堆

PE文件基础

① PE (Portable Executable):微软参考COFF(Common Object File Format)规范,在Windows NT系统上制定的一种标准, 用于exe可执行文件.obj目标文件和dll动态链接库等文件格式.PE32+是PE的64位扩展,其并未添加额外结构,只是把原来32位的字段变成了64位. 与COFF一样,PE也是基于段(Segment,注:有时也被叫节Section)的结构, 按照不同属性将信息分段存放,常见的段有:代码段(.text).数据段(.data