解析PE文件

最近在自学解析PE文件,根据小辣椒(CFF Explorer)以及各论坛上大佬的帖子,做了个黑屏打印PE文件的,历时7天完成,在此想跟有相关需要的同学们分享下思路,有不足之处也希望大家不吝赐教,指点出来。谢谢。

PE文件主要有DOS头,NT头(包含PE头,可选PE头,可选PE头中又包含着一个数据目录,里面包含了其他表单的RVA和大小),节表

表单主要有导出表,导入表,重定位表,资源表。除了资源表外,其他表单的各项数据都用代码实现了打印。

首先通过CreateFile打开文件,获得文件句柄,随后调用CreateFileMapping生成内存映射对象,再调用MapViewOfFile获得文件映射基址。

DOS头RVA=(IMAGE_DOS_HEADER)映射基址;

NT头RVA=(IMAGE_NT_HEADERS)DOS头+DOS头->e_lfanew的值;

PE头RVA=&((IMAGE_FILE_HEADER)NT头->FileHeader);

可选PE头RVA=&((IMAGE_OPTIONAL_HEADER)NT头->OptionalHeader);

节表RVA=IMAGE_FIRST_SECTION(NT头);//节表的位置=DOS头大小(确定)+标准PE头大小(确定)+可选PE头大小(标准PE头的成员.SizeOfOptionalHeader记录了可选PE头大小)

节表数量:PE头->NumberOfSections;

导入表、导出表、重定位表的RVA和长度分别在可选PE头的->DataDirectory的第三、第一、第六个数组当中的VirtualAddress和size成员中(注意数组索引从0开始)

注意,在解析前,先要判断当前文件是否为PE文件:以下2个条件缺一不可。

1.dos头的值,即dos->e_magic是否等于0x5A4D(MZ)

2.NT头,即DOS头(RVA)+DOS->e_lfnew的值 得到的即为NT头的RVA    查看NT头的值,即NT->signatrue的值是否为0x00004550(PE)

下面po上代码:

#include<Windows.h>
#include<iostream>
#include<time.h>
using namespace std;

typedef struct _MApFileH_STRUCT
{
HANDLE hFile; //文件句柄
HANDLE hFileMap; //映射文件句柄
LPVOID pMapImageBase; //映像基址
} MApFileH_STRUCT, *PMApFileH_STRUCT;
/*一、加载要打开的文件
采用内存映射的方式将文件加载到内存中,内存映射函数包括:CreateFileMapping, OpenFileMapping, MapViewOfFile, UnmapViewOfFile和FlushViewOfFile。
定义一个函数,如果是一个PE文件则返回true, 否则返回false:*/
BOOL LoadFile(LPTSTR pFileName, PMApFileH_STRUCT pFileStruct)//加载文件 并将文件句柄、
{
if (pFileName == nullptr)
{
return false;
}
HANDLE hFile = NULL;
HANDLE hFileMap = NULL;
LPVOID pMapImageBase = NULL;
memset(pFileStruct, 0, sizeof(MApFileH_STRUCT));
//1、只读方式打开文件,返回文件句柄
hFile = CreateFile(pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (!hFile)
{
return false;
}
//2、创建内存映射文件对象
hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, 0);
if (!hFileMap)
{
CloseHandle(hFile);
return false;
}
//3、创建内存映射文件的视图
pMapImageBase = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
if (!pMapImageBase)
{
CloseHandle(hFile);
CloseHandle(hFileMap);
return false;
}
pFileStruct->hFile = hFile;
pFileStruct->hFileMap = hFileMap;
pFileStruct->pMapImageBase = pMapImageBase;
return true;
}
//二、程序执行完毕后,回收资源
void UnLoadFile(PMApFileH_STRUCT pFileStruct)
{
if (pFileStruct->hFile)
{
CloseHandle(pFileStruct->hFile);
}

if (pFileStruct->hFileMap)
{
CloseHandle(pFileStruct->hFileMap);
}

if (pFileStruct->pMapImageBase)
{
//撤销映射并使用CloseHandle函数关闭内存映射文件对象句柄
UnmapViewOfFile(pFileStruct->pMapImageBase);
}
}
//三、文件格式检测
bool IsPEFile(LPVOID pMapImageBase)//如果1.基地址为0 2.DOC头的e_magic不为5A4D(MZ) 3.NT头的Signature不为00004550(PE) 任一成立,则该文件不是PE文件
{
PIMAGE_DOS_HEADER pDosH = nullptr;//指向空指针
PIMAGE_NT_HEADERS32 pNtH = nullptr;
if (!pMapImageBase)
{
return false;
}

pDosH = (PIMAGE_DOS_HEADER)pMapImageBase;
if (pDosH->e_magic != IMAGE_DOS_SIGNATURE) // 5A4D "MZ"
{
return false;
}
// pDH->e_lfanew保存PIMAGE_NT_HEADERS32的偏移地址,加上基址pDH即为MAGE_NT_HEADERS的地址
pNtH = (PIMAGE_NT_HEADERS32)((DWORD)pDosH + pDosH->e_lfanew); //基地址+DOS头的最后一个元素取得的值 即为下一个NT头的地址
if (pNtH->Signature != IMAGE_NT_SIGNATURE) //"PE"
{
return false;
}
return true;
}

