解析PE文件
Posted aaaguai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解析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开始)
下面po上代码:
#include<Windows.h>
#include<iostream>
#include<time.h>
using namespace std;
typedef struct _MAP_FILE_STRUCT
{
HANDLE hFile; //文件句柄
HANDLE hMapping; //映射文件句柄
LPVOID YXImageBase; //映像基址
} MAP_FILE_STRUCT, *PMAP_FILE_STRUCT;
/*一、加载要打开的文件
采用内存映射的方式将文件加载到内存中,内存映射函数包括:CreateFileMapping, OpenFileMapping, MapViewOfFile, UnmapViewOfFile和FlushViewOfFile。
定义一个函数,如果是一个PE文件则返回true, 否则返回false:*/
BOOL LoadFile(LPTSTR lpFileName, PMAP_FILE_STRUCT pstMapFile)//加载文件 并将文件句柄、
{
if (lpFileName == nullptr)
{
return false;
}
HANDLE hFile = NULL;
HANDLE hMapping = NULL;
LPVOID ImageBase = NULL;
memset(pstMapFile, 0, sizeof(MAP_FILE_STRUCT));
//1、只读方式打开文件,返回文件句柄
hFile = CreateFile(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (!hFile)
{
return false;
}
//2、创建内存映射文件对象
hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, 0);
if (!hMapping)
{
CloseHandle(hFile);
return false;
}
//3、创建内存映射文件的视图
ImageBase = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
if (!ImageBase)
{
CloseHandle(hFile);
CloseHandle(hMapping);
return false;
}
pstMapFile->hFile = hFile;
pstMapFile->hMapping = hMapping;
pstMapFile->YXImageBase = ImageBase;
return true;
}
//二、程序执行完毕后,回收资源
void UnLoadFile(PMAP_FILE_STRUCT pstMapFile)
{
if (pstMapFile->hFile)
{
CloseHandle(pstMapFile->hFile);
}
if (pstMapFile->hMapping)
{
CloseHandle(pstMapFile->hMapping);
}
if (pstMapFile->YXImageBase)
{
//撤销映射并使用CloseHandle函数关闭内存映射文件对象句柄
UnmapViewOfFile(pstMapFile->YXImageBase);
}
}
//三、文件格式检测
bool IsPEFile(LPVOID ImageBase)//如果1.基地址为0 2.DOC头的e_magic不为5A4D(MZ) 3.NT头的Signature不为00004550(PE) 任一成立,则该文件不是PE文件
{
PIMAGE_DOS_HEADER pDH = nullptr;//指向空指针
PIMAGE_NT_HEADERS32 pNtH = nullptr;
if (!ImageBase)
{
return false;
}
pDH = (PIMAGE_DOS_HEADER)ImageBase;
if (pDH->e_magic != IMAGE_DOS_SIGNATURE) // 5A4D "MZ"
{
return false;
}
// pDH->e_lfanew保存PIMAGE_NT_HEADERS32的偏移地址,加上基址pDH即为MAGE_NT_HEADERS的地址
pNtH = (PIMAGE_NT_HEADERS32)((DWORD)pDH + pDH->e_lfanew); //基地址+DOS头的最后一个元素取得的值 即为下一个NT头的地址
if (pNtH->Signature != IMAGE_NT_SIGNATURE) //"PE"
{
return false;
}
return true;
}
//四、读取FileHeader和OptionalHeader的内容
PIMAGE_DOS_HEADER GetDOSHeaders(LPVOID ImageBase)//1、获取指向IMAGE_DOS_HEADERS结构的指针
{
PIMAGE_DOS_HEADER pDH = nullptr;
if (!IsPEFile(ImageBase))
{
return nullptr;
}
pDH = (PIMAGE_DOS_HEADER)ImageBase;
return pDH;
}
PIMAGE_NT_HEADERS GetNtHeader(LPVOID ImageBase)//2、获取指向IMAGE_NT_HEADERS结构的指针
{
PIMAGE_DOS_HEADER pDH = nullptr;
PIMAGE_NT_HEADERS pNtH = nullptr;
if (!IsPEFile(ImageBase))
{
return nullptr;
}
pDH = (PIMAGE_DOS_HEADER)ImageBase;
pNtH = (PIMAGE_NT_HEADERS)((DWORD)pDH + pDH->e_lfanew);
return pNtH;
}
PIMAGE_FILE_HEADER GetFileHeader(LPVOID ImageBase)//3、获取指向IMAGE_FILE_HEADER结构的指针
{
PIMAGE_NT_HEADERS pNtH = GetNtHeader(ImageBase);
if (!pNtH)
{
return nullptr;
}
return &(pNtH->FileHeader);
}
PIMAGE_OPTIONAL_HEADER GetOptionalHeader(LPVOID ImageBase)//4、获取指向IMAGE_OPTIONAL_HEADER结构的指针
{
PIMAGE_NT_HEADERS pNtH = GetNtHeader(ImageBase);
if (!pNtH)
{
return nullptr;
}
return &(pNtH->OptionalHeader);
}
PIMAGE_SECTION_HEADER GetSectionone(LPVOID ImageBase)//5、获取指向第一个节头的指针
{
PIMAGE_NT_HEADERS pNtH = GetNtHeader(ImageBase);//得到NT头
if (!pNtH)
{
return nullptr;
}
//节表的位置=DOS头大小(确定)+标准PE头大小(确定)+可选PE头大小(标准PE头的成员.SizeOfOptionalHeader记录了可选PE头大小)
return IMAGE_FIRST_SECTION(pNtH);//通过公式计算 得到SECTION的首地址
}
short GetSectionCount(LPVOID ImageBase)//6、节头的数量 通过File Hander的NumberOfSections得出
{
PIMAGE_FILE_HEADER pNtFileHeader = GetFileHeader(ImageBase);
if (pNtFileHeader != NULL)
{
return pNtFileHeader->NumberOfSections;
}
}
//五、地址转换
//1.相对虚拟地址转文件地址(文件地址=映射基址+文件偏移地址(文件偏移地址=RVA-范围内的内存偏移(起点)+范围内的文件偏移(起点)))
DWORD RVAtoFA(LPVOID ImageBase,DWORD RVA)//接受一个当前文件的映射基址和需转换的RVA
{
PIMAGE_SECTION_HEADER m_section = GetSectionone(ImageBase);//指向节表
PIMAGE_FILE_HEADER m_pe = GetFileHeader(ImageBase);//指向PE头
int sectioncount = m_pe->NumberOfSections;//获得节表数量
for (int num = 0; num < sectioncount;num++)
{
if (RVA>=m_section->VirtualAddress && RVA < (m_section->VirtualAddress + m_section->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内
{
return (DWORD)ImageBase + RVA - m_section->VirtualAddress + m_section->PointerToRawData;
/*则文件地址=映射基址 + 文件偏移地址( RVA- VirtualAddress + RawAddress)
= 映射基址 + RVA - VirtualAddress + RawAddress*/
}
m_section++;//指向下一个节表
}
return 0;
}
//2.相对虚拟地址转文件偏移地址(文件偏移地址=RVA-范围内的内存偏移(起点)+范围内的文件偏移(起点)))
DWORD RVAtoFVA(LPVOID ImageBase, DWORD RVA)//接受一个当前文件的映射基址和需转换的RVA
{
PIMAGE_SECTION_HEADER m_section = GetSectionone(ImageBase);//指向节表
PIMAGE_FILE_HEADER m_pe = GetFileHeader(ImageBase);//指向PE头
int sectioncount = m_pe->NumberOfSections;//获得节表数量
for (int num = 0; num < sectioncount; num++)
{
if (RVA >= m_section->VirtualAddress && RVA < (m_section->VirtualAddress + m_section->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内
{
return RVA - m_section->VirtualAddress + m_section->PointerToRawData;
}
m_section++;//指向下一个节表
}
return 0;
}
//3.虚拟地址转文件偏移地址
DWORD VAtoFVA(LPVOID ImageBase, DWORD VA)//接受一个当前文件的映射基址和需转换的VA
{
PIMAGE_NT_HEADERS pNT = GetNtHeader(ImageBase);//获得NT头
DWORD IB = pNT->OptionalHeader.ImageBase;//获得基地址
DWORD RVA = VA - IB;//将要转换的VA先转换为RVA
PIMAGE_SECTION_HEADER m_section = GetSectionone(ImageBase);//指向节表
PIMAGE_FILE_HEADER m_pe = GetFileHeader(ImageBase);//指向PE头
int sectioncount = m_pe->NumberOfSections;//获得节表数量
for (int num = 0; num < sectioncount; num++)
{
if (RVA>m_section->VirtualAddress && RVA < (m_section->VirtualAddress + m_section->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内
{
return RVA - m_section->VirtualAddress + m_section->PointerToRawData;//则文件偏移地址=RVA - VirtualAddress + RawAddress
}
m_section++;//指向下一个节表
}
return 0;
}
//4.相对虚拟地址转虚拟地址
DWORD RVAtoVA(LPVOID ImageBase, DWORD RVA)//接受一个当前文件的映射基址和需转换的RVA
{
PIMAGE_NT_HEADERS pNT = GetNtHeader(ImageBase);//获得NT头
DWORD IB = pNT->OptionalHeader.ImageBase;//获得基地址
return RVA + IB;
}
//5.虚拟地址转相对虚拟地址
DWORD VAtoRVA(LPVOID ImageBase, DWORD VA)//接受一个当前文件的映射基址和需转换的VA
{
PIMAGE_NT_HEADERS pNT = GetNtHeader(ImageBase);//获得NT头
DWORD IB = pNT->OptionalHeader.ImageBase;//获得基地址
return VA - IB;
}
//六、输出文件头信息
void ShowFileHeaderInfo(PMAP_FILE_STRUCT stMapFile)
{
char strTmp[1024] = { 0 };
PIMAGE_DOS_HEADER p_dos = nullptr;//DOS头
p_dos = GetDOSHeaders(stMapFile->YXImageBase);
if (!p_dos)
{
printf("获取DOS头失败
");
return;
}
printf("Dos Header:
");
printf("e_magic: %04lX
", p_dos->e_magic);
/*省略若干个信息*/
printf("
");
PIMAGE_NT_HEADERS m_nt = nullptr; //NT头
m_nt = GetNtHeader(stMapFile->YXImageBase);
if (!m_nt)
{
printf("获取文件头失败
");
return;
}
printf("NT Header:
");
printf("Signature: %08x
", m_nt->Signature);
PIMAGE_FILE_HEADER p_file = nullptr;//PE头
//将信息按十六进制格式化
p_file = GetFileHeader(stMapFile->YXImageBase);//此时返回出来的是NT头的File结构的首地址
char *strFileHeaderFormat = "
IMAGE_FILE_HEADER:
Machine: %04lX
NumberOfSections: %04lX
TimeDateStamp: %04lX
PointerToSymbolTable:%08X
NumberOfSymbols: %08lX
SizeOfOptionalHeader:%04lX
Characteristics: %04lX
";
sprintf(strTmp, strFileHeaderFormat, p_file->Machine, p_file->NumberOfSections, p_file->TimeDateStamp, p_file->PointerToSymbolTable, p_file->NumberOfSymbols,
p_file->SizeOfOptionalHeader, p_file->Characteristics);
printf("%s", strTmp);
memset(strTmp, 0, sizeof(strTmp));
PIMAGE_OPTIONAL_HEADER p_oh = nullptr; //可选PE头
char *strFileOptHeaderFormat = "
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
";
p_oh = GetOptionalHeader(stMapFile->YXImageBase);//此时返回出来的是NT头的optional结构的首地址
if (!p_oh)
{
printf("Get File Optional Header failed!
");
return;
}
sprintf(strTmp, strFileOptHeaderFormat, p_oh->AddressOfEntryPoint, p_oh->ImageBase, p_oh->BaseOfCode, p_oh->BaseOfData,
p_oh->SizeOfImage, p_oh->SizeOfHeaders, p_oh->SectionAlignment, p_oh->FileAlignment, p_oh->Subsystem, p_oh->CheckSum, p_oh->DllCharacteristics);
printf("%s", strTmp);
PIMAGE_SECTION_HEADER p_section = nullptr; //节表
p_section = GetSectionone(stMapFile->YXImageBase);//通过基址得到NT头 再通过公式算出第一个节的地址
DWORD sectioncount = p_file->NumberOfSections;//通过PE头的NumberOfSections成员 得到节的数量
printf("name: Virtual Size Virtual Address Raw Size Raw Address
");
for (DWORD num = 0; num < sectioncount; num++)
{
/*printf("name:%s,Virtual Size:%08x,Virtual Address:%08x,Raw Size:%08x,Raw Address:%08x
",
p_section->Name, p_section->Misc.VirtualSize, p_section->VirtualAddress, p_section->SizeOfRawData, p_section->PointerToRawData);*/
printf("%s: %08x %08x %08x %08x
", p_section->Name, p_section->Misc.VirtualSize, p_section->VirtualAddress, p_section->SizeOfRawData, p_section->PointerToRawData);
p_section++;
}
printf("
");
/*通过可选PE头的DataDirectory数组(该数组为IMAGE_DATA_DIRECTORY结构 里面是各表的RVA和大小)*/
PIMAGE_OPTIONAL_HEADER ID_RVA = nullptr;//导入表
ID_RVA = GetOptionalHeader(stMapFile->YXImageBase);//此时返回出来的是NT头的optional结构的首地址
printf("导入表的相对偏移地址为:0x%08x
", ID_RVA->DataDirectory[1].VirtualAddress);
printf("导入表的大小为:0x%08x
", ID_RVA->DataDirectory[1].Size);//已对比 取值正确
int dllcount;//dll的数量
int funccount;//dll中函数的数量
long d_dRVA = ID_RVA->DataDirectory[1].VirtualAddress;
//得到第一个导入表的地址
PIMAGE_IMPORT_DESCRIPTOR pdll = (PIMAGE_IMPORT_DESCRIPTOR)((UCHAR*)RVAtoFA(stMapFile->YXImageBase, d_dRVA));
/*上下2条同理,都是通过所需地址的RVA求得在文件中的VA,上面那条求得文件的FVA后,要手动加上映像基地址;下面那条直接返回FRVA(文件地址)
VA=虚拟地址 RVA=相对虚拟地址 IB基地址 FVA=文件地址 FRVA=文件偏移地址(相对开头)FIB=映射基地址(即stMapFile->YXImageBase)*/
//PIMAGE_IMPORT_DESCRIPTOR pdll2 = (PIMAGE_IMPORT_DESCRIPTOR)ImageRvaToVa(d_nt, d_nIB, CCC->DataDirectory[1].VirtualAddress, NULL);
for (dllcount = 0; pdll->FirstThunk != NULL; dllcount++)
{
char* dllname = (char*)RVAtoFA(stMapFile->YXImageBase, pdll->Name);//将存放DLL名称的地址转换成字符指针
printf("DLL Name:%s INT:0x%08x,Name RVA:0x%08x,IAT:0x%08x
",
dllname, pdll->OriginalFirstThunk, pdll->Name, pdll->FirstThunk);
//通过导入表的OriginalFirstThunk(INT的VRA) 求得dll的输入名称表的va
//PIMAGE_THUNK_DATA f_thunk = (PIMAGE_THUNK_DATA)ImageRvaToVa(d_nt, d_nIB, pdll->OriginalFirstThunk, NULL);
PIMAGE_THUNK_DATA f_thunk = (PIMAGE_THUNK_DATA)((UCHAR*)RVAtoFA(stMapFile->YXImageBase, pdll->OriginalFirstThunk));
for (funccount = 0; f_thunk->u1.Function != 0; funccount++)
{
if (f_thunk->u1.AddressOfData & 0x80000000) //如果导入表的输入名称表(INT)的最高位为1,则为按序号导入
{
printf(" [%d] IAT:%04x 导入序号:%04x
", funccount, f_thunk->u1.AddressOfData, f_thunk->u1.AddressOfData & 0xffff);
} //这里的f_thunk->u1.AddressOfData &0xffff是为了让32位的二进制最后以16位显示出来
else //不为1,则函数名称以字符串形式 此时变为指向PIMAGE_IMPORT_BY_NAME结构的RVA
{
PIMAGE_IMPORT_BY_NAME f_name = (PIMAGE_IMPORT_BY_NAME)RVAtoFA(stMapFile->YXImageBase, f_thunk->u1.AddressOfData);
printf(" [%d] Hint:%04x Name:%s
", funccount, f_name->Hint, f_name->Name);
}
f_thunk++;
}
printf("-----------------------------------------------------------------------------------------
");
pdll++;
}
printf("该PE文件导入表共有%d个DLL
", dllcount);
IMAGE_IMPORT_DESCRIPTOR ITDP; //导入表由这样的结构的数组构成,最后以一个该结构全为0作为结束标志
IMAGE_THUNK_DATA ITD2P;//上述结构中,有2个成员导入名称表(INT)和导入地址表(IAT)是该类型的结构
IMAGE_IMPORT_BY_NAME IIBN;//当ITD的最高位为1时,IAT的值变成了指向这样一个结构的RVA
/*导出表*/
PIMAGE_OPTIONAL_HEADER ED_RVA = GetOptionalHeader(stMapFile->YXImageBase);
printf("导出表的相对偏移地址:0x%08x
", ED_RVA->DataDirectory[0].VirtualAddress);
printf("导出表长度:0x%08x
", ED_RVA->DataDirectory[0].Size);
DWORD m_EDRva = ED_RVA->DataDirectory[0].VirtualAddress;//导出表的相对偏移地址
PIMAGE_EXPORT_DIRECTORY pEDFA = (PIMAGE_EXPORT_DIRECTORY)((UCHAR*)RVAtoFA(stMapFile->YXImageBase, m_EDRva));//求得导出表的文件地址
printf("--------------------------------------------------------------------------------
");
printf("Member Offset Size Value
");
printf("--------------------------------------------------------------------------------
");
printf("Characteristics %08x Dword %08x
", m_EDRva, pEDFA->Characteristics);
printf("TimeDateStamp %08x Dword %08x
", m_EDRva + sizeof(DWORD), pEDFA->TimeDateStamp);
printf("MajorVersion %08x Word %04x
", m_EDRva + sizeof(DWORD)* 2, pEDFA->MajorVersion);
printf("MinorVersion %08x Word %04x
", m_EDRva + sizeof(DWORD)* 2 + sizeof(WORD), pEDFA->MinorVersion);
printf("Name %08x Dword %08x
", m_EDRva + sizeof(DWORD)* 2 + sizeof(WORD)* 2, pEDFA->Name);
printf("Base %08x Dword %08x
", m_EDRva + sizeof(DWORD)* 3 + sizeof(WORD)* 2, pEDFA->Base);
printf("NumberOfFunctions %08x Dword %08x
", m_EDRva + sizeof(DWORD)* 4 + sizeof(WORD)* 2, pEDFA->NumberOfFunctions);
printf("NumberOfNames %08x Dword %08x
", m_EDRva + sizeof(DWORD)* 5 + sizeof(WORD)* 2, pEDFA->NumberOfNames);
printf("AddressOfFunctions %08x Dword %08x
", m_EDRva + sizeof(DWORD)* 6 + sizeof(WORD)* 2, pEDFA->AddressOfFunctions);
printf("AddressOfNames %08x Dword %08x
", m_EDRva + sizeof(DWORD)* 7 + sizeof(WORD)* 2, pEDFA->AddressOfNames);
printf("AddressOfNameOrdinals %08x Dword %08x
", m_EDRva + sizeof(DWORD)* 8 + sizeof(WORD)* 2, pEDFA->AddressOfNameOrdinals);
DWORD addressFA = 0;//用于接受函数名字RVA
char* addressName = NULL;//用于输出函数名字
for (DWORD EDcount = 0; EDcount < pEDFA->NumberOfFunctions; EDcount++) //导出序号不大于导出函数的值
{
DWORD EDB = pEDFA->Base + (EDcount * 1);//导出序号
DWORD EDAOF = pEDFA->AddressOfFunctions + (EDcount*sizeof(DWORD));//导出函数地址的rva
DWORD EDAON = pEDFA->AddressOfNames + (EDcount*sizeof(DWORD));//导出函数的名字的rva的rva
DWORD EDNB = pEDFA->AddressOfNameOrdinals + (EDcount*sizeof(WORD));//导出函数名字的序号的rva
addressFA = *(DWORD*)RVAtoFA(stMapFile->YXImageBase, EDAON);//将存放着函数名字RVA的RVA转换成文件地址 解引用获得函数名字RVA
addressName = (char*)(RVAtoFA(stMapFile->YXImageBase, addressFA));//再将函数名字RVA转换成文件地址,取得文件名字
printf("Ordinals:%08x Functions RVA:%08x Name Ordinal:%08x Name RVA:%08X Name:%s
", EDB, EDAOF, EDNB, EDAON, addressName);
}
/*重定位表*/
DWORD BR_RVA = p_oh->DataDirectory[5].VirtualAddress;//重定位表rva
DWORD BR_count = p_oh->DataDirectory[5].Size;//重定位表的大小
printf("
重定位表的相对偏移地址:0x%08x
", p_oh->DataDirectory[5].VirtualAddress);
printf("重定位表的大小:0x%08x
", p_oh->DataDirectory[5].Size);
if (BR_RVA == 0)
{
printf("该文件没有重定位表
");
}
PIMAGE_BASE_RELOCATION pBR;//重定位表的文件地址
//当前位置的RVA不能大于重定位表最后一块数据的RVA(起始位置+大小)
while (BR_RVA<(p_oh->DataDirectory[5].VirtualAddress+p_oh->DataDirectory[5].Size))
{
pBR = (PIMAGE_BASE_RELOCATION)(RVAtoFA(stMapFile->YXImageBase, BR_RVA));//取得当前块文件地址
int BR_count = (pBR->SizeOfBlock - 8) / 2;//这个块中需要重定位的值的个数 (该块前8位分别是起始地址和长度)
printf("VirtualAddress:%08x SizeOfBlock:%08x ltems:%d
", pBR->VirtualAddress, pBR->SizeOfBlock,BR_count);
for (int num = 0; num < BR_count; num++)
{
WORD pBR_Chunk = *(WORD*)RVAtoFA(stMapFile->YXImageBase, BR_RVA + 8+sizeof(WORD)*num);
if (pBR_Chunk != 0)
{
printf("ltem:%04x 需要重定位的数据的RVA:%08x
", pBR_Chunk, (pBR_Chunk ^ 0x3000)+pBR->VirtualAddress);
/*重定位地址=当前块内的偏移(当前2字节的值(需要通过文件地址取出来)^ 0x3000)+当前块的基址*/
}
}
printf("--------------------------------------------------------------
");
BR_RVA = BR_RVA + pBR->SizeOfBlock;
}
}
MAP_FILE_STRUCT stMapFile = { nullptr, nullptr, nullptr };
int main()
{
LPTSTR filePath = TEXT("C:\\Users\\Admin\\Desktop\\kkk.dll");
if (!LoadFile(filePath, &stMapFile))//载入文件内容
{
return -1;
}
if (!IsPEFile(stMapFile.YXImageBase))//检查文件是否为PE文件
{
UnLoadFile(&stMapFile);
return -1;
}
ShowFileHeaderInfo(&stMapFile);
UnLoadFile(&stMapFile);
system("pause");
}
以上是关于解析PE文件的主要内容,如果未能解决你的问题,请参考以下文章