[原创]《32位PE解析、PE修改、导入表注入》(上传打印资源表代码)

您所在的位置:网站首页 pe修改注册表工具 [原创]《32位PE解析、PE修改、导入表注入》(上传打印资源表代码)

[原创]《32位PE解析、PE修改、导入表注入》(上传打印资源表代码)

2024-04-18 06:45| 来源: 网络整理| 查看: 265

混子目录

                                                 0x0 PE概述

PE文件的全称是Portable Executable,意为可移植的可执行的文件,常见的EXE、DLL、OCX、SYS、COM都是PE文件,我们比较熟悉的DLL和exe文件都是PE文件。了解PE文件格式有助于加深对操作系统的理解,掌握可执行文件的数据结构机器运行机制,对于逆向破解,加壳等安全方面方面的同学极其重要。

PE在二进制文件中就是一堆0和1,但是它是按一定的格式来存储的,下面我先从宏观上来给大家介绍。

PE分dos头、NT头(包含标准PE头和可选PE头(包含数据目录))、节表。

下面我上一张图给大家看一下:

dos头(方块1第一块),NT头(方块1第二块),标准PE头(方块2),可选PE头(方块3),数据目录(方块5)、节表(方块4)。

下面我按着这个编号来介绍。

0x1解析PE

首先我讲一下dos头,如图

微软定义如下:

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header     WORD   e_magic;          +0           // Magic number  DOS签名 4D5A "MZ"    WORD   e_cblp;           +2           // Bytes on last page of file     WORD   e_cp;             +4           // Pages in file     WORD   e_crlc;           +6           // Relocations     WORD   e_cparhdr;        +8           // Size of header in paragraphs     WORD   e_minalloc;       +10           // Minimum extra paragraphs needed     WORD   e_maxalloc;       +12           // Maximum extra paragraphs needed     WORD   e_ss;             +14           // Initial (relative) SS value     WORD   e_sp;             +16           // Initial SP value     WORD   e_csum;           +18           // Checksum     WORD   e_ip;             +20           // Initial IP value     WORD   e_cs;             +22           // Initial (relative) CS value     WORD   e_lfarlc;         +24           // File address of relocation table     WORD   e_ovno;           +26           // Overlay number     WORD   e_res[4];         +28 +32 +34 +36           // Reserved words     WORD   e_oemid;          +38           // OEM identifier (for e_oeminfo)     WORD   e_oeminfo;        +40           // OEM information; e_oemid specific     WORD   e_res2[10];                 // Reserved words     LONG   e_lfanew;         +60     // File address of new exe header  指向PE头 3Ch       } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

虽然字段比较多,但是我们初学只要知道2个字段就ok了——   WORD   e_magic和LONG   e_lfanew;    e_magic字段是标记,用途就是标记这个文件是什么类型的文件,比如:exe,dll,sys文件都是4D5A开头的。

MZ是一个大佬名字的简称,不过他貌似已经挂了???xxxx 反正知道mz标记就是代表可执行文件就ok了。

lfanew字段就是标记NT头在文件里的偏移,文件头+lfanew就是NT头的位置(中间有一段没用的数据叫 dos  sub 下文增加节会讲)。

0x50 0x45是PE的标志 ,因为“PE”的ascll码就是0x50 0x45。

NT头

NT头包含标准PE头和可选PE头,文件的很多信息都在这里面。

Signature字段里存的就是pe标记,注意宽度是4,然后就是标准PE头。

标准PE头

标准PE头一共是20个字节,下面我讲一下字段的用处。

typedef struct _IMAGE_FILE_HEADER {         * WORD   [60]+4 Machine; 运行平台        * WORD    NumberOfSections; [60]+6 文件的节数目        * DWORD   TimeDateStamp;   [60]+8 文件创建日期和时间         DWORD   PointerToSymbolTable;   [60]+12  指向符号表(用于调试)         DWORD   NumberOfSymbols;  [60]+16 符号表中的符号数量(用于调试)        * WORD    SizeOfOptionalHeader;[60]+20  可选头的长度        * WORD    Characteristics; [60]+22  文件属性 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER

1.Machine字段,表示目标CPU 的类型。

几个常见的及其标识如下:

机器              标识     

Intel I386          14ch

MIPS R3000        162h

Alpha AXP          184h

Power PC           1F0h

MIPS R4000         184h

2.NumberOfSection,标识节的数目,关于节后面会详细讲。

3.TimeDateStamp

PE文件创建的时间,这个时间是指从1970年1月1日到创建该文件的所有的秒数。

4)PointerToSymbolTable。略

5)NumberOfSymbol。略

6)SizeOfOptionalHeader:紧跟着标准PE头后面的数据大小,这也是一个数据结构,它叫做可选PE头,其大小依赖于是64位还是32位文件。32位文件值通常是00E0h,对于64位值通常为00F0h。

7)Characteristics:文件属性,普通EXE文件这个字段值为010fh,DLL文件这个字段一般是0210h。

(标准pe头)

可选PE头

可选PE头,其大小依赖于是64位还是32位文件。32位文件值通常是00E0h,对于64位值通常为00F0h。

可选PE头字段多,但是重要的不多(加*的都很重要,不要慌,*是我加的)。

