吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 802|回复: 0
收起左侧

[C&C++ 原创] pe内容解析程序学习

[复制链接]
xiaoxiaoY520 发表于 2023-7-25 15:20

学习一个未知的东西需要一个强烈且明确的目的推动!学习逆向以来,一直使用的现有工具去解析pe结构,在一次偶然的机会,遇到了iat重建问题,手工重建多次未果后决定系统的了解一下pe结构。光从书面去了解我觉得应该是不够的,决定手动去写一个程序来解析pe头,也能对pe各字段的作用有更深的了解。
查阅了大量的资料,大多是文字表述,篇幅很长,内容雷同(天下文章一大抄,唉...),很难让人有耐心读下去。我也是抽了很多碎片时间逛了多次某sdn一点一点的读完了(真的很煎熬,虽然很拉跨,但不能否认前人对知识的分享),通过梳理文字,将pe头的结构通过图的形式展示出来(一下子就直观清晰了),图如下:

图片.png

通过该图,尝试写出了一个程序,解析pe头部内容,后续计划通过头部内容解析出IAT表的内容,打算后面写个界面出来,所以目前只打印了dos头和节表看一下效果,实际数据已经读取了。程序如下(很拉跨):
#include <iostream>
#include <fstream>
#include <cstdint>
#include <iomanip>
#include <sstream>
#include <vector>
using namespace std;

// DOS 头数据结构 (IMAGE_DOS_HEADER)
struct DosHeader {
    char e_magic[2];     // 魔术数字 (MZ)
    uint16_t e_cblp;     // 文件最后页字节数
    uint16_t e_cp;       // 文件页数
    uint16_t e_crlc;     // 重定义元素个数
    uint16_t e_cparhdr;  // 头部尺寸,以段落为单位
    uint16_t e_minalloc; // 所需最小附加段
    uint16_t e_maxalloc; // 所需最大附加段
    uint16_t e_ss;       // 初始ss值
    uint16_t e_sp;       // 初始sp值
    uint16_t e_csum;     // 校验和
    uint16_t e_ip;       // 初始ip值
    uint16_t e_cs;       // 初始cs值(相对偏移量)
    uint16_t e_lfarlc;   // 重分配表文件地址
    uint16_t e_ovno;     // 覆盖号
    uint16_t e_res[4];   // 保留字
    uint16_t e_oemid;    // OEM 标识符
    uint16_t e_oeminfo;  // OEM 信息
    uint16_t e_res2[10]; // 保留字
    int32_t e_lfanew;    // pe头偏移量
};


//file_header
struct FileHeader {
    uint16_t Machine;//运行平台
    uint16_t NumberOfSections;//区块表的个数
    uint32_t TimeDataStamp;//文件创建时间,是从1970年至今的秒数
    uint32_t PointerToSymbolicTable;//指向符号表的指针
    uint32_t NumberOfSymbols;//符号表的数目
    uint16_t SizeOfOptionalHeader;//IMAGE_NT_HEADERS结构中OptionHeader成员的大小,对于win32平台这个值通常是0x00e0
    uint16_t Characteristics;//文件的属性值
};

// 数据目录条目结构 (IMAGE_DATA_DIRECTORY)
struct DataDirectoryEntry {
    uint32_t virtualAddress; // 虚拟地址,指向数据目录的位置
    uint32_t size;           // 数据目录的大小
};

struct directoryDataStruct {
    DataDirectoryEntry exportDirectory; //导出表
    DataDirectoryEntry importDirectory; //导入表
    DataDirectoryEntry resourceDirectory; //资源表
    DataDirectoryEntry exceptionDirectory; //异常表
    DataDirectoryEntry secirityDirectory; //安全性表
    DataDirectoryEntry relocationDirectory; //基址重定位表
    DataDirectoryEntry debugDirectory;   //调试信息表
    DataDirectoryEntry architectureDirectory; //体系结构相关表
    DataDirectoryEntry reservedDirectory; //全局指针寄存器表
    DataDirectoryEntry TLSDirectory;   //tls表
    DataDirectoryEntry configurationDirectory; //加载配置表
    DataDirectoryEntry boundImportDirectory;   //绑定导入表
    DataDirectoryEntry IATDirectory;  //IAT表
    DataDirectoryEntry delayDirectory;  //延迟导入表
    DataDirectoryEntry runTimeDirectory; //运行时描述符表

};

//optionalHeader(可选头)
struct optionalHeader {
    uint16_t    Magic;// 标志字, ROM 映像(0107h),普通可执行文件(010Bh)
    unsigned char    MajorLinkerVersion;// 链接程序的主版本号
    unsigned char    MinorLinkerVersion;// 链接程序的次版本号
    uint32_t   SizeOfCode;// 所有含代码的节的总大小
    uint32_t   SizeOfInitializedData;// 所有含已初始化数据的节的总大小
    uint32_t   SizeOfUninitializedData;// 所有含未初始化数据的节的大小
    uint32_t   AddressOfEntryPoint;// 程序执行入口RVA
    uint32_t   BaseOfCode;// 代码的区块的起始RVA
    uint32_t   BaseOfData;// 数据的区块的起始RVA

