• 解析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");
    }

  • 相关阅读:
    设计模式(六)—原型模式Prototype(创建型)
    hdu_2039_三角形_解题报告
    古代赌局 俗话说:十赌九输。因为大多数赌局的背后都藏有阴谋。不过也不尽然,有些赌局背后藏有的是:“阳谋”。 有一种赌局是这样的:桌子上放六个匣子,编号是1至6。多位参与者(以下称玩家)可以把
    读《Boost程序库完全开发指南》
    使用HttpSessionListener接口监听Session的创建和失效
    HDU2317:Nasty Hacks
    意淫的需求要不得
    enumerate(s)与range(len(s))的作用是相同
    一种数据展示方式,UI设计新颖,供大家参考(源码部分) (demo已经上传)
    RMAN Compressed Backupset
  • 原文地址:https://www.cnblogs.com/aaaguai/p/11780315.html
Copyright © 2020-2023  润新知