吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3966|回复: 11
收起左侧

[其他原创] [造轮子系列]手动打造一个PE分析工具

  [复制链接]
低调的菜鸡 发表于 2019-12-20 22:04
本帖最后由 低调的菜鸡 于 2019-12-20 22:09 编辑
十分感谢版主大大给我的上一篇帖子加了优秀,这次为了回报坛友,就借着发帖的机会,跟各位小伙伴们分享一下PE结构

引言

​        一提起LoadPE,或者PEID,相信在座的各位不管是大牛还是和我一样新入行的小白都特别熟悉,那么,今天我就手把手的跟大家一起造个轮子,亲手打造一个PE解析工具,同时也和大家分享一下PE文件结构相关的知识。界面仿的LoadPE,全程MFC开发

​        为什么用MFC呢,因为不想在界面上下功夫,我们主要是学PE的结构,MFC的界面丑是丑了点,但是能看,各位大佬轻喷,接下来我们就进入今天的主题吧

一、程序运行效果

主窗口.png
区段表窗口.png
目录表窗口.png

二、PE结构

请注意:这个结构特别重要

  1. 为什么要了解PE结构?

    • PE文件格式自古以来都是Windows软件安全的一个非常重要的知识点,不管是加壳脱壳,IAT注入,HOOK寻找文件偏移,都离不开这个结构。它描述了程序运行的所有信息,什么入口点啦,导入导出表啦,资源文件啦,几乎你所需要的一切,都能在这个结构里找到。当然,如果熟练掌握了这个结构,以后在Windows逆向的过程中,必然事半功倍
  2. PE到底是什么?

    • PE文件格式是微软Windows NT内核系列系统和Win32子集中可执行的二进制文件格式。我们加载的用的DLL模块,以及我们能跑起来的EXE程序,都必须遵从这个格式。
    • 如果简单说的话,PE其实就是可执行文件中,区段前的一堆二进制代码,存放着可执行文件里面所有的信息。
  3. PE结构详解

    • 大致描述:这里只是概述一下有哪些东西,我们需要用到什么

      • PE文件从文件开始,分别有3大数据块

      • MS-DOS头:为了兼容dos,没什么用,现在已经成为PE文件的标配。

        • 重要的字段:WORD   e_magic;                     // Magic number 0x5A4D

        ​                                        LONG   e_lfanew;                    // PE头的偏移,用于找到PE头的位置

      • PE头:

        • 标志位:标志是否为一个PE文件
        • PE文件头:包含了区段数量,运行平台,文件类型,文件创建时间等信息
        • PE可选头:包含了程序运行入口的RVA,程序默认载入基地址,目录表等信息
        • 目录表(非常重要):我们的导入、导出表以及IAT表的地址就存放在这里
      • 区段表:里面存放着我们加载入内存中的区段信息,比如数据段,代码段等

    • 详细解释:这里的结构体代码详细描述了上述结构

      • MS-DOS头:
      //DOS头
      //typedef struct _IMAGE_DOS_HEADER {                // DOS .EXE header
      //                WORD   e_magic;                     // Magic number 0x5A4D
      //                WORD   e_cblp;                      // 文件末页字节数
      //                WORD   e_cp;                        // 文件页数
      //                WORD   e_crlc;                      // 重定位项的数目
      //                WORD   e_cparhdr;                   // 区段中头部大小
      //                WORD   e_minalloc;                  // 最小内存附加段需求
      //                WORD   e_maxalloc;                  // 最大内存附加段需求
      //                WORD   e_ss;                        // 初始SS值
      //                WORD   e_sp;                        // 初始SP值
      //                WORD   e_csum;                      // 校验和
      //                WORD   e_ip;                        // 初始IP值(程序入口点)
      //                WORD   e_cs;                        // 初始CS值
      //                WORD   e_lfarlc;                    // 重定位表偏移
      //                WORD   e_ovno;                      // 代码附加数
      //                WORD   e_res[4];                    // Reserved words
      //                WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
      //                WORD   e_oeminfo;                   // OEM information; e_oemid specific
      //                WORD   e_res2[10];                  // Reserved words
      //                LONG   e_lfanew;                    // PE头的偏移
      //} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
      • PE头
      //PE文件头(PE头)
      //typedef struct _IMAGE_FILE_HEADER {
      //                WORD    Machine;                                                //运行平台,一般为IMAGE_FILE_MACHINE_I386
      //                WORD    NumberOfSections;                                //区段的数量
      //                DWORD   TimeDateStamp;                                        //文件的创建时间
      //                DWORD   PointerToSymbolTable;                        //符号表指针,一般为0
      //                DWORD   NumberOfSymbols;                                //符号表中符号的数量
      //                WORD    SizeOfOptionalHeader;                        //在IMAGE_FILE_HEADER结构后面的扩展头大小,一般为0x00E0
      //                WORD    Characteristics;                                //文件属性,EXE文件(IMAGE_FILE_EXECUTABLE_IMAGE)为0x010F,DLL(IMAGE_FILE_DLL)为0x210
      //} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
      • PE文件头

        //PE文件头(PE头)
        //typedef struct _IMAGE_FILE_HEADER {
        //                WORD    Machine;                                                //运行平台,一般为IMAGE_FILE_MACHINE_I386
        //                WORD    NumberOfSections;                                //区段的数量
        //                DWORD   TimeDateStamp;                                        //文件的创建时间
        //                DWORD   PointerToSymbolTable;                        //符号表指针,一般为0
        //                DWORD   NumberOfSymbols;                                //符号表中符号的数量
        //                WORD    SizeOfOptionalHeader;                        //在IMAGE_FILE_HEADER结构后面的扩展头大小,一般为0x00E0
        //                WORD    Characteristics;                                //文件属性,EXE文件(IMAGE_FILE_EXECUTABLE_IMAGE)为0x010F,DLL(IMAGE_FILE_DLL)为0x210
        //} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
      • PE可选头

        //PE可选头
        //typedef struct _IMAGE_OPTIONAL_HEADER {
        //        //
        //        // Standard fields.
        //        //
        //
        //                WORD    Magic;                                        //文件类型标识(普通可执行映像0x010B、ROM镜像为0x0170、PE32+为0x020B)
        //                BYTE    MajorLinkerVersion;                //链接器的主版本号。
        //                BYTE    MinorLinkerVersion;                //链接器的子版本号。
        //                DWORD   SizeOfCode;                                //所有IMAGE_SCN_CNT_CODE属性的区段总大小,此大小在计算时按照磁盘扇区字节数的整数倍计算。
        //                DWORD   SizeOfInitializedData;        //已初始化的数据块大小。
        //                DWORD   SizeOfUninitializedData;//未初始化的数据块大小,装载程序需要在虚拟地址空间中为这些
                                                                                     //数据保留空间,但是这些块在磁盘中并不占用任何空间(在程序
                                                                                     //运行之前没有指定的值),未初始化的数据通常都位于一个名称为.bbs的区段中。
        //                DWORD   AddressOfEntryPoint;        //程序执行入口的RAV,一般指向运行时的库代码,然后再调用main
                                                                                     //在DLL文件中,这个入口
                                                                                     //点有可能在进程初始化、进程关闭、线程创建与线程关闭时被调用,
                                                                                     //并且鉴于DLL文件的特殊性,这个入口点在DLL文件中可以被置为0
        //                DWORD   BaseOfCode;                                //代码段的起始RVA,这个值通常为0x00001000
        //                DWORD   BaseOfData;                                //数据段的起始RVA,数据段通常位于PE文件头与代码段之后
        //
        //                //
        //                // NT additional fields.
        //                //
        //
        //                DWORD   ImageBase;                                        //文件在内存中的首选装入,地址加载器将试图在此地址装
                                                                                             //入这个映像文件,如果载入成功,则装载器将跳过应用基
                                                                                             //址重定位的步骤,如果此地址被占用,则加载器会重新在
                                                                                             //正确对齐的合法地址中选择一个作为实际装载地址。
        //                DWORD   SectionAlignment;                        //映像文件在被装入内存时的区段对齐大小
        //                DWORD   FileAlignment;                                //映像文件在磁盘上的区段对齐大小
        //                WORD    MajorOperatingSystemVersion;//要求操作系统最低版本的主版本号
        //                WORD    MinorOperatingSystemVersion;//要求操作系统最低版本的子版本号
        //                WORD    MajorImageVersion;                        //此可执行文件的主版本号,此版本号由程序作者指定
                                                                                             //(它与后一个字段成对使用,可以被置为0,可以通过连接器开关 / VERSION控制)
        //                WORD    MinorImageVersion;                        //此可执行文件的子版本号,此版本号由程序作者指定
        //                WORD    MajorSubsystemVersion;                //要求最低子系统的主版本号(与后一个字段成对使用,一
                                                                                             //般情况下其值为4,可以通过连接器开关 / SUBSYSTEM控制)。
        //                WORD    MinorSubsystemVersion;                //要求最低子系统的子版本号
        //                DWORD   Win32VersionValue;                        //这是一个保留值,且必须为0x00000000
        //                DWORD   SizeOfImage;                                //映像文件装入内存后的总大小(从Image Base
                                                                                             //到最后一个区段的总大小)
        //                DWORD   SizeOfHeaders;                                //是MS-DOS头、PE头、区块表的尺寸之和
        //                DWORD   CheckSum;                                        //映像文件的校验和(在内核模式的驱动和系统dll中必须为有效值)
        //                WORD    Subsystem;                                        //可执行文件所期望的子系统值IMAGE_SUBSYSTEM_WINDOWS_GUI
        //                WORD    DllCharacteristics;                        //DllMain()函数何时被调用,默认为0
        //                DWORD   SizeOfStackReserve;                        //在EXE文件中,为线程保留的堆栈大小
        //                DWORD   SizeOfStackCommit;                        //在EXE文件中,栈初始内存大小(默认4KB)
        //                DWORD   SizeOfHeapReserve;                        //在EXE文件中,为进程默认堆保留的内存(默认1MB)
        //                DWORD   SizeOfHeapCommit;                        //在EXE文件中,每次指派给堆的内存大小(默认4KB)
        //                DWORD   LoaderFlags;                                //与调试有关,默认为0
        //                DWORD   NumberOfRvaAndSizes;                //数据目录成员的数量,一般为0x00000010(16个)
        
                     //数据目录表
        //                IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
        //} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
        • 数据目录表
        //数据目录表
        //typedef struct _IMAGE_DATA_DIRECTORY {
        //                DWORD   VirtualAddress;                        // 数据起始块的RVA地址
        //                DWORD   Size;                                        // 数据块的长度
        //} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
      • 区段表

      //区段表
      //typedef struct _IMAGE_SECTION_HEADER {
      //                BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];                //区段名,一般情况下以.开头
      //                union {
      //                        DWORD   PhysicalAddress;                                //
      //                        DWORD   VirtualSize;                                        //
      //                } Misc;                                                                                //实际被使用的区段大小
      //                DWORD   VirtualAddress;                                                //此区段载入内存后的RAV                
      //                DWORD   SizeOfRawData;                                                //此区段在磁盘中的体积,这个地址是按照文件页对齐
                                                                                                             //的(恒为PE头结构中FileAlignment字段的整数倍)
      //                DWORD   PointerToRawData;                                        //此区段在文件中的偏移
      //                DWORD   PointerToRelocations;                                //此区段重定位表的偏移地址,它指向IMAGE_RELOCATION结构数组
      //                DWORD   PointerToLinenumbers;                                //行号表在文件中的偏移
      //                WORD    NumberOfRelocations;                                //此区段重定位表项的数量
      //                WORD    NumberOfLinenumbers;                                //行号表项的数量
      //                DWORD   Characteristics;                                        //区段属性,用以描述此区段的读写情况、状态等属性
                                                                                                             //IMAGE_SCN_MEM_READ(可读)
      //} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
  • 好啦,我们对PE结构的介绍就到这里啦,如果想深究,大家可以下来细品我的工程代码和我上述的教程这里就不多说啦,有个印象就成,接下来咱们就来获取PE结构吧。