    // NT additional fields.    以下是属于NT结构增加的领域。
    uint32_t   ImageBase;// 程序的首选装载地址
    uint32_t   SectionAlignment;// 内存中的区块的对齐大小
    uint32_t   FileAlignment;// 文件中的区块的对齐大小
    uint16_t    MajorOperatingSystemVersion;// 要求操作系统最低版本号的主版本号
    uint16_t    MinorOperatingSystemVersion;// 要求操作系统最低版本号的副版本号
    uint16_t    MajorImageVersion;// 可运行于操作系统的主版本号
    uint16_t    MinorImageVersion;// 可运行于操作系统的次版本号
    uint16_t    MajorSubsystemVersion;// 要求最低子系统版本的主版本号
    uint16_t    MinorSubsystemVersion;// 要求最低子系统版本的次版本号
    uint32_t   Win32VersionValue;// 莫须有字段,不被病毒利用的话一般为0
    uint32_t   SizeOfImage;// 映像装入内存后的总尺寸
    uint32_t   SizeOfHeaders;// 所有头 + 区块表的尺寸大小
    uint32_t   CheckSum;// 映像的校检和
    uint16_t    Subsystem;// 可执行文件期望的子系统
    uint16_t    DllCharacteristics;// DllMain()函数何时被调用,默认为 0
    uint32_t   SizeOfStackReserve;// 初始化时的栈大小
    uint32_t   SizeOfStackCommit;// 初始化时实际提交的栈大小
    uint32_t   SizeOfHeapReserve;// 初始化时保留的堆大小
    uint32_t   SizeOfHeapCommit;// 初始化时实际提交的堆大小
    uint32_t   LoaderFlags;// 与调试有关,默认为 0
    uint32_t   NumberOfRvaAndSizes;// 下边数据目录的项数,这个字段自Windows NT 发布以来一直是16
    directoryDataStruct Data_Directory; // 数据目录表
};


// PE 结构 (IMAGE_NT_HEADERS)
struct PeSignature {
    char signature[4];        // PE Signature (PE\0\0)
    FileHeader fileheader;
    optionalHeader optionalheader;
};

// 节表
typedef struct SectionTable {
    unsigned char Name[8];
    uint32_t VirtualSize; //指出实际的、被使用的区块的大小(也就是区块的数据没有对齐处理的实际大小)16H个
    uint32_t VirtualAddress; //该块装载到内存中的RVA
    uint32_t SizeOfRawData;  //该块在磁盘文件中所占的大小
    uint32_t PointerToRawData;//该块在磁盘文件中的偏移
    uint32_t PointerToRelocations;//在EXE文件中无意义
    uint32_t PointerToLinenumbers;
    uint16_t NumberOfRelocations;//由pointerToRelocations指向的重定位的数目
    uint16_t NumberOfLinenumbers;
    uint32_t Characteristics; //块属性
};

