PE 导出表

Posted 不会写代码的丝丽

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PE 导出表相关的知识,希望对你有一定的参考价值。

名称寻址

我们下window经常导出函数,因此在动态库我们往往有一个特殊结构叫做导出表

Microsoft官方文档说明

我们首先看一个导出模块信息

#export.def
EXPORTS
	MsgBox
	Foo
	TestFunc
	ABCDFunc

上面我们导出四个函数且是名称导出

我们的导出表地址位于NT头中数据目录的第一项。


这个导出目录的位于虚拟地址的2060h处 换算成文件地址为660h

相关的数据结构

对应上面的数值我们得出以下数据

FieldValue备注
Export Flags0
Time/Date Stamp0x62510738换算为10进制得到对应时间戳2022-04-09 12:10:32
Major Version0
Minor Version0
Name RVA20B0h名称字符串所在虚拟地址
Ordinal Base1导出函数起始序列号
Address Table Entries4证明有四个地址数组
Number of Name Pointers4证明有四个名称导出
Export Address Table RVA0x2088h导出地址表
Name Pointer RVA0x2098h导出名称表地址
Ordinal Table RVA20A8h导出名称对应序列号表地址

要明白这些字段的含义我们首先要理解动态连接器是如何完成寻址的。

我们首先讲本例中的程序三个以名称导出示例:

  1. 连接器在导出名称地址表搜索某个函数x所在下标index
  2. index带入到导出名称序号地址表中得到某个数值yy就是是导出地址表的下标
  3. 得到导出地址表下标为y的地址

假设我们想得到TetFunc的函数地址:
1 遍历名称地址表得到下标为3的位置
2 去导出序号表中得到下标为3的数值,非常凑巧也为3
3 得到导出地址表下标3的地址0x00001022

上面是名称导出的寻址过程。我们看看序号导出的情况

序号寻址

假设我们有如下模块定义

EXPORTS
	MsgBox @3
	Foo @4 NONAME
	TestFunc @7
	ABCDFunc @8 NONAME

我们通过CFF Explorer给出如下导出表信息

上面需要特别注意我们如下几个字段
NumberOfFunctions是6而我们导出的函数只有4四个,证明编译器帮我们多填充了。
Base 基础下标值3,这个会用于名称查找

再次举例名称查找TestFunc

  1. 名称表得到下标1,
  2. 名称序号表的下标1得到导出地址表下标4
  3. 导出地址表下标4的地址0x00001022

序号查找导出函数地址:
导出函数序号-Base = 导出地址表下标

举例查找导出序号4 Foo函数的地址:
4 - 3 = 1
在从导出地址表下标为1得出地址0x00001011

转发函数

我们知道模块定义中的某个函数可能存在函数转发行为
如下代码的MyMsBox

EXPORTS
	MsgBox @3
	Foo @4 
	TestFunc @7
	ABCDFunc @8 
	MyMsBox = user32.MessageBoxA


我们注意下MyMsBox 在20E8这个位置中。
同时我们的导出表的范围为2060-2104 这个范围中,所以对于转发函数来说它的函数位置在导出表范围,而非转发在别的区域(官方文档有写)

模拟GetProcAddress

.586
.model flat,stdcall
option casemap:none

   include windows.inc
   include user32.inc
   include kernel32.inc
   include msvcrt.inc
   
   
   includelib user32.lib
   includelib kernel32.lib
   includelib msvcrt.lib




.data
   g_szDllName db "DllExport",0
   g_szFuncName db "MsgBox",0

.code


MyGetProcAddress proc hDll:HINSTANCE,pName:LPCTSTR
	LOCAL @dwAddressOfNames:DWORD
	LOCAL @dwCnt:DWORD

	mov esi,hDll
	assume esi:ptr IMAGE_DOS_HEADER
	mov esi,[esi].e_lfanew
	add esi,hDll
	
	assume esi:ptr IMAGE_NT_HEADERS
	;得到导出表位置
	mov esi,[esi].OptionalHeader.DataDirectory[0].VirtualAddress
	add esi,hDll
	
	assume esi:ptr IMAGE_EXPORT_DIRECTORY
	
	.if pName > 0ffffh ;名称
		;导出名称表地址
		mov eax,[esi].AddressOfNames
		add eax,hDll
		mov @dwAddressOfNames,eax
	
		;遍历导出名称表,查找对应函数
		mov eax,[esi].NumberOfNames
		mov @dwCnt,eax
		dec @dwCnt
 		.while @dwCnt> 0
			;数组首地址
			mov ebx,@dwAddressOfNames
			mov eax,@dwCnt
			mov eax,[ebx+ eax * sizeof DWORD]
			add eax,hDll 

			;对比字符串
			invoke crt_strcmp,pName,eax
			.if eax == 0
				;int 3;
				mov eax,@dwCnt
				mov ebx,[esi].AddressOfNameOrdinals
				add ebx,hDll
				movzx eax,word ptr [ebx+eax*sizeof WORD]
				
				;获取地址
				mov ebx,[esi].AddressOfFunctions
				add ebx,hDll
				mov eax,[ebx+eax*sizeof DWORD]
				
				add eax,hDll
				ret
			.endif
		
			dec @dwCnt
		.endw
	
	.else
		;获取导出地址表中的下标索引
		
		ret	
	.endif
	

	xor eax,eax	
	ret

MyGetProcAddress endp 

; ---------------------------------------------------------------------------


start:

	invoke LoadLibrary,offset g_szDllName
	invoke MyGetProcAddress,eax,offset g_szFuncName
	call eax
	
	xor eax,eax
	invoke ExitProcess,eax





end start

以上是关于PE 导出表的主要内容,如果未能解决你的问题,请参考以下文章

如何用VBA将excel表导出成文本类型的文件?

《初识PE》导出表

PE 导出表

PE 导出表

PE 导出目录表的 OrdinalBase 字段被忽略?

从Navicat中导出数据成Excel表怎么转化成文本格式