//四、读取FileHeader和OptionalHeader的内容
PIMAGE_DOS_HEADER GetDOSHeaders(LPVOID pMapImageBase)//1、获取指向IMAGE_DOS_HEADERS结构的指针
{
PIMAGE_DOS_HEADER pDosH = nullptr;
if (!IsPEFile(pMapImageBase))
{
return nullptr;
}
pDosH = (PIMAGE_DOS_HEADER)pMapImageBase;
return pDosH;
}
PIMAGE_NT_HEADERS GetNtHeader(LPVOID pMapImageBase)//2、获取指向IMAGE_NT_HEADERS结构的指针
{
PIMAGE_DOS_HEADER pDosH = nullptr;
PIMAGE_NT_HEADERS pNtH = nullptr;
if (!IsPEFile(pMapImageBase))
{
return nullptr;
}

pDosH = (PIMAGE_DOS_HEADER)pMapImageBase;
pNtH = (PIMAGE_NT_HEADERS)((DWORD)pDosH + pDosH->e_lfanew);
return pNtH;
}
PIMAGE_FILE_HEADER GetFileHeader(LPVOID pMapImageBase)//3、获取指向IMAGE_FILE_HEADER结构的指针
{
PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);
if (!pNtH)
{
return nullptr;
}
return &(pNtH->FileHeader);
}
PIMAGE_OPTIONAL_HEADER GetOptionalHeader(LPVOID pMapImageBase)//4、获取指向IMAGE_OPTIONAL_HEADER结构的指针
{
PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);
if (!pNtH)
{
return nullptr;
}
return &(pNtH->OptionalHeader);
}

PIMAGE_SECTION_HEADER GetSectionone(LPVOID pMapImageBase)//5、获取指向第一个节头的指针
{
PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//得到NT头
if (!pNtH)
{
return nullptr;
}
//节表的位置=DOS头大小(确定)+标准PE头大小(确定)+可选PE头大小(标准PE头的成员.SizeOfOptionalHeader记录了可选PE头大小)
return IMAGE_FIRST_SECTION(pNtH);//通过公式计算 得到SECTION的首地址
}