int main() {
    // 打开pe文件-二进制格式
    ifstream file("C:/Users/小鱼/Desktop/字符.exe", ios::binary);
    if (!file) {
        cerr << "文件打开失败!" << endl;
        return 1;
    }

    // 读取文件内容并保存到fileData容器中(保留数据,可能有其他功能开发需要)
    vector<unsigned char> fileData;
    unsigned char byte;
    while (file.get(reinterpret_cast<char&>(byte))) {
        fileData.push_back(byte);
    }
    //清除文件错误,否则seekg不生效
    file.clear();
    file.seekg(0, ios::beg); // 设置文件指针回到文件开头
    //cout << file.tellg() << endl;

    // 十六进制显示数据
    //十六进制格式写入流
    int count = 0;
    stringstream hexStream;
    for (const auto& byte : fileData) {
        hexStream << hex << setw(2) << setfill('0') << static_cast<int>(byte) << " ";
        count++;
        if (count % 16 == 0) {
            hexStream << endl;
        }
    }
    //以字符形式将流输出到dos窗口,便于对照文件内容
    //cout << hexStream.str() << endl;
   
    // 读取DOS头数据
    DosHeader dosHeader;
    file.read(reinterpret_cast<char*>(&dosHeader), sizeof(DosHeader));

    //检查是否存在MZ
    if (dosHeader.e_magic[0] != 'M' || dosHeader.e_magic[1] != 'Z') {
        cerr << "非法的DOS头格式!" << endl;
        file.close();
        return 1;
    }

    // 移动文件指针到pe头
    file.seekg(dosHeader.e_lfanew, ios::beg);
    // 读取pe结构
    PeSignature peStructs;
    file.read(reinterpret_cast<char*>(&peStructs), sizeof(PeSignature));
    // 检查PE魔术字符是否存在
    if (peStructs.signature[0] != 'P' || peStructs.signature[1] != 'E' ||
        peStructs.signature[2] != '\0' || peStructs.signature[3] != '\0') {
        cerr << "非法的PE格式!." << endl;
        file.close();
        return 1;
    }
   
    //显示文件当前指针
    //cout << file.tellg() << endl;

    //读取节表
    file.seekg(8, ios::cur);  //文件指针后移8个字节,绕过8个字符到节表处
    int sectionNum = (int)peStructs.fileheader.NumberOfSections;
    //cout << sectionNum << endl; // 显示节数
    // 使用 vector 动态创建 SectionTable 结构体数组
    vector<SectionTable> sectionTableArray(sectionNum);
    // 循环读取节表数据,写入vector结构体数组
    for (int i = 0; i < sectionNum; i++) {
        file.read(reinterpret_cast<char*>(&sectionTableArray), sizeof(SectionTable));
    }

    //输出DOS头信息
    cout << "DOS头信息:" << endl;
    cout << left << setw(15) << "头部:"<< dosHeader.e_magic << endl;
    cout << left<<setw(15)<<"e_cblp:" <<left<<setw(15)<<hex<<dosHeader.e_cblp<< endl;
    cout << left<<setw(15) << "e_cp:" << left << setw(15) << hex<<dosHeader.e_cp<< endl;
    cout << left << setw(15) << "e_crlc:" << left << setw(15) << hex<<dosHeader.e_crlc<<endl;
    cout << left << setw(15) << "e_cparhdr:" << left << setw(15) << hex<<dosHeader.e_cparhdr<< endl;
    cout << left << setw(15) << "e_minalloc:" << left << setw(15) << hex<<dosHeader.e_minalloc<<endl;
    cout << left << setw(15) << "e_maxalloc:" << left << setw(15) << hex<<dosHeader.e_maxalloc<<endl;
    cout << left << setw(15) << "e_ss:" << left << setw(15) << hex<<dosHeader.e_ss<< endl;
    cout << left << setw(15) << "e_sp:" << left << setw(15) << hex<<dosHeader.e_sp<< endl;
    cout << left << setw(15) << "e_csum:" << left << setw(15) << hex<<dosHeader.e_csum<< endl;
    cout << left << setw(15) << "e_ip:" << left << setw(15) << hex<<dosHeader.e_ip<< endl;
    cout << left << setw(15) << "e_cs:" << left << setw(15) << hex<<dosHeader.e_cs<<endl;
    cout << left << setw(15) << "e_lfarlc:" << left << setw(15) << hex<<dosHeader.e_lfarlc<< endl;
    cout << left << setw(15) << "e_ovno:" << left << setw(15) << hex<<dosHeader.e_ovno<<endl;
    cout << left << setw(15) << "e_res[4]:" << left << setw(15) << hex<<dosHeader.e_res<< endl;
    cout << left << setw(15) << "e_oemid:" << left << setw(15) << hex<<dosHeader.e_oemid<<endl;
    cout << left << setw(15) << "e_oeminfo:" << left << setw(15) << hex<<dosHeader.e_oeminfo<<endl;
    cout << left << setw(15) << "e_res2[10]:" << left << setw(15) << hex<<dosHeader.e_res2<< endl;
    cout << left << setw(15) << "e_lfanew:" << left << setw(15) << hex<<dosHeader.e_lfanew<<endl;
    cout << endl;


    // 输出节表数据
    cout << "节表数据:" << endl;
    for (const auto& section : sectionTableArray) {
        // 以结构体形式输出 SectionTable 数据
        cout << "Name: ";
        for (int i = 0; i < 8; i++) {
            cout << section.Name;
        }
        cout << endl;
        cout << "VirtualSize: " << section.VirtualSize << endl;
        cout << "VirtualAddress: 0x" << hex << section.VirtualAddress << endl;
        cout << "SizeOfRawData: " << dec << section.SizeOfRawData << " bytes" << endl;
        cout << "PointerToRawData: " << hex << section.PointerToRawData << endl;
        cout << "PointerToRelocations: " << hex << section.PointerToRelocations << endl;
        cout << "PointerToLinenumbers: " << hex << section.PointerToLinenumbers << endl;
        cout << "NumberOfRelocations: " << dec << section.NumberOfRelocations << endl;
        cout << "NumberOfLinenumbers: " << dec << section.NumberOfLinenumbers << endl;
        cout << "Characteristics: " << hex << section.Characteristics << endl;
        cout << endl;
    }

    // 在不再使用sectionTable数组时,释放内存
    file.close();
    return 0;
}

运行效果如下:

图片.png


免费评分

参与人数 4吾爱币 +10 热心值 +3 收起 理由
helian147 + 1 + 1 热心回复!
derangedrabbit + 1 用心讨论,共获提升!
海天一色001 + 1 + 1 我很赞同!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

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

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-24 21:34

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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