[原创]《32位PE解析、PE修改、导入表注入》(上传打印资源表代码) |
您所在的位置:网站首页 › pe修改注册表工具 › [原创]《32位PE解析、PE修改、导入表注入》(上传打印资源表代码) |
混子目录 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_HEADER1.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 |