从磁盘位置读取 PE 文件时,我需要将 RVA(取自 pdb 文件的相对虚拟地址)映射到 PE 文件(EXE)偏移量。为此,我需要将 RVA 转换为文件偏移量,以便可以从该位置读出 GUIDS(CLSID,IID)。
问候乌斯曼
从磁盘位置读取 PE 文件时,我需要将 RVA(取自 pdb 文件的相对虚拟地址)映射到 PE 文件(EXE)偏移量。为此,我需要将 RVA 转换为文件偏移量,以便可以从该位置读出 GUIDS(CLSID,IID)。
问候乌斯曼
template <class T> LPVOID GetPtrFromRVA(
DWORD rva,
T* pNTHeader,
PBYTE imageBase ) // 'T' = PIMAGE_NT_HEADERS
{
PIMAGE_SECTION_HEADER pSectionHdr;
INT delta;
pSectionHdr = GetEnclosingSectionHeader( rva, pNTHeader);
if ( !pSectionHdr )
return 0;
delta = (INT)(pSectionHdr->VirtualAddress-pSectionHdr->PointerToRawData);
return (PVOID) ( imageBase + rva - delta );
}
template <class T> PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(
DWORD rva,
T* pNTHeader) // 'T' == PIMAGE_NT_HEADERS
{
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
unsigned i;
for ( i=0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++ )
{
// This 3 line idiocy is because Watcom's linker actually sets the
// Misc.VirtualSize field to 0. (!!! - Retards....!!!)
DWORD size = section->Misc.VirtualSize;
if ( 0 == size )
size = section->SizeOfRawData;
// Is the RVA within this section?
if ( (rva >= section->VirtualAddress) &&
(rva < (section->VirtualAddress + size)))
return section;
}
return 0;
}
应该做的伎俩...
您可以使用ImageRvaToVa函数。
下面的代码片段将入口点地址的 RVA 转换为实际的磁盘偏移量。代替 AEP 的地址发送任何 RVA 以将其转换为磁盘偏移量。
LPCSTR fileName="exe_file_to_parse";
HANDLE hFile;
HANDLE hFileMapping;
LPVOID lpFileBase;
PIMAGE_DOS_HEADER dosHeader;
PIMAGE_NT_HEADERS peHeader;
PIMAGE_SECTION_HEADER sectionHeader;
hFile = CreateFileA(fileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if(hFile==INVALID_HANDLE_VALUE)
{
printf("\n CreateFile failed in read mode \n");
return 1;
}
hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,NULL);
if(hFileMapping==0)
{
printf("\n CreateFileMapping failed \n");
CloseHandle(hFile);
return 1;
}
lpFileBase = MapViewOfFile(hFileMapping,FILE_MAP_READ,0,0,0);
if(lpFileBase==0)
{
printf("\n MapViewOfFile failed \n");
CloseHandle(hFileMapping);
CloseHandle(hFile);
return 1;
}
dosHeader = (PIMAGE_DOS_HEADER) lpFileBase; //pointer to dos headers
if(dosHeader->e_magic==IMAGE_DOS_SIGNATURE)
{
//if it is executable file print different fileds of structure
//dosHeader->e_lfanew : RVA for PE Header
printf("\n DOS Signature (MZ) Matched");
//pointer to PE/NT header
peHeader = (PIMAGE_NT_HEADERS) ((u_char*)dosHeader+dosHeader->e_lfanew);
if(peHeader->Signature==IMAGE_NT_SIGNATURE)
{
printf("\n PE Signature (PE) Matched \n");
// valid executable
//address of entry point
DWORD ptr = peHeader->OptionalHeader.AddressOfEntryPoint;
//instead of AEP send any pointer to get actual disk offset of it
printf("\n RVA : %x \n",ptr); // this is in memory address
//suppose any one wants to know actual disk offset of "address of entry point" (AEP)
sectionHeader = IMAGE_FIRST_SECTION(peHeader);
UINT nSectionCount = peHeader->FileHeader.NumberOfSections;
UINT i=0;
for( i=0; i<=nSectionCount; ++i, ++sectionHeader )
{
if((sectionHeader->VirtualAddress) > ptr)
{
sectionHeader--;
break;
}
}
if(i>nSectionCount)
{
sectionHeader = IMAGE_FIRST_SECTION(peHeader);
UINT nSectionCount = peHeader->FileHeader.NumberOfSections;
for(i=0; i<nSectionCount-1; ++i,++sectionHeader);
}
DWORD retAddr = ptr - (sectionHeader->VirtualAddress) +
(sectionHeader->PointerToRawData);
printf("\n Disk Offset : %x \n",retAddr+(PBYTE)lpFileBase);
// retAddr + lpFileBase contains the actual disk address of address of entry point
}
UnmapViewOfFile(lpFileBase);
CloseHandle(hFileMapping);
CloseHandle(hFile);
//getchar();
return 0;
}
else
{
printf("\n DOS Signature (MZ) Not Matched \n");
UnmapViewOfFile(lpFileBase);
CloseHandle(hFileMapping);
CloseHandle(hFile);
return 1;
}