short GetdwSectionCount(LPVOID pMapImageBase)//6、节头的数量 通过File Hander的NumberOfSections得出
{
PIMAGE_FILE_HEADER pFileHeader = GetFileHeader(pMapImageBase);
if (pFileHeader != NULL)
{
return pFileHeader->NumberOfSections;
}
}
//五、地址转换
//1.相对虚拟地址转文件地址(文件地址=映射基址+文件偏移地址(文件偏移地址=RVA-范围内的内存偏移(起点)+范围内的文件偏移(起点)))
DWORD RVAtoFA(LPVOID pMapImageBase, DWORD dwRVA)//接受一个当前文件的映射基址和需转换的RVA
{
PIMAGE_SECTION_HEADER pSectionHH = GetSectionone(pMapImageBase);//指向节表
PIMAGE_FILE_HEADER pFileH = GetFileHeader(pMapImageBase);//指向PE头
int dwSectionCount = pFileH->NumberOfSections;//获得节表数量
for (int iNum = 0; iNum < dwSectionCount; iNum++)
{
if (dwRVA >= pSectionHH->VirtualAddress && dwRVA < (pSectionHH->VirtualAddress + pSectionHH->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内
{
return (DWORD)pMapImageBase + dwRVA - pSectionHH->VirtualAddress + pSectionHH->PointerToRawData;
/*则文件地址=映射基址 + 文件偏移地址( RVA- VirtualAddress + RawAddress)
= 映射基址 + RVA - VirtualAddress + RawAddress*/
}
pSectionHH++;//指向下一个节表
}
return 0;
}
//2.相对虚拟地址转文件偏移地址(文件偏移地址=RVA-范围内的内存偏移(起点)+范围内的文件偏移(起点)))
DWORD RVAtoFVA(LPVOID pMapImageBase, DWORD dwRVA)//接受一个当前文件的映射基址和需转换的RVA
{
PIMAGE_SECTION_HEADER pSectionH = GetSectionone(pMapImageBase);//指向节表
PIMAGE_FILE_HEADER pFileH = GetFileHeader(pMapImageBase);//指向PE头
int dwSectionCount = pFileH->NumberOfSections;//获得节表数量
for (int iNum = 0; iNum < dwSectionCount; iNum++)
{
if (dwRVA >= pSectionH->VirtualAddress && dwRVA < (pSectionH->VirtualAddress + pSectionH->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内
{
return dwRVA - pSectionH->VirtualAddress + pSectionH->PointerToRawData;
}
pSectionH++;//指向下一个节表
}
return 0;
}
//3.虚拟地址转文件偏移地址
DWORD VAtoFVA(LPVOID pMapImageBase, DWORD dwVA)//接受一个当前文件的映射基址和需转换的VA
{
PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//获得NT头
DWORD dwIB = pNtH->OptionalHeader.ImageBase;//获得基地址
DWORD dwRVA = dwVA - dwIB;//将要转换的VA先转换为RVA
PIMAGE_SECTION_HEADER pSectionH = GetSectionone(pMapImageBase);//指向节表
PIMAGE_FILE_HEADER pFileH = GetFileHeader(pMapImageBase);//指向PE头
int dwSectionCount = pFileH->NumberOfSections;//获得节表数量
for (int iNum = 0; iNum < dwSectionCount; iNum++)
{
if (dwRVA>pSectionH->VirtualAddress && dwRVA < (pSectionH->VirtualAddress + pSectionH->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内
{
return dwRVA - pSectionH->VirtualAddress + pSectionH->PointerToRawData;//则文件偏移地址=RVA - VirtualAddress + RawAddress
}
pSectionH++;//指向下一个节表
}
return 0;
}
//4.相对虚拟地址转虚拟地址
DWORD RVAtoVA(LPVOID pMapImageBase, DWORD dwRVA)//接受一个当前文件的映射基址和需转换的RVA
{
PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//获得NT头
DWORD dwIB = pNtH->OptionalHeader.ImageBase;//获得基地址
return dwRVA + dwIB;
}
//5.虚拟地址转相对虚拟地址
DWORD VAtoRVA(LPVOID pMapImageBase, DWORD dwVA)//接受一个当前文件的映射基址和需转换的VA
{
PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//获得NT头
DWORD dwIB = pNtH->OptionalHeader.ImageBase;//获得基地址
return dwVA - dwIB;
}
//六、输出文件头信息
void ShowFileHeaderInfo(PMApFileH_STRUCT pFileStruct)
{
char szOutText[1024] = { 0 };
PIMAGE_DOS_HEADER pDosH = nullptr;//DOS头
pDosH = GetDOSHeaders(pFileStruct->pMapImageBase);
if (!pDosH)
{
printf("获取DOS头失败 ");
return;
}
printf("Dos Header: ");
printf("e_magic: %04lX ", pDosH->e_magic);
/*省略若干个信息*/
printf(" ");
PIMAGE_NT_HEADERS pNtH = nullptr; //NT头
pNtH = GetNtHeader(pFileStruct->pMapImageBase);
if (!pNtH)
{
printf("获取文件头失败 ");
return;
}
printf("NT Header: ");
printf("Signature: %08x ", pNtH->Signature);
PIMAGE_FILE_HEADER pFileH = nullptr;//PE头
//将信息按十六进制格式化
pFileH = GetFileHeader(pFileStruct->pMapImageBase);//此时返回出来的是NT头的File结构的首地址
char *szFileHeaderFormat = " IMAGE_FILE_HEADER:
Machine: %04lX
NumberOfSections: %04lX
TimeDateStamp: %04lX
PointerToSymbolTable:%08X
NumberOfSymbols: %08lX
SizeOfOptionalHeader:%04lX
Characteristics: %04lX ";
sprintf(szOutText, szFileHeaderFormat, pFileH->Machine, pFileH->NumberOfSections, pFileH->TimeDateStamp, pFileH->PointerToSymbolTable, pFileH->NumberOfSymbols,
pFileH->SizeOfOptionalHeader, pFileH->Characteristics);
printf("%s", szOutText);
memset(szOutText, 0, sizeof(szOutText));

PIMAGE_OPTIONAL_HEADER pOFileH = nullptr; //可选PE头
char *szFileOptHeaderFormat = " IMAGE_OPTIONAL_HEADER:
Entry Point: %08lX
Image Base: %08lX
Code Base: %08lX
Data Base: %08lX
Image Size: %08lX
Headers Size: %08lX
Section Alignment:%08lX
File Alignment: %08lX
Subsystem: %08lX
Check Sum: %04lX
Dll Flags: %04lX ";
pOFileH = GetOptionalHeader(pFileStruct->pMapImageBase);//此时返回出来的是NT头的optional结构的首地址
if (!pOFileH)
{
printf("Get File Optional Header failed! ");
return;
}
sprintf(szOutText, szFileOptHeaderFormat, pOFileH->AddressOfEntryPoint, pOFileH->ImageBase, pOFileH->BaseOfCode, pOFileH->BaseOfData,
pOFileH->SizeOfImage, pOFileH->SizeOfHeaders, pOFileH->SectionAlignment, pOFileH->FileAlignment, pOFileH->Subsystem, pOFileH->CheckSum, pOFileH->DllCharacteristics);
printf("%s", szOutText);


PIMAGE_SECTION_HEADER pSectionH = nullptr; //节表
pSectionH = GetSectionone(pFileStruct->pMapImageBase);//通过基址得到NT头 再通过公式算出第一个节的地址
DWORD dwSectionCount = pFileH->NumberOfSections;//通过PE头的NumberOfSections成员 得到节的数量
printf("name: Virtual Size Virtual Address Raw Size Raw Address ");
for (DWORD dwNum = 0; dwNum < dwSectionCount; dwNum++)
{
/*printf("name:%s,Virtual Size:%08x,Virtual Address:%08x,Raw Size:%08x,Raw Address:%08x ",
pSectionH->Name, pSectionH->Misc.VirtualSize, pSectionH->VirtualAddress, pSectionH->SizeOfRawData, pSectionH->PointerToRawData);*/
printf("%s: %08x %08x %08x %08x ", pSectionH->Name, pSectionH->Misc.VirtualSize, pSectionH->VirtualAddress, pSectionH->SizeOfRawData, pSectionH->PointerToRawData);
pSectionH++;
}
printf(" ");

/*通过可选PE头的DataDirectory数组(该数组为IMAGE_DATA_DIRECTORY结构 里面是各表的RVA和大小)*/
pOFileH = GetOptionalHeader(pFileStruct->pMapImageBase);//此时返回出来的是NT头的optional结构的首地址
printf("导入表的相对偏移地址为:0x%08x ", pOFileH->DataDirectory[1].VirtualAddress);
printf("导入表的大小为:0x%08x ", pOFileH->DataDirectory[1].Size);//已对比 取值正确
int iDllCount;//dll的数量
int iFuncCount;//dll中函数的数量

long lImportRVA = pOFileH->DataDirectory[1].VirtualAddress;//导入表的位置
//得到第一个导入表的地址
PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((UCHAR*)RVAtoFA(pFileStruct->pMapImageBase, lImportRVA));
/*上下2条同理,都是通过所需地址的RVA求得在文件中的VA,上面那条求得文件的FVA后,要手动加上映像基地址;下面那条直接返回FRVA(文件地址)
VA=虚拟地址 RVA=相对虚拟地址 IB基地址 FVA=文件地址 FRVA=文件偏移地址(相对开头)FIB=映射基地址(即pFileStruct->pMapImageBase)*/
//PIMAGE_IMPORT_DESCRIPTOR pImportH2 = (PIMAGE_IMPORT_DESCRIPTOR)ImageRvaToVa(d_nt, d_nIB, CCC->DataDirectory[1].VirtualAddress, NULL);
for (iDllCount = 0; pImportTable->FirstThunk != NULL; iDllCount++)
{
char* pszDllName = (char*)RVAtoFA(pFileStruct->pMapImageBase, pImportTable->Name);//将存放DLL名称的地址转换成字符指针
printf("DLL Name:%s INT:0x%08x,Name RVA:0x%08x,IAT:0x%08x ",
pszDllName, pImportTable->OriginalFirstThunk, pImportTable->Name, pImportTable->FirstThunk);

//获得导入的DLL中的数据
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((UCHAR*)RVAtoFA(pFileStruct->pMapImageBase, pImportTable->OriginalFirstThunk));
for (iFuncCount = 0; pThunk->u1.Function != 0; iFuncCount++)
{
if (pThunk->u1.AddressOfData & 0x80000000) //如果导入表的输入名称表(INT)的最高位为1,则为按序号导入
{
printf(" [%d] IAT:%04x 导入序号:%04x ", iFuncCount, pThunk->u1.AddressOfData, pThunk->u1.AddressOfData & 0xffff);
} //这里的pThunk->u1.AddressOfData &0xffff是为了让32位的二进制最后以16位显示出来
else //不为1,则函数名称以字符串形式 此时变为指向PIMAGE_IMPORT_BY_NAME结构的RVA
{
PIMAGE_IMPORT_BY_NAME pImportName = (PIMAGE_IMPORT_BY_NAME)RVAtoFA(pFileStruct->pMapImageBase, pThunk->u1.AddressOfData);
printf(" [%d] Hint:%04x Name:%s ", iFuncCount, pImportName->Hint, pImportName->Name);
}
pThunk++;
}
printf("----------------------------------------------------------------------------------------- ");
pImportTable++;
}
printf("该PE文件导入表共有%d个DLL ", iDllCount);
//PIMAGE_IMPORT_DESCRIPTOR pImportTable; //导入表由这样的结构的数组构成,最后以一个该结构全为0作为结束标志
//PIMAGE_THUNK_DATA pThunkNum;//上述结构中,有2个成员导入名称表(INT)和导入地址表(IAT)是该类型的结构 为1时直接输出
//PIMAGE_IMPORT_BY_NAME pImportName;//当ITD的最高位不为1时,IAT的值变成了指向这样一个结构的RVA

/*导出表*/
//PIMAGE_OPTIONAL_HEADER pOFileH = GetOptionalHeader(pFileStruct->pMapImageBase);

DWORD dwExportRVA = pOFileH->DataDirectory[0].VirtualAddress;//导出表的相对偏移地址
if (dwExportRVA == 0)
goto Relocation;
printf("导出表的相对偏移地址:0x%08x ", pOFileH->DataDirectory[0].VirtualAddress);
printf("导出表长度:0x%08x ", pOFileH->DataDirectory[0].Size);
PIMAGE_EXPORT_DIRECTORY dwExportTable = (PIMAGE_EXPORT_DIRECTORY)((UCHAR*)RVAtoFA(pFileStruct->pMapImageBase, dwExportRVA));//求得导出表的文件地址
printf("-------------------------------------------------------------------------------- ");
printf("Member Offset Size Value ");
printf("-------------------------------------------------------------------------------- ");
printf("Characteristics %08x Dword %08x ", dwExportRVA, dwExportTable->Characteristics);
printf("TimeDateStamp %08x Dword %08x ", dwExportRVA + sizeof(DWORD), dwExportTable->TimeDateStamp);
printf("MajorVersion %08x Word %04x ", dwExportRVA + sizeof(DWORD)* 2, dwExportTable->MajorVersion);
printf("MinorVersion %08x Word %04x ", dwExportRVA + sizeof(DWORD)* 2 + sizeof(WORD), dwExportTable->MinorVersion);
printf("Name %08x Dword %08x ", dwExportRVA + sizeof(DWORD)* 2 + sizeof(WORD)* 2, dwExportTable->Name);
printf("Base %08x Dword %08x ", dwExportRVA + sizeof(DWORD)* 3 + sizeof(WORD)* 2, dwExportTable->Base);
printf("NumberOfFunctions %08x Dword %08x ", dwExportRVA + sizeof(DWORD)* 4 + sizeof(WORD)* 2, dwExportTable->NumberOfFunctions);
printf("NumberOfNames %08x Dword %08x ", dwExportRVA + sizeof(DWORD)* 5 + sizeof(WORD)* 2, dwExportTable->NumberOfNames);
printf("AddressOfFunctions %08x Dword %08x ", dwExportRVA + sizeof(DWORD)* 6 + sizeof(WORD)* 2, dwExportTable->AddressOfFunctions);
printf("AddressOfNames %08x Dword %08x ", dwExportRVA + sizeof(DWORD)* 7 + sizeof(WORD)* 2, dwExportTable->AddressOfNames);
printf("AddressOfNameOrdinals %08x Dword %08x ", dwExportRVA + sizeof(DWORD)* 8 + sizeof(WORD)* 2, dwExportTable->AddressOfNameOrdinals);

char* szFuncName = NULL;//用于输出函数名字
for (DWORD dwCount = 0; dwCount < dwExportTable->NumberOfFunctions; dwCount++) //导出序号不大于导出函数的值
{
DWORD dwExportNum = dwExportTable->Base + (dwCount * 1);//导出序号
DWORD dwFuncAddrRVA = dwExportTable->AddressOfFunctions + (dwCount*sizeof(DWORD));//导出函数地址的rva
DWORD dwFuncNameAddrRVA = dwExportTable->AddressOfNames + (dwCount*sizeof(DWORD));//存放着导出函数名字地址的地址
DWORD dwFuncNameNumRVA = dwExportTable->AddressOfNameOrdinals + (dwCount*sizeof(WORD));//导出函数名字的序号的rva
DWORD dwFuncNameRVA = *(DWORD*)RVAtoFA(pFileStruct->pMapImageBase, dwFuncNameAddrRVA);//将存放着函数名字RVA的RVA转换成文件地址 解引用获得函数名字RVA
szFuncName = (char*)(RVAtoFA(pFileStruct->pMapImageBase, dwFuncNameRVA));//再将函数名字RVA转换成文件地址,取得文件名字
printf("Ordinals:%08x Functions RVA:%08x Name Ordinal:%08x Name RVA:%08X Name:%s ", dwExportNum, dwFuncAddrRVA, dwFuncNameNumRVA, dwFuncNameAddrRVA, szFuncName);
}
Relocation:
/*重定位表*/
DWORD dwRelocationRVA = pOFileH->DataDirectory[5].VirtualAddress;//重定位表rva
DWORD dwdwRelocationSize = pOFileH->DataDirectory[5].Size;//重定位表的大小
printf(" 重定位表的相对偏移地址:0x%08x ", pOFileH->DataDirectory[5].VirtualAddress);
printf("重定位表的大小:0x%08x ", pOFileH->DataDirectory[5].Size);
if (dwRelocationRVA == 0)
{
printf("该文件没有重定位表 ");
return;
}
PIMAGE_BASE_RELOCATION pRelocationTable;//重定位表的文件地址
//当前位置的RVA不能大于重定位表最后一块数据的RVA(起始位置+大小)
while (dwRelocationRVA<(pOFileH->DataDirectory[5].VirtualAddress + pOFileH->DataDirectory[5].Size))
{
pRelocationTable = (PIMAGE_BASE_RELOCATION)(RVAtoFA(pFileStruct->pMapImageBase, dwRelocationRVA));//取得当前块文件地址
int dwdwRelocationSize = (pRelocationTable->SizeOfBlock - 8) / 2;//这个块中需要重定位的值的个数 (该块前8位分别是起始地址和长度)
printf("VirtualAddress:%08x SizeOfBlock:%08x ltems:%d ", pRelocationTable->VirtualAddress, pRelocationTable->SizeOfBlock, dwdwRelocationSize);
for (int iNum = 0; iNum < dwdwRelocationSize; iNum++)
{
WORD pRelocationTable_Chunk = *(WORD*)RVAtoFA(pFileStruct->pMapImageBase, dwRelocationRVA + 8 + sizeof(WORD)*iNum);
if (pRelocationTable_Chunk != 0)
{
printf("ltem:%04x 需要重定位的数据的RVA:%08x ", pRelocationTable_Chunk, (pRelocationTable_Chunk ^ 0x3000) + pRelocationTable->VirtualAddress);
/*重定位地址=当前块内的偏移(当前2字节的值(需要通过文件地址取出来)^ 0x3000)+当前块的基址*/
}
}
printf("-------------------------------------------------------------- ");
dwRelocationRVA = dwRelocationRVA + pRelocationTable->SizeOfBlock;
}
}


MApFileH_STRUCT pFileStruct = { nullptr, nullptr, nullptr };
int main()
{
LPTSTR pFilePath = TEXT("E:\学习\C++\静态解析PE文件测试\解析PE文件测试\kkk.dll");
if (!LoadFile(pFilePath, &pFileStruct))//载入文件内容
{
return -1;
}

if (!IsPEFile(pFileStruct.pMapImageBase))//检查文件是否为PE文件
{
UnLoadFile(&pFileStruct);
return -1;
}
ShowFileHeaderInfo(&pFileStruct);
UnLoadFile(&pFileStruct);
system("pause");
}

原文地址:https://www.cnblogs.com/aaaguai/p/11780315.html