有时会有这样的需求,要把一个dll加载到进程空间或者对付地址空间随机化技术,这就需要自己实现一个PELoader。在PE文件中,许多数据在内存中的位置已经以RVA的形式在链接时给出,我们只需要根据节表的描述准确的按位置加载,大部分程序就可以正确运行了,但有几个类型的数据还是需要loader来调整的:
- IAT
在PE文件中,调用静态链接的DLL例程一般都是以call ds:[xxxxxxxxh]的形式,其中这个地址是指向了IAT表的对应项,而在文件中,IAT表的每一项是和INT表一样指向了一个IMAGE_IMPORT_BY_NAME或者是个Id,所以当加载到内存中时需要将表项修正为函数地址。 - 重定位表
一般加载exe文件没有重定位的问题,因为exe文件会加载到默认的基址上,而在进程地址空间初始化后,这个基址几乎肯定是未分配的,但dll或者使用了地址空间随机化技术的exe就会使用到重定位表,因此我们需要根据加载的基址和OPTIONAL_HEADER中的基址来算出偏移
注:在这篇中主要写加载dll,至于exe调试时还有一些不稳定的情况,以后再说。
文章中的ctx是自己写的一个pe解析器,等完善后会放到开源网站上。
首先,为了避开CreateSection检测,我们直接用CreateFile:
IMAGE_DOS_HEADER dos_header;
IMAGE_NT_HEADERS32 nt_header;
SetFilePointer(pe_file, 0, 0, FILE_BEGIN);
ReadFile(pe_file, &dos_header, sizeof(IMAGE_DOS_HEADER), &ret, NULL);
if (ret != sizeof(IMAGE_DOS_HEADER))
return false;
SetFilePointer(pe_file, dos_header.e_lfanew, NULL, FILE_BEGIN);
ReadFile(pe_file, &nt_header, sizeof(IMAGE_NT_HEADERS32), &ret, NULL);
if (ret != sizeof(IMAGE_NT_HEADERS32))
return false;
//在OptionalHeader中找到准确的印象大小
PVOID map_ptr = VirtualAlloc(NULL,
nt_header.OptionalHeader.SizeOfImage,
MEM_COMMIT, PAGE_READWRITE);
ZeroMemory(map_ptr, nt_header.OptionalHe