1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 《逆向工程核心原理》第13章——PE文件格式(2):IAT与EAT

《逆向工程核心原理》第13章——PE文件格式(2):IAT与EAT

时间:2023-11-04 18:47:34

相关推荐

《逆向工程核心原理》第13章——PE文件格式(2):IAT与EAT

PE文件格式(2):IAT与EAT

IATDLLIMAGE_IMPORT_DESCRIPTOR使用notepad.exe练习1 库名称(Name)2 OriginalFirstThunk-INT3 IMAGE_IMPORT_BY_NAME4 FirstThunk-IATEATIMAGE_EXPORT_DIRECTORY使用kernel32.dll练习0 练习前的小总结——PE头相关1 函数名称数组2 查找指定函数名称3 Ordinal数组4 函数地址数组—EAT5 AddAtomW函数地址Q&A

IAT

IAT:导入地址表(Import Adress Table)。简而言之IAT是一种表格,用来记录程序正在使用哪些库中的哪些函数

DLL

16位DOS时代调用函数时,会从C库中读取相应函数的二进制代码并将其插入应用程序。而Windows OS支持多任务且使用了数量庞大的库函数来支持32位的Windows环境。库包含方式低效且内存严重浪费,于是引入DLL。

不要把库包含到程序中,单独组成DLL文件,需要时调用即可内存映射技术使加载后的DLL代码、资源在多个进程中实现共享更新库时只要替换相关DLL文件即可,简单易行

加载DLL的两种方式:显示链接,程序使用DLL时加载,使用完毕后释放;隐式链接,程序开始时即一同加载,程序终止时在释放占用的内存。

IAT提供的机制与隐式链接有关。

如图,调用CreateFileW()函数时并非直接使用call 758CCC56调用,而是通过获取01001104地址处的值来实现(所有API调用均在用这种方式)。

原因在于:(1)不同环境中,kernel32.dll版本不同,CreateFileW()函数的地址也不想通过。为确保所有环境中都能正常使用该函数,编译器准备了要存放函数实际地址的位置,并仅记下call dword ptr ds:[1001104]指令。执行文件时,PE装载器只需要将CreatFileW()函数的地址写到01001104的位置。(2)DLL重定位,DLL文件的ImageBase一般位10000000,但如果该位置已经装载其他DLL文件,PE装载器就只能查找其他空白位置装载。

实际操作中无法保证DLL一定会被加载到PE头内指定的ImageBase处但是EXE文件(生成进程的主体)却可以准确加载到自身ImageBase处,因为它拥有自己的虚拟空间

IMAGE_IMPORT_DESCRIPTOR

IMAGE_IMPORT_DESCRIPTOR记录着PE文件要导入哪些库文件。其本身处于PE体中,在可选头DataDirectory[1].VirtualAddress中可以获得它的起始地址(RVA)。

程序导入多少个库,就有多少个IMAGE_IMPORT_DESCRIPTOR结构体,结构体数组以NULL结构体结束。

几个重要成员:

OriginalFirstThunk:INT的地址(RVA),INT的元素值是IMAGE_IMPORT_BY_NAME结构体指针Name:库名称字符串地址(RVA)FirstThunk:IAT地址(RVA)

INT(导入函数名称表)与IAT(导入函数地址表)的大小应相等。INT和IAT各元素一般都指向相同的地址,但也有很多情况是不一致的。

下图描述了notepad.exe值kernel32.dll的IMAGE_IMPORT_DESCRIPTOR结构。

PE装载器把导入函数输入至IAT的顺序:

读取IID的Name成员,获取库名称字符串(kernel32.dll)装载相应库:LoadLibrary(“kernel32.dll”)读取IID的OriginalFirstThunk成员,获取INT地址逐一读取INT中数组的值,获取相应的IMAGE_IMPORT_BY_NAME地址(RVA)使用IMAGE_IMPORT_BY_NAME的Hint(ordinary:库中函数的固有编号,2字节)或Name项,获取相应函数的起始地址GetProcAddress(“GetCurrentThreadld”)读取IID的FirstNameThunk(IAT)成员,获得IAT地址上面获得的函数地址输入到相应的IAT数组中。重复以上步骤4~7,直到INT结束(遇到NULL时)。

注意上述过程中获得的地址均为RVA地址,需要将其转换为RWA地址后才可在文件中查找。

使用notepad.exe练习

可选头中DataDirectory[1]=Import Directory,它的值为如下

包含DWORD VirtualAddress(RVA);DWORD Size分别为:RVA=00007604;Size=000000C8;RVA转换为文件偏移为6A04;也就是说IMAGE_IMPORT_DESCRIPTOR结构体数组位于文件偏移6A04处,大小为C8。

红色框选处了第一个结构体元素。查看该结构体成员:

可以看到:

INT地址:RVA=00007909,RAW=00006D90Name地址:RVA=00007AAC,RAW=00006EACIAT地址:RVA=000012C4,RAW=000006C4

1 库名称(Name)

库名称地址:RVA=00007AAC,RAW=00006EAC。查看6EAC处:可以看到导入函数所属库文件名称,即comdlg32.dll。

2 OriginalFirstThunk-INT

INT地址:RVA=00007909,RAW=00006D90,它指向一系列IMAGE_IMPORT_BY_NAME结构体,该结构体包含Hint(ordinary:库中函数的固有编号)和Name两个元素,每一个结构体对应一个函数信息。