三、关键代码

​        这里我封装了一个C语言的函数,可以直接把PE文件的这些信息存放到内存中

bool CLoadPEDlg::GetFilePEInfo(const TCHAR * pszPathName)
{
        // 获取当前文件的所有信息,保存到类的结构体中

        //打开文件
        FILE *pFile = NULL;
        _tfopen_s(&pFile, pszPathName, _T("rb+"));
        if (pFile == NULL)
        {
                MessageBox(_T("文件打开失败!!"));
                return false;
        }

        //读取DOS头的信息
        fread(&m_stDosHeader, sizeof(IMAGE_DOS_HEADER), 1, pFile);

        //定义NT头结构体
        IMAGE_NT_HEADERS stNtHeader;
        //获取NT头信息
        fseek(pFile, m_stDosHeader.e_lfanew, SEEK_SET);
        fread(&stNtHeader, sizeof(IMAGE_NT_HEADERS), 1, pFile);

        //如果不是PE文件,则提示重新获取
        if (m_stDosHeader.e_magic != IMAGE_DOS_SIGNATURE || stNtHeader.Signature != IMAGE_NT_SIGNATURE)
        {
                MessageBox(_T("该文件不是可解析的PE文件,请重试"),_T("ERROR"));
                fclose(pFile);
                return false;
        }

        //获取文件头信息和可选头信息
        m_stFileHeader = stNtHeader.FileHeader;
        m_stOptHeader = stNtHeader.OptionalHeader;

        //循环获取区段表
        m_vecSections.clear();
        IMAGE_SECTION_HEADER stSectionHeader;
        for (int i = 0; i < m_stFileHeader.NumberOfSections; i++)
        {
                fread(&stSectionHeader, sizeof(IMAGE_SECTION_HEADER), 1, pFile);
                m_vecSections.push_back(stSectionHeader);
        }
        fclose(pFile);
        return true;
}

