问候溢出者,
我即将完成网络安全副学士学位。尝试从字节码形式的 dll 中手动加载导入。 PIMAGE_DATA_DIRECTORY 的虚拟地址和大小显示正确的值。解析 PIMAGE_IMPORT_DESCRIPTOR 后,这些值无法正确解析。
PIMAGE_NT_HEADERS32 ntHeader = (PIMAGE_NT_HEADERS32)((DWORD)dllFile32 + ((PIMAGE_DOS_HEADER)(DWORD)dllFile32)->e_lfanew);
PIMAGE_DATA_DIRECTORY importDir = &NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (importDir->VirtualAddress > 0 && importDir->Size > sizeof(IMAGE_IMPORT_DESCRIPTOR))
{
PIMAGE_IMPORT_DESCRIPTOR iid = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)dllFile32 + importDir->VirtualAddress);
printf(" VirtualAddress: %08X\n", importDir->VirtualAddress);
printf(" Size: %08X\n", importDir->Size);
printf("OriginalFirstThunk: %08X\n", iid->OriginalFirstThunk);
printf(" TimeDateStamp: %08X\n", iid->TimeDateStamp);
printf(" ForwarderChain: %08X\n", iid->ForwarderChain);
}
VirtualAddress: 0000507C - CORRECT
Size: 0000003C - CORRECT
OriginalFirstThunk: 00000000 - INCORRECT
TimeDateStamp: 56413F2E - INCORRECT
ForwarderChain: 74737973 - INCORRECT
最佳答案
在PE格式中,每个段的数据有两个不同的地址。第一个是文件中的文件偏移
。当文件没有映射时,即PE在磁盘上时,可以使用该地址访问文件中的节数据。第二个是RVA
(相对虚拟地址)。当操作系统将PE文件映射到内存时,应该使用这个地址。
在您的情况下,您只需将 Dll 复制到内存中的缓冲区,而不将节数据映射到指定的 RVA。因此,您无法使用 RVA
。相反,您应该使用文件偏移量。
将RVA
转换为文件偏移
的实用函数可以编写如下。
UINT RvaToFileOffset(BYTE* baseAddress, UINT rva) {
PIMAGE_NT_HEADERS lpNtHeaders = ImageNtHeader(baseAddress);
PIMAGE_FILE_HEADER lpFileHeader = &lpNtHeaders->FileHeader;
BYTE* lpOptionalHeader = &lpNtHeaders->OptionalHeader;
PIMAGE_SECTION_HEADER lpSections = (PIMAGE_SECTION_HEADER)(lpOptionalHeader + lpFileHeader->SizeOfOptionalHeader);
for (int i = 0; i < lpFileHeader->NumberOfSections; ++i)
{
IMAGE_SECTION_HEADER section = lpSections[i];
if (rva >= section.VirtualAddress && rva <= section.VirtualAddress + section.Misc.VirtualSize)
return section.PointerToRawData + rva - section.VirtualAddress;
}
return -1; // Invalid RVA
}
这是一个测试程序。
#include <stdio.h>
#include <windows.h>
#include <ImageHlp.h>
#pragma comment(lib,"imagehlp.lib")
// Helper function to be used for converting RVAs to File Offsets.
UINT RvaToFileOffset(BYTE * base, UINT rva);
int main(int argc, char* argv[]) {
PIMAGE_DOS_HEADER lpDosHeader;
PIMAGE_NT_HEADERS lpNtHeaders;
PIMAGE_OPTIONAL_HEADER lpOptionalHeader;
PIMAGE_IMPORT_DESCRIPTOR lpImportDescriptor;
if (argc != 2)
{
printf("You didn't specified a PE file.\n");
printf("Usage: ImportsParser.exe <Full path of PE File>\n");
return -1;
}
HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return -1;
HANDLE hMemoryMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (!hMemoryMap)
return -2;
PBYTE baseAddress = (PBYTE)MapViewOfFile(hMemoryMap, FILE_MAP_READ, 0, 0, 0);
if (!baseAddress)
return -3;
lpDosHeader = (PIMAGE_DOS_HEADER)baseAddress;
lpNtHeaders = (PIMAGE_NT_HEADERS)(baseAddress + lpDosHeader->e_lfanew);
lpOptionalHeader = (PIMAGE_OPTIONAL_HEADER)(&(lpNtHeaders->OptionalHeader));
PIMAGE_DATA_DIRECTORY lpImportDirectoryEntry = &lpOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
lpImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(baseAddress +
RvaToFileOffset(baseAddress, lpImportDirectoryEntry->VirtualAddress));
printf(" VirtualAddress: %08X\n", lpImportDirectoryEntry->VirtualAddress);
printf(" Size: %08X\n", lpImportDirectoryEntry->Size);
while (lpImportDescriptor->FirstThunk)
{
char* szDllName = baseAddress + RvaToFileOffset(baseAddress, lpImportDescriptor->Name);
printf("DLL Name: %s\n", szDllName);
printf("OriginalFirstThunk: %08X\n", lpImportDescriptor->OriginalFirstThunk);
printf(" TimeDateStamp: %08X\n", lpImportDescriptor->TimeDateStamp);
printf(" ForwarderChain: %08X\n", lpImportDescriptor->ForwarderChain);
printf("\n");
lpImportDescriptor++;
}
getchar();
return 0;
}
UINT RvaToFileOffset(BYTE * baseAddress, UINT rva) {
PIMAGE_NT_HEADERS lpNtHeaders = ImageNtHeader(baseAddress);
PIMAGE_FILE_HEADER lpFileHeader = &lpNtHeaders->FileHeader;
BYTE* lpOptionalHeader = &lpNtHeaders->OptionalHeader;
PIMAGE_SECTION_HEADER lpSections = (PIMAGE_SECTION_HEADER)(lpOptionalHeader + lpFileHeader->SizeOfOptionalHeader);
for (int i = 0; i < lpFileHeader->NumberOfSections; ++i)
{
IMAGE_SECTION_HEADER section = lpSections[i];
if (rva >= section.VirtualAddress && rva <= section.VirtualAddress + section.Misc.VirtualSize)
return section.PointerToRawData + rva - section.VirtualAddress;
}
return -1; // Invalid RVA
}
关于c - 解析PIMAGE_IMPORT_DESCRIPTOR时手动加载PE导入结果错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72839654/