跟踪数组第一个值00007A7A,进入该地址可以看到对应导入的API函数的编号与名称字符串。

3 IMAGE_IMPORT_BY_NAME

7A7A转换成文件地址为6E7A:

可以看到该函数编号Odinary为000F,名称为PageSetupDlgW。

4 FirstThunk-IAT

IAT地址:RVA=000012C4,RAW=000006C4。可以看到文件偏移06C4~06EB部分即为IAT数组区域,对应与comdlg32.dll库。每四字节对应库中一个函数的地址。

IAT的第一个元素被硬编码为76324906,该值无意义,notepad.exe文件加载到内存时,准确的地址会取代该值。

使用OD查看notepad.exe的IAT。因其ImageBase=01000000,所以comdlg32.dll的PageSetupDlgW函数的IAT地址为010012C4。

其值为9426D676,为API的准确起始值。

EAT

Windows操作系统中,“库”是为了方便其他程序调用而集中包含相关函数的文件。EAT是一种核心机制,它使不同的应用程序可以调用库文件中提供的函数。也就是说,只有通过EAT才能准确求得从相应库中导出函数的起始地址。

PE文件中的结构体IMAGE_EXPORT_DIRECTORY保存着导出信息,且仅有这一个用来说明EAT的结构体。NT:可选头中的DataDirectory[0].VirtualAddress值即为IMAGE_EXPORT_DIRECTORY结构体数组的起始地址(RVA)。

下图为kernel32.dll文件NT:可选头的DataDirectory[0]。

EXPORT_DIRECTORY的RVA:0000262C,Size:00006CFD。

对应文件偏移地址为1A2C。

IMAGE_EXPORT_DIRECTORY

IMAGE_EXPORT_DIRECTORY结构体如下:

有以下重要成员:

NumberOfFunctions:实际EXPORT函数的个数NumberOfNames:Export函数中具名函数个数AddressOfFunctions:EXPORT函数地址数组,数组元素个数=NumberOfFunctionsAddressOfNames:Export函数名称地址数组,数组元素个数=NumberOfNamesAddressOfNameOrdinalsOrdinal地址数组,数组元素个数=NumberOfNames

从库中获得函数地址的API为GetProAddress()函数,该API引用EAT来获取指定API的地址。其操作原理如下:

利用AddressOfNames成员转到“函数名称数组”“函数名称数组”中存储着字符串地址。通过比较字符串,查找指定的函数名称,获取对应数组的索引name_index利用AddressOfNameOrdinals成员,转到Ordinary数组在ordinary数组中通过name_index查找相应ordinary利用AddressOfFunctions成员转到“函数地址数组”(EAT)在“函数地址数组”中将刚刚求得的ordinal用作数组索引,获得指定函数的起始地址

使用kernel32.dll练习

0 练习前的小总结——PE头相关

注:红色数字部分表示对应关系

下面开始正式对kernel32.dll分析。

查看DOS头:可以看到NT头偏移为000000F0:

查看NT:文件头。文件共4个节区,可选头长度为00E0,属性值:210E,为dll文件。

查看NT:可选头。ImageBase=7C800000,PE文件内存中大小为00130000,PE头大小为00000400,EXPORT Directory中显示地址RVA=00002C26。

查看节区头:

由此可得各节区位置在文件和内存中的位置如下图:

kernel32.dll文件NT:可选头的DataDirectory[0].VirtualAddress值为0000262C。对应RAW值为1A2C,即IMAGE_EXPORT_DIRECTORY结构体偏移,查看结构体:

1 函数名称数组

AddressOfNames函数名称数组RVA=00003538,RWA=2938。

此处为4字节RVA组成的数组,数组个数为3B9。逐一跟踪所有的RVA即可发现函数名称字符串。

2 查找指定函数名称

要查找的韩素华名称字符串为“AddAtomW”,上图中红色框选处地址(RVA:4BB3,RAW:3FB3)即为目标字符串处。

此时“AddAtomW”函数名是函数名称数组的第三个元素,数组索引为2。

3 Ordinal数组

下面查找“AddAtomW”函数的Ordinal值。AddressOfNameOrdinals成员的值(RVA:441C,RAW:381C)

可以看到Ordinal值为2。

4 函数地址数组—EAT

最后查找函数的实际地址。AddressOfFunctions函数地址数组值为RVA:2654,RAW:1A54。下图即为4字节函数地址数组,也就是Export函数的地址。

5 AddAtomW函数地址

上图可得地址为RVA=000326D9,又kernel32.dll的ImageBase=7C800000。因此函数实际地址为:7C8326D9。

使用OD验证,如下图该地值正是要查找的AddAtomW()函数

Q&A

Q.两个notepad.exe同时运行时ImageBase都是10000000,它们不会侵占彼此的空间区域吗?

A.生成进程(加载到内存)时,OS会单独为它分配4GB大小的虚拟内存。虚拟内存与实际物理内存是不同的。同时运行两个notepad时,各进程分别在自己的独有的虚拟内存空间中,所以它们彼此不会重叠。这是由OS保障的。即使它们的ImageBase一样也完全没有问题。

Q.PE映像是什么?

A.PE映像这一术语是微软创建PE结构时开始使用的。一般是指PE文件运行时加载到内存中的形态。PE头中的SizeOfImage项指出了PE映像所占内存的大小。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。