我正在尝试学习PE格式以及PE加载器的工作原理,我以这个存储库为例,https://github.com/TheD1rkMtr/FilelessPELoader。
FilelessPELoader不支持重定位。现在,如果 PE 加载器可以在 PE 的 ImageBase 上分配可执行内存,则可以加载并执行 PE 文件,否则将会失败。
preferAddr = (LPVOID)ntHeader->OptionalHeader.ImageBase;
...
pImageBase = (BYTE*)VirtualAlloc(
preferAddr,
ntHeader->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
所以我尽了最大努力自己实现这个功能,但不幸的是我没有得到想要的结果。这是我对
PELoader()
函数的更改,
void pe_loader(char* data, DWORD datasize) {
masquerade_cmdline();
DWORD chksum = 0;
for (DWORD i = 0; i < datasize; i++) {
chksum = data[i] * i + chksum / 3;
};
BYTE* pImageBase = NULL;
LPVOID preferAddr = 0;
DWORD OldProtect = 0;
IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)get_nt_header(data);
if (!ntHeader) {
exit(0);
}
IMAGE_DATA_DIRECTORY* relocDir = get_pe_directory(data, IMAGE_DIRECTORY_ENTRY_BASERELOC);
preferAddr = (LPVOID)ntHeader->OptionalHeader.ImageBase;
HMODULE dll = LoadLibraryA("ntdll.dll");
NtUnmapViewOfSectionProc NtUnmapViewOfSection = (NtUnmapViewOfSectionProc)
GetProcAddress(dll, "NtUnmapViewOfSection");
NtUnmapViewOfSection((HANDLE)-1, (LPVOID)ntHeader->OptionalHeader.ImageBase);
//pImageBase = (BYTE*)VirtualAlloc(
// preferAddr,
// ntHeader->OptionalHeader.SizeOfImage,
// MEM_COMMIT | MEM_RESERVE,
// PAGE_EXECUTE_READWRITE);
//if (!pImageBase) {
// if (!relocDir) {
// exit(0);
// }
// else {
// pImageBase = (BYTE*)VirtualAlloc(
// NULL,
// ntHeader->OptionalHeader.SizeOfImage,
// MEM_COMMIT | MEM_RESERVE,
// PAGE_EXECUTE_READWRITE);
// if (!pImageBase)
// {
// exit(0);
// }
// }
//}
pImageBase = (BYTE*)VirtualAlloc(NULL,
ntHeader->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (!pImageBase)
{
exit(0);
}
// Copy the PE file in allocated memory
ntHeader->OptionalHeader.ImageBase = (size_t)pImageBase;
memcpy(pImageBase, data, ntHeader->OptionalHeader.SizeOfHeaders);
LPVOID lpRelocHdr = NULL;
IMAGE_SECTION_HEADER* SectionHeaderArr =
(IMAGE_SECTION_HEADER*)(size_t(ntHeader) + sizeof(IMAGE_NT_HEADERS));
for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
{
if (strcmp((char*)SectionHeaderArr[i].Name, ".reloc") == 0)
{
//lpImageRelocSection = (PIMAGE_SECTION_HEADER)SectionHeaderArr[i].VirtualAddress;
lpRelocHdr = LPVOID(size_t(pImageBase) + SectionHeaderArr[i].VirtualAddress);
}
memcpy(LPVOID(size_t(pImageBase) + SectionHeaderArr[i].VirtualAddress),
LPVOID(size_t(data) + SectionHeaderArr[i].PointerToRawData),
SectionHeaderArr[i].SizeOfRawData);
}
printf("Executable memory : %p\n", pImageBase);
printf("RW memory : %p\n", data);
DWORD DeltaImageBase = (DWORD)pImageBase - ntHeader->OptionalHeader.ImageBase;
IMAGE_BASE_RELOCATION* ImageBaseReloc = (IMAGE_BASE_RELOCATION*)lpRelocHdr;
cout << hex << ImageBaseReloc << endl;
while (ImageBaseReloc->VirtualAddress != 0)
{
DWORD RelocEntries = (ImageBaseReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
cout << "Virtual Address : " << hex << ImageBaseReloc->VirtualAddress << endl;
cout << "Relocation Entries : " << hex << RelocEntries << endl;
cout << "Size of Relocation Header : " << hex << (ImageBaseReloc->SizeOfBlock) << endl;
cout << hex << ImageBaseReloc << endl;
WORD* ImageRelocEntry = (WORD*)((char*)ImageBaseReloc + sizeof(IMAGE_BASE_RELOCATION));
for (int i = 0; i < RelocEntries; i++)
{
//type is the first 4 bits of the relocation word
int type = ImageRelocEntry[i] >> 12;
// offset is the last 12 bits
int offset = ImageRelocEntry[i] & 0x0fff;
cout << type << "\t" << offset << endl;
if (type == 0)
continue;
DWORD* ChangeAddr = (DWORD*)(pImageBase + ImageBaseReloc->VirtualAddress + offset);
*ChangeAddr = DeltaImageBase;
//memcpy(ChangeAddr, &DeltaImageBase, sizeof(DWORD));
}
ImageBaseReloc = (IMAGE_BASE_RELOCATION*)(((char*)ImageBaseReloc + ImageBaseReloc->SizeOfBlock));
}
// Fix the PE Import addr table
repair_iat(pImageBase);
// AddressOfEntryPoint
size_t ret_addr = (size_t)(pImageBase)+ntHeader->OptionalHeader.AddressOfEntryPoint;
// Jumping to the EntryPoint of the loaded PE
std::cout << " - Jumping..." << std::endl;
//
EnumThreadWindows(0, (WNDENUMPROC)ret_addr, 0);
return;
}
我修改前的原函数代码可以在这里
找到非常感谢您帮助告诉我我做错了什么以及如何修复代码?
另外,在解析重定位表和条目时,我还需要修复导入地址表,对吗?这是两个不同的事情?我也不太确定那部分。
谢谢
我又回到这个问题了…… 在网上寻找一些线索时,我发现了以下 YouTube 视频在此处输入链接描述。它帮助我了解我应该做什么以及我做错了什么。
所以我最终得到的工作代码是,
void pe_loader(char* data, DWORD datasize) {
masquerade_cmdline();
DWORD chksum = 0;
for (DWORD i = 0; i < datasize; i++) {
chksum = data[i] * i + chksum / 3;
};
BYTE* pImageBase = NULL;
LPVOID preferAddr = 0;
DWORD OldProtect = 0;
IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)get_nt_header(data);
if (!ntHeader) {
exit(0);
}
IMAGE_DATA_DIRECTORY* relocDir = get_pe_directory(data, IMAGE_DIRECTORY_ENTRY_BASERELOC);
preferAddr = (LPVOID)ntHeader->OptionalHeader.ImageBase;
pImageBase = (BYTE*)VirtualAlloc(NULL,
ntHeader->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (!pImageBase)
{
exit(0);
}
// Copy the PE file in allocated memory
memcpy(pImageBase, data, ntHeader->OptionalHeader.SizeOfHeaders);
LPVOID lpRelocHdr = NULL;
IMAGE_SECTION_HEADER* SectionHeaderArr =
(IMAGE_SECTION_HEADER*)(size_t(ntHeader) + sizeof(IMAGE_NT_HEADERS));
for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
{
if (strcmp((char*)SectionHeaderArr[i].Name, ".reloc") == 0)
{
//lpImageRelocSection = (PIMAGE_SECTION_HEADER)SectionHeaderArr[i].VirtualAddress;
lpRelocHdr = LPVOID(size_t(pImageBase) + SectionHeaderArr[i].VirtualAddress);
}
memcpy(LPVOID(size_t(pImageBase) + SectionHeaderArr[i].VirtualAddress),
LPVOID(size_t(data) + SectionHeaderArr[i].PointerToRawData),
SectionHeaderArr[i].SizeOfRawData);
}
printf("Executable memory : %p\n", pImageBase);
printf("RW memory : %p\n", data);
DWORD DeltaImageBase = (DWORD)pImageBase - ntHeader->OptionalHeader.ImageBase;
IMAGE_BASE_RELOCATION* ImageBaseReloc = (IMAGE_BASE_RELOCATION*)lpRelocHdr;
cout << hex << ImageBaseReloc << endl;
while (ImageBaseReloc->VirtualAddress != 0)
{
DWORD RelocEntries = (ImageBaseReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
cout << "Virtual Address : " << hex << ImageBaseReloc->VirtualAddress << endl;
cout << "Relocation Entries : " << hex << RelocEntries << endl;
cout << "Size of Relocation Header : " << hex << (ImageBaseReloc->SizeOfBlock) << endl;
cout << hex << ImageBaseReloc << endl;
WORD* ImageRelocEntry = (WORD*)((char*)ImageBaseReloc + sizeof(IMAGE_BASE_RELOCATION));
for (int i = 0; i < RelocEntries; i++)
{
//type is the first 4 bits of the relocation word
int type = ImageRelocEntry[i] >> 12;
// offset is the last 12 bits
int offset = ImageRelocEntry[i] & 0x0fff;
cout << type << "\t" << offset << endl;
if (type == 0)
continue;
DWORD* ChangeAddr = (DWORD*)(pImageBase + ImageBaseReloc->VirtualAddress + offset);
*ChangeAddr += DeltaImageBase;
}
ImageBaseReloc = (IMAGE_BASE_RELOCATION*)(((char*)ImageBaseReloc + ImageBaseReloc->SizeOfBlock));
}
// Fix the PE Import addr table
repair_iat(pImageBase);
// AddressOfEntryPoint
size_t ret_addr = (size_t)(pImageBase)+ntHeader->OptionalHeader.AddressOfEntryPoint;
// Jumping to the EntryPoint of the loaded PE
std::cout << " - Jumping..." << std::endl;
//
EnumThreadWindows(0, (WNDENUMPROC)ret_addr, 0);
return;
}
我最初的代码有不止一个问题,指针算术、设置
ntHeader->OptionalHeader.ImageBase = (size_t)pImageBase;
最后,重定位表上的每个条目都指向基于基地址的地址,我们需要将增量添加到当前值。
感谢该视频的创作者。