typedef struct _IMAGE_OPTIONAL_HEADER {         * WORD    Magic;  10B 32位PE 20B 64位PE 107 ROM映像[60]+24         BYTE    MajorLinkerVersion;  链接器版本号   [60]+26                          BYTE    MinorLinkerVersion;  链接器副版本号  [60]+27        * DWORD   SizeOfCode;  所有代码节的总和  该大小是基于文件对齐后的大小[60]+28        * DWORD   SizeOfInitializedData;  所有含已初始化数据的节的总大小  [60]+32        * DWORD   SizeOfUninitializedData;  所有含未初始化数据的节的大小    [60]+36        * DWORD   AddressOfEntryPoint;  程序执行入口RVA     [60]+40        * DWORD   BaseOfCode;  代码节的起始RVA              [60]+44        * DWORD   BaseOfData;  数据节的起始RVA               [60]+48        * DWORD   ImageBase;  程序的优先装载地址              [60]+52        * DWORD   SectionAlignment;  内存中节的对齐粒度                 [60]+56                                       * DWORD   FileAlignment;  文件中节的对齐粒度           [60]+60         WORD    MajorOperatingSystemVersion;  操作系统主版本号 [60]+64         WORD    MinorOperatingSystemVersion;  操作系统副版本号   [60]+66         WORD    MajorImageVersion;  PE文件映像的版本号     [60]+68         WORD    MinorImageVersion;                          [60]+70         WORD    MajorSubsystemVersion;  子系统的版本号       [60]+72         WORD    MinorSubsystemVersion;                     [60]+74         DWORD   Win32VersionValue;  未用 必须设置0          [60]+76         DWORD   SizeOfImage;  内存中整个PE文件的映像尺寸       [60]+80         DWORD   SizeOfHeaders;  所有节表按照文件对齐粒度后的大小   [60]+84         DWORD   CheckSum;  校验和                    [60]+88         WORD    Subsystem;  指定使用界面的子系统       [60]+92         WORD    DllCharacteristics;  DALL文件属性        [60]+94       *  DWORD   SizeOfStackReserve;  初始化时保留的栈的大小  [60]+96       *  DWORD   SizeOfStackCommit;  初始化时实际提交的栈的大小   [60]+100       *  DWORD   SizeOfHeapReserve;  初始化时保留的堆的大小   [60]+104       *  DWORD   SizeOfHeapCommit;  初始化时实际提交的堆的大小 [60]+108         DWORD   LoaderFlags;  加载标志  未用      [60]+112         DWORD   NumberOfRvaAndSizes;  下面的数据目录结构的数量  +116         IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];+120  } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

额,重要的也蛮多。。。

详细字段的用处我会在下面代码里会详细说明,数据目录。。。。好吧,现在讲。

数据目录

在PE结构中最重要的就是节表和数据目录表,数据目录一共指向16张表,分别是:导出表、导入表、资源表、异常信息表、安全证书表、重定位表、调试信息表、版权所以表、全局指针表                           

TLS表、加载配置表、绑定导入表、IAT表、延迟导入表、COM信息表 最后一个保留未使用。                     

这么多表中我们只要注重导出表、导入表、重定位表、IAT表就可以了。他们直接影响程序运行。 注意:数据目录只是各种表的地址(rva)。需要寻址才能找到他们。

(数据目录,太长了没有截完)

每张记录宽度8字节 然后*16=128字节

经过解析就变成

代码实现解析PE头(PE头和标准PE头是两个东西)

代码实现思路就是按照字段宽度偏移来遍历dos头、NT头。(这里rva和foa都一样)

__int64 flen = 0; //int arr_dos[30]; char* word_dos[] = {          "e_magic",  "e_cblp",  "e_cp",  "e_crlc",  "e_cparhdr",  "e_minalloc", "e_maxalloc",   "e_ss",  "e_sp",          "e_csum",  "e_ip",  "e_cs", "e_lfarlc","e_ovno",   "e_res[4]",  "e_res[4]",  "e_res[4]", "e_res[4]",  "e_oemid",          "e_oeminfo",   "e_res2[10]",  "e_res2[10]",    "e_res2[10]",  "e_res2[10]",     "e_res2[10]",   "e_res2[10]",         "e_res2[10]",    "e_res2[10]",    "e_res2[10]",      "e_res2[10]",     "e_lfanew" }; /////////////////////////////////////////////////////////////////char* NT_word[] int arr_pe[8] = { 4,2,2,4,4,4,2,2 }; //标准pe每个字段的宽度 int pe_s = 0;//没用 凑数的 int arr_o_pe[] = { 2,1,1,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,4 ,4,4,4,2,2,4 ,4,4,4 ,4,4 };//取字段字节数 int arr_sch[10] = { 8,4,4,4,4,4,4,2,2,4 }; //char*// file_ser(char*, unsigned __int64); char* file_open(char* fstr)//打开文件并保存文件流 {          FILE* pf = fopen(fstr, "rb");          if (pf == NULL)          {                    perror("The following error occurred");                    return 0;          }          fseek(pf, 0, 2);          unsigned __int64 fsize = ftell(pf);          flen = fsize;          rewind(pf);          char* fbuff = (char*)malloc(fsize);          if (fbuff == NULL)          {                    perror("The following error occurred");                    return 0;          }          memset(fbuff, 0, fsize);          unsigned __int64 r = fread(fbuff, 1, fsize, pf);          if (r == 0)          {                    perror("The following error occurred");                    return 0;          }          //fclose(pf);          //free(pf);          //pf = 0;          //print_pe(fbuff);          //ftoi_32(fbuff);          return fbuff; } 先打开文件,然后调用打印函数。 void print_pe(char* ppe)//打印pe字段 {          char* flag = ppe;//flag是游标          for (int j = 0; j 


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3