1.创建PDE,page directory entry,用来存放virtual memory <--> physical memory之间的映射关系
2.创建virtual memory跟可执行文件(.exe,.dll...)之间的映射关系,缺页的时候需要从文件里那数据放到内存里的
3.对CPU的PC寄存器设置为可执行文件入口地址
这样就ok了,后续用到什么memory,MMU会分配/释放相应的物理内存,并填写PTE(page directory entry).
装载过程中一个重要的步骤是配置IAT(Import Address Table).
PE文件Optional Header中有两个Directory Entry特别重要,IMAGE_DIRECTORY_ENTRY_EXPORT,IMAGE_DIRECTORY_ENTRY_IMPORT.
/************************* To get the exported API's RVA *******************************/
对于Export Entry,它提供了这个module想要export的API的RVA,我们可能希望知道怎么去拿到这些API对应的RVA.
1. Get RVA of IMAGE_DIRECTORY_ENTRY_EXPORT from optional header
1 struct data_directory 2 { 3 long VirtualAddress; 4 long Size; 5 }
2. Parse the Export based on this structure
1 struct IMAGE_EXPORT_DIRECTORY { 2 DWORD Characteristics; 3 DWORD TimeDateStamp; 4 WORD MajorVersion; 5 WORD MinorVersion; 6 DWORD Name; 7 DWORD Base; 8 DWORD NumberOfFunctions; 9 DWORD NumberOfNames; 10 DWORD *AddressOfFunctions; 11 DWORD *AddressOfNames; 12 DWORD *AddressOfNameOrdinals; 13 }
3. Get NumberOfNames, AddressOfNames, AddressOfNameOrdinals, AddressOfFunctions
compare function "xxxx" with *AddressOfNames, to get its index(hint).
Based on the index(hint) to get its ordinals (AddressOfNameOrdinals[hint])
based on the ordinals to get the function's address(AddressOfFunctions[ordinals]).
/********************************************************/
对于 Import Entry,我们可能会想知道怎么去拿到Imported API的地址。
1. Get RVA of IMAGE_DIRECTORY_ENTRY_IMPORT from optional header
1 struct data_directory 2 { 3 long VirtualAddress; 4 long Size; 5 }
2. Parse the import array structure, it is an array ended by an Zero-Entry
1 struct IMAGE_IMPORT_DESCRIPTOR { 2 DWORD *OriginalFirstThunk; 3 DWORD TimeDateStamp; 4 DWORD ForwarderChain; 5 DWORD Name; // module name 6 DWORD *FirstThunk; 7 }
每个结构都会对应一个library,compare if name is the module we want.
3. 得到我们想了解的library对应的内容之后,有两个结构的内容特别重要*OriginalFirstThunk,*FirstThunk。
在loader填IAT之前,OriginalFirstThunk跟FirstThunk的内容是一样的,都是一个array ended by an Zero-Element.
每个值都对应一个结构体
1 struct IMAGE_IMPORT_BY_NAME { 2 short Hint; 3 char Name[1]; 4 }
name是函数的名字,hint是这个name在library的Export Entry中很有可能的hint值,用来优化加载时间.如果hint对应的api name跟我们想要的不一样,那么就需要二分遍历它的name array。
4. Loader根据Name,Hint去对应的Library中可以拿到这个API相应的address.
Loader会把这个address填写到FirstThunk指向的那个数组,于是,从此之后FirstThunk的内容就跟OriginalFirstThunk不一样了
5. 以后这个Module想要call相应的API的时候,就是个相对寻址, call ptr [FirstThunk]。
所以FirstThunk指向的数组最终保存了import API的virtual address.
如果想要做个hook在Module访问其他module的API时,只要在loader之后修改FirstThunk中的数据就能达到目的了:)