可以看到,我这里是用的文件读写直接做的,其实PE结构也就这样,把文件的相应位读出来,就可以根据上述结构体直接解析啦。

四、工程源码及下载


        PE工具源码: LoadPE.zip (214.04 KB, 下载次数: 109)
        编译环境:vs2017 + win10
        偷偷告诉大家,这次的源码可是我用上次的清理工具清理过的哟,就这么大,刺激不,哈哈哈

五、结语


        怕大家理解困难,所以这里并没有进行导入导出表的解析,功能可能会在下面一次帖子加上,主要是做界面太麻烦了,耗费时间,哈哈。看这次的反应吧,如果大家不喜欢,那可能就只有这一个版本了。

免费评分

参与人数 10吾爱币 +15 热心值 +8 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
marlborogolo + 1 + 1 谢谢@Thanks!
qaz003 + 2 谢谢@Thanks!
被封号的萌新 + 1 唱跳rap,独缺篮球!
SherryMefum + 1 + 1 谢谢@Thanks!
为海尔而战 + 1 + 1 我很赞同!
moranyuyan + 1 我很赞同!
苏紫方璇 + 6 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
erh + 1 + 1 我很赞同!
冥月影 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

冥月影 发表于 2019-12-20 22:20
不懂这个,但还是支持一下。。。
518 发表于 2019-12-20 22:50
erh 发表于 2019-12-20 22:59
diyikuai 发表于 2019-12-20 23:11
这真是高手所为。
moranyuyan 发表于 2019-12-20 23:13
谢谢分享
SherryMefum 发表于 2019-12-20 23:26
MFC很多第三方UI库,还可以自绘,但没什么必要,这种工具效率为主。
蒂兰圣雪 发表于 2019-12-21 00:18
不明觉厉支持下!
syrmb 发表于 2019-12-21 00:19
用c画界面会哭
growing88 发表于 2019-12-21 00:57
感谢楼主,谢谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-16 19:36

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表