zoukankan      html  css  js  c++  java
  • 解析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");
    }

  • 相关阅读:
    thinkphp5 tp5 命名空间 报错 Namespace declaration statement has to be the very first statement in the script
    开启 php 错误 提示 php-fpm 重启 nginx 500错误 解决办法 wdlinux lnmp 一键包 php脚本无法解析执行
    js 设置 cookie 定时 弹出层 提示层 下次访问 不再显示 弹窗 getCookie setCookie setTimeout
    php 二维数组 转字符串 implode 方便 mysql in 查询
    nginx 重启 ps -ef|grep nginx kill -HUP 主进程号
    jquery bootstrap help-block input 表单 提示 帮助 信息
    jquery 倒计时 60秒 短信 验证码 js ajax 获取
    jQuery如何获取同一个类标签的所有的值 遍历
    linux下C语言文件操作相关函数
    gcc,gdb用法
  • 原文地址:https://www.cnblogs.com/aaaguai/p/11780315.html
Copyright © 2011-2022 走看看