美文网首页
PE文件简单解析

PE文件简单解析

作者: MagicalGuy | 来源:发表于2018-10-09 00:18 被阅读0次
    image.png image.png
    // PE文件Day002.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <windows.h>
    
    BYTE* g_pFileImageBase = 0;
    PIMAGE_NT_HEADERS g_pNt = 0;
    void ReadFileToMem(WCHAR* szFilePath)
    {
        //打开文件获取文件句柄
        HANDLE hFile = CreateFile(szFilePath, GENERIC_READ, FALSE,
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
            printf("文件打开失败\n");
            return;
        }
        //获取文件大小
        DWORD dwFileSize = GetFileSize(hFile, NULL);
    
        //  HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
        //  char* lpBuf = (char*)MapViewOfFile(hMap, FILE_MAP_READ, NULL,  NULL,dwFileSize);
        //  char* p = lpBuf++;
        g_pFileImageBase = new BYTE[dwFileSize]{};
        DWORD dwRead;
        //将文件读取到内存中
        bool bRet =
            ReadFile(hFile, g_pFileImageBase, dwFileSize, &dwRead, NULL);
        if (!bRet)
        {
            delete[] g_pFileImageBase;
        }
        //关闭句柄
        CloseHandle(hFile);
    }
    bool IsPEFile()
    {
        //使用PIMAGE_DOS_HEADER(占64字节)解释前64个字节
        PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)g_pFileImageBase;
        //判断PE文件的标识是否正确,有一个不对,那么它就不是PE文件
        if (pDos->e_magic != IMAGE_DOS_SIGNATURE)//0x5A4D('MZ')
        {
            return false;
        }
        g_pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + g_pFileImageBase);
        if (g_pNt->Signature != IMAGE_NT_SIGNATURE)//0x00004550('PE')
        {
            return false;
        }
        return true;
    }
    
    void ShowNtImportance()
    {
        printf("入口点RVA:%08X", g_pNt->OptionalHeader.AddressOfEntryPoint);
        printf("文件默认加载:%08X", g_pNt->OptionalHeader.ImageBase);
    
        printf("文件区段个数:%d", g_pNt->FileHeader.NumberOfSections);
        //....
    }
    
    void ShowDirTable()
    {
        //获取目录表个数
        int nCountOfDirTable = g_pNt->OptionalHeader.NumberOfRvaAndSizes;
        printf("数据目录表个数:%d", g_pNt->OptionalHeader.NumberOfRvaAndSizes);
        for (int i = 0; i < nCountOfDirTable; i++)
        {
            //如果VirtualAddress不为0,说明此表存在
            if (g_pNt->OptionalHeader.DataDirectory[i].VirtualAddress)
            {
                //....
            }
        }
    }
    
    void ShowSectionTable()
    {
        //获取区段个数
        int nCountOfSection = g_pNt->FileHeader.NumberOfSections;
        //得到首个区段的位置
        PIMAGE_SECTION_HEADER pSec = IMAGE_FIRST_SECTION(g_pNt);
        //保存区段名字(区段名字可能不是以0为结尾的)
        char strName[9] = {};
        for (int i = 0; i < nCountOfSection; i++)
        {
            memcpy(strName, pSec->Name, 8);
            printf("第%d个区段名:%s", i + 1, strName);
            printf("区段RVA:%08X", pSec->VirtualAddress);
            //....
            //下一个区段地址
            pSec++;
        }
    }
    
    DWORD RVAtoFOA(DWORD dwRVA)
    {
        //此RVA落在哪个区段中
        //找到所在区段后,
        //减去所在区段的起始位置,加上在文件中的起始位置
        int nCountOfSection = g_pNt->FileHeader.NumberOfSections;
        PIMAGE_SECTION_HEADER pSec = IMAGE_FIRST_SECTION(g_pNt);
    
        DWORD dwSecAligment = g_pNt->OptionalHeader.SectionAlignment;
        for (int i = 0; i < nCountOfSection; i++)
        {
            //求在内存中的真实大小
            DWORD dwRealVirSize = pSec->Misc.VirtualSize % dwSecAligment ?
                pSec->Misc.VirtualSize / dwSecAligment * dwSecAligment + dwSecAligment
                : pSec->Misc.VirtualSize;
            if (dwRVA >= pSec->VirtualAddress &&
                dwRVA < pSec->VirtualAddress + dwRealVirSize)
            {
                //FOA = RVA - 内存中区段的起始位置 + 在文件中区段的起始位置 
                return dwRVA - pSec->VirtualAddress + pSec->PointerToRawData;
            }
            //下一个区段地址
            pSec++;
        }
    }
    // DWORD FOAtoRVA(DWORD dwFOA)
    // {
    // 
    // }
    
    void TravseralExportTable()
    {
        //找到导出表
        DWORD dwExportRVA =
            g_pNt->OptionalHeader.DataDirectory[0].VirtualAddress;
        //获取在文件中的位置
        PIMAGE_EXPORT_DIRECTORY pExport =
            (PIMAGE_EXPORT_DIRECTORY)(RVAtoFOA(dwExportRVA) + g_pFileImageBase);
    
        //模块名字
        char* pName = (char*)(RVAtoFOA(pExport->Name) + g_pFileImageBase);
        printf("%s\n", pName);
        //地址表中的个数
        DWORD dwCountOfFuntions = pExport->NumberOfFunctions;
        //名称表中的个数
        DWORD dwCountOfNames = pExport->NumberOfNames;
    
        //地址表地址
        PDWORD pAddrOfFuntion = (PDWORD)(RVAtoFOA(pExport->AddressOfFunctions) + g_pFileImageBase);
        //名称表地址
        PDWORD pAddrOfName = (PDWORD)(RVAtoFOA(pExport->AddressOfNames) + g_pFileImageBase);
        //序号表地址
        PWORD pAddrOfOrdial = (PWORD)(RVAtoFOA(pExport->AddressOfNameOrdinals) + g_pFileImageBase);
        //base值
        DWORD dwBase = pExport->Base;
        //遍历地址表中的元素
        for (int i = 0; i < dwCountOfFuntions; i++)
        {
            //地址表中可能存在无用的值(就是为0的值)
            if (pAddrOfFuntion[i] == 0)
            {
                continue;
            }
            //根据序号表中是否有值地址表的下标值,
            //来判断是否是名称导出
            bool bRet = false;
            for (int j = 0; j < dwCountOfNames; j++)
            {
                if (i == pAddrOfOrdial[j])
                {
                    //取出名称表中的名称地址RVA
                    DWORD dwNameRVA = pAddrOfName[j];
                    char* pFunName = (char*)(RVAtoFOA(dwNameRVA) + g_pFileImageBase);
                    printf("%04d  %s  0x%08x\n", i + dwBase, pFunName, pAddrOfFuntion[i]);
                    bRet = true;
                    break;
                }
            }
            if (!bRet)
            {
                //序号表中没有,说明是以序号导出的
                printf("%04d  %08X\n", i + dwBase, pAddrOfFuntion[i]);
            }
    
        }
    
    }
    void TravseralImportTable()
    {
        //找到导入表
        DWORD dwImpotRVA = g_pNt->OptionalHeader.DataDirectory[1].VirtualAddress;
        //在文件中的位置
        DWORD dwImportInFile = (DWORD)(RVAtoFOA(dwImpotRVA) + g_pFileImageBase);
        PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwImportInFile;
    
        //遍历每一个导入表
        while (pImport->Name)
        {
            //函数名称地址
            PIMAGE_THUNK_DATA pFirsThunk =
                (PIMAGE_THUNK_DATA)(RVAtoFOA(pImport->FirstThunk) + g_pFileImageBase);
            //模块名
            char* pName = (char*)(RVAtoFOA(pImport->Name) + g_pFileImageBase);
            printf("导入模块名字%s\n", pName);
            while (pFirsThunk->u1.AddressOfData)
            {
                //判断导入方式
                if (IMAGE_SNAP_BY_ORDINAL32(pFirsThunk->u1.AddressOfData))
                {
                    //说明是序号导入(低16位是其序号)
                    printf("\t\t%04X \n", pFirsThunk->u1.Ordinal & 0xFFFF);
                }
                else
                {
                    //名称导入
                    PIMAGE_IMPORT_BY_NAME pImportName =
                        (PIMAGE_IMPORT_BY_NAME)(RVAtoFOA(pFirsThunk->u1.AddressOfData) + g_pFileImageBase);
                    printf("\t\t%04X %s \n", pImportName->Hint, pImportName->Name);
                }
                //
                pFirsThunk++;
            }
            pImport++;
        }
    }
    void ShowResouceTable()
    {
        //找到资源表
        DWORD dwResRVA = 
            g_pNt->OptionalHeader.DataDirectory[2].VirtualAddress;
        DWORD dwResFOA = (DWORD)(RVAtoFOA(dwResRVA) + g_pFileImageBase);
        PIMAGE_RESOURCE_DIRECTORY pRes = (PIMAGE_RESOURCE_DIRECTORY)dwResFOA;
    
        //第一层(种类)
        //种类个数
        DWORD dwCountOfResType =
            pRes->NumberOfIdEntries + pRes->NumberOfNamedEntries;
    
        for (int i = 0; i < dwCountOfResType;i++)
        {
            PIMAGE_RESOURCE_DIRECTORY_ENTRY pResEntry = 
                (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes + 1);
    
            //判断这种资源是字符串还是ID
            if (pResEntry->NameIsString)
            {
                //如果是字符串,NameOffset保存的就是这个字符串相对于资源段起始位置的偏移量
                //得到名字字符串的FOA
                //DWORD dwNameFOA = (DWORD)(RVAtoFOA(pResEntry->NameOffset) + g_pFileImageBase);
                DWORD dwNameFOA = (DWORD)(RVAtoFOA(pResEntry->NameOffset) + (DWORD)pRes);
                //NameOffset所指向的结构体是IMAGE_RESOURCE_DIR_STRING_U类型
                //这里保存了字符串的长度和起始位置
                PIMAGE_RESOURCE_DIR_STRING_U pName = (PIMAGE_RESOURCE_DIR_STRING_U)dwNameFOA;
                //这里的字符串不是以0结尾的,所以需要拷贝出来加上‘\0’结尾后再打印
                WCHAR *pResName = new WCHAR[pName->Length + 1]{};
                memcpy(pResName, pName, (pName->Length)*sizeof(WCHAR));
                //因为是WCHAR,所以用wprintf
                wprintf(L"%s\n", pResName);
                //释放内存
                delete[] pResName;
            }
            else   //id
            {
                char* arryResType[] = { "", "鼠标指针(Cursor)", "位图(Bitmap)", "图标(Icon)", "菜单(Menu)"
                    , "对话框(Dialog)", "字符串列表(String Table)", "字体目录(Font Directory)", "字体(Font)", "快捷键(Accelerators)"
                    , "非格式化资源(Unformatted)", "消息列表(Message Table)", "鼠标指针组(Croup Cursor)", "", "图标组(Group Icon)", ""
                    , "版本信息(Version Information)" };
                if (pResEntry->Id < 17)
                {
                    printf("%s\n", arryResType[pResEntry->Id]);
                }
                else
                {
                    printf("%04X\n", pResEntry->Id);
                }
    
                //判断是否有下一层
                if (pResEntry->DataIsDirectory)
                {
                    DWORD dwResSecond = (DWORD)pRes + pResEntry->OffsetToDirectory;
                    PIMAGE_RESOURCE_DIRECTORY pResSecond = (PIMAGE_RESOURCE_DIRECTORY)dwResSecond;
                    //第二层个数
                    DWORD dwCountOfSecond = 
                        pResSecond->NumberOfIdEntries + pResSecond->NumberOfNamedEntries;
                    //遍历每一个资源
                    for (int iSecond = 0; iSecond < dwCountOfSecond;iSecond++)
                    {
                        PIMAGE_RESOURCE_DIRECTORY_ENTRY pResSecondEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResSecond + 1);
    
                        //判断这种资源是字符串还是ID
                        if (pResEntry->NameIsString)
                        {
                            //如果是字符串,NameOffset保存的就是这个字符串的RVA
                            //得到名字字符串的FOA
                            DWORD dwNameFOA = (DWORD)(RVAtoFOA(pResEntry->NameOffset) + g_pFileImageBase);
                            //NameOffset所指向的结构体是IMAGE_RESOURCE_DIR_STRING_U类型
                            //这里保存了字符串的长度和起始位置
                            PIMAGE_RESOURCE_DIR_STRING_U pName = (PIMAGE_RESOURCE_DIR_STRING_U)dwNameFOA;
                            //这里的字符串不是以0结尾的,所以需要拷贝出来加上‘\0’结尾后再打印
                            WCHAR *pResName = new WCHAR[pName->Length + 1]{};
                            memcpy(pResName, pName, (pName->Length)*sizeof(WCHAR));
                            wprintf(L"%s\n", pResName);
                            delete[] pResName;
                        }
                        else   //id
                        {
                            printf("%04X\n", pResEntry->Id);
                        }
                        //判断有没有下一层
                        //第三层
                        if (pResSecondEntry->DataIsDirectory)
                        {
                            //第三层的起始位置
                            DWORD dwResThrid = 
                                (DWORD)pRes + pResSecondEntry->OffsetToDirectory;                       
                            PIMAGE_RESOURCE_DIRECTORY pResThrid = (PIMAGE_RESOURCE_DIRECTORY)dwResThrid;
    
                            PIMAGE_RESOURCE_DIRECTORY_ENTRY pResThridEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResThrid + 1);
                            //第三层,已经是最后一层,使用PIMAGE_RESOURCE_DIRECTORY_ENTRY中的
                            //OffsetToData成员,得到PIMAGE_RESOURCE_DATA_ENTRY结构的位置
                            PIMAGE_RESOURCE_DATA_ENTRY pResData =
                                (PIMAGE_RESOURCE_DATA_ENTRY)(pResThridEntry->OffsetToData + (DWORD)pRes);
                            //资源的RVA和Size
                            DWORD dwResDataRVA = pResData->OffsetToData;
                            DWORD dwResDataSize = pResData->Size;
                            //PIMAGE_RESOURCE_DATA_ENTRY中的OffsetToData是个RVA
                            DWORD dwResDataFOA = (DWORD)(RVAtoFOA(dwResDataRVA) + g_pFileImageBase);
                            //资源的二进制数据
                            //遍历打印资源的二进制数据
                            PBYTE pData = (PBYTE)dwResDataFOA;
                            for (int iData = 0; iData < dwResDataSize; iData++)
                            {
                                if (iData % 16 == 0 && iData != 0)
                                {
                                    printf("\n");
                                }
                                printf("%02X ", pData[iData]);
                            }
                        }
                        //下一个资源
                        pResSecondEntry++;
                    }
                }           
            }
            //下一种资源
            pResEntry++;
        }
    }
    void ShowRelocTable()
    {
        typedef struct _OFFSET_TYPE 
        {
            WORD offset : 12; //本页的偏移量
            WORD type : 4;    //重定位类型(3)
        }OFFSET_TYPE, *POFFSET_TYPE;
        //重定位表RVA
        DWORD dwRelocRVA = g_pNt->OptionalHeader.DataDirectory[5].VirtualAddress;
        //是否为空
        if (!dwRelocRVA)
        {
            printf("没有重定位表\n");
            return;
        }
        //重定位表在文件中的地址
        PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)(RVAtoFOA(dwRelocRVA) + g_pFileImageBase);
        
        //循环重定位表
        //如果SizeOfBlock为0,说明没有需要重定位的数据了
        while (pReloc->SizeOfBlock)
        {
            //当前重定位页RVA
            printf("%08X\n\n", pReloc->VirtualAddress);
            //这一页一共有多少个重定位块(即多少个需要重定位的数据)
            DWORD dwCount = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
            //指向重定位块地址
            POFFSET_TYPE pOffset = (POFFSET_TYPE)(pReloc + 1);
            //遍历每一个重定位块
            for (int i = 0; i < dwCount;i++)
            {
                //在这一页中的位置地址RVA
                DWORD dwRelocDataRVA = pReloc->VirtualAddress + pOffset->offset;
                //转成FOA
                DWORD dwRelocDataFOA = (DWORD)(RVAtoFOA(dwRelocDataRVA) + g_pFileImageBase);
                //实际需要重定位的数据地址是个VA
                DWORD dwRealDataVA = *(DWORD*)dwRelocDataFOA;
                //转成RVA,得到FOA
                DWORD dwRealDataRVA = dwRealDataVA - g_pNt->OptionalHeader.ImageBase;           
                DWORD dwRealDataFOA = (DWORD)(RVAtoFOA(dwRealDataRVA) + g_pFileImageBase);
                //需要重定位的具体数据(字节数不确定)
                DWORD dwData = *(DWORD*)dwRealDataFOA;
    
                printf("需要重定位的第%d个数据 RVA:%08X  VA:%08X  DATA:%08X\n", 
                    i + 1, dwRelocDataRVA, dwRealDataVA,dwData);
                //下一个重定位数据位置
                pOffset++;
            }
            
            //下一页
            pReloc = 
                (PIMAGE_BASE_RELOCATION)(pReloc->SizeOfBlock + (DWORD)pReloc);
        }
    
    }
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        ReadFileToMem(L"RVAtoFOA.exe");
        IsPEFile();
    //  TravseralExportTable();
    //  TravseralImportTable();
    //  
    //      typedef void(*PF)();
    //      HMODULE hMod = LoadLibrary(L"TestDll.dll");
    //      PF fA = (PF)GetProcAddress(hMod, "funA");
    //      PF fB = (PF)GetProcAddress(hMod, (char*)0x10);
    //  
    //      fA();
    //      fB();
        //ShowResouceTable();
        ShowRelocTable();
        return 0;
    }
    

    =====================
    // TLS解析

    DllTest.h

    #pragma once
    extern "C" __declspec(dllexport)
    void funB();
    extern "C" __declspec(dllexport)
    void funA();
    void funC();
    

    Export.def

    EXPORTS
    funA @13
    funB @16
    funC @17 noname
    gg   @15
    

    #include "stdafx.h"
    #include <windows.h>
    
    #include <delayimp.h>
    #pragma comment(lib, "Delayimp.lib")
    
    //自己的dll
    #include "DllTest.h"
    #pragma comment(lib,"TestDll.lib")
    
    #pragma comment(linker, "/INCLUDE:__tls_used")
    
    // TLS变量
    __declspec (thread) int  g_nNum = 0x11111111;
    __declspec (thread) char g_szStr[] = "TLS g_nNum = 0x%p ...\r\n";
    // TLS回调函数A
    void NTAPI t_TlsCallBack_A(PVOID DllHandle, DWORD Reason, PVOID Red) {
        
        if (DLL_THREAD_ATTACH == Reason) // 如果线程退出则打印信息
            printf("t_TlsCallBack_A -> ThreadDetach!\r\n");
        return;
    }
    // TLS回调函数B
    void NTAPI t_TlsCallBack_B(PVOID DllHandle, DWORD Reason, PVOID Red) {
        printf("t_TlsCallBack_B -> ThreadDetach!\r\n");
    //  if (DLL_THREAD_DETACH == Reason) // 如果线程退出则打印信息
    //      printf("t_TlsCallBack_B -> ThreadDetach!\r\n");
        return;
    }
    #pragma data_seg(".CRT$XLB")
    PIMAGE_TLS_CALLBACK p_thread_callback[] = {
        t_TlsCallBack_A,
        t_TlsCallBack_B,
        NULL };
    #pragma data_seg()
    
    DWORD WINAPI fun(LPVOID)
    {
        printf("-----");
        return 0;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        //CreateThread(0, 0, fun, 0, 0, 0);
        
        //Sleep(100);
        funA();
        //system("pause");
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:PE文件简单解析

          本文链接:https://www.haomeiwen.com/subject/nkwlaftx.html