【问题标题】:Why PE need Original First Thunk(OFT)?为什么 PE 需要 Original First Think(OF)?
【发布时间】:2017-07-13 19:51:27
【问题描述】:
有“First Thunk”(FT),加载器在执行后用正确的地址覆盖。
但是当 PE 使用 OFT 时呢?
PE 甚至需要它吗?
【问题讨论】:
标签:
windows
assembly
exe
reverse-engineering
portable-executable
【解决方案1】:
如果导入已绑定但导入的 .DLL 不匹配,则需要原始的第一个 thunk。
在未打补丁的全新 Windows 版本上,基础 .DLL(ntdll、kernel32、user32 等)中所有函数的所有地址都是已知的。以shell32为例,它链接到kernel32!CreateProcess,CreateProcess的真实地址可以直接存储在shell32中。这称为import binding,让加载程序跳过查找导入函数的所有地址的步骤。
如果导入的 .DLL 未在其首选地址加载或 .DLL 已更改(安全更新等),则此方法不起作用。如果发生这种情况,则加载程序必须“以正常方式”查找函数,并且必须使用原始的第一个 thunk 数组,因为这是存储函数名称的 RVAs 的唯一位置。
如果不使用导入绑定,则原始的第一个 thunk 数组是可选的,可能不存在。
ASLR 可能使这种优化变得无关紧要。
【解决方案2】:
让我在这里为您总结很多事情。当你加载一个Library,例如Milad.dll,然后尝试从中调用MPrint之类的函数时,windows操作系统的动态加载器必须解析MPrint函数的地址,然后调用它。操作系统如何解析该函数的地址?
Windows 经历了一些非常复杂的事情,我想用简单的语言告诉你这些步骤。 windows操作系统的动态加载器解析DLL中函数的地址必须检查导入名称表(INT)、导入序号表(IOT)和导入地址表(IAT)表。这些表由AddressOfNames、AddressOfNamesOrdinal 和AddressOfFunction 成员在Export 目录中指向一个PE 结构。
OS 在 LoadLibrary 的帮助下将 Milad.dll 加载到目标进程的地址空间后,它将使用 GetProcAddress 在进程的目标地址空间中填充 INT、IOT 和 IAT 表并进行一些计算。
流程结构中有一个Import Directory数组,有OriginalFirstThunk、TimeDateStamp、ForwarderChain、Name、FirstThunk这些成员指向一些重要的地址。
-
导入目录 (Image_Import_Data) 中的 Name 指向的名称
进程试图调用的 DLL,在这个例子中这个 DLL 是
Milad.dll。
-
OriginalFirstThunk 指向包含名称的导入名称表
Milad.Dll 导出的函数。此表中的函数
有一个唯一的索引,加载器获取该索引并转到下一个
步骤和引用具有该索引的 Import Ordinal Table 并采用
Import Ordinal Table 的索引中的值
这是另一个整数值。
-
FirstThunk 是另一个指向 IAT 的重要成员。在里面
上一步动态加载器通过 IOT 获取一个整数值。这
value 是一个索引号,动态加载器使用该值引用 IAT。
在这个表中,索引值中有一个地址是动态的
loader 从 INT-IOT 获取。经过这些步骤当动态加载器
找出函数的正确地址,它把那个地址
为 MPrint 功能导入地址表。所以进程可以调用
该函数及其地址。
这是对加载程序通过 Image_Import_Data 中的 Name、OFT(INT) 和 FT(IAT) 成员解析 DLL 中函数地址的复杂内容的简单说明。
【解决方案3】:
我们需要知道,当PE文件被加载到内存时,PE加载器会查看IMAGE_THUNK_DATAs和IMAGE_IMPORT_BY_NAMEs并确定导入函数的地址。然后它将 FirstThunk 指向的数组中的 IMAGE_THUNK_DATAs 替换为函数的真实地址。因此,当 PE 文件准备好运行时。 OriginalFirstThunk 指向的 RVA 数组保持不变,因此如果需要查找导入函数的名称,PE 加载器仍然可以找到它们。