工具
1.010Editor :填充16进制码
2.lordPE :检查PE文件出错原因
知识背景
1. PE知识 : (PE中结构体的字段分布,PE加载器的原理)
2. 汇编知识 : (汇编调用MessageBoxA个ExitProcess)
目标:构造一个程序(CUI).exe
程序功能:
1.调用MessageBoxA弹出消息框
2.调用ExitProcess退出程序
思路
1.在Windows下PE文件格式的程序如何运行?
答:符合PE格式的文件+系统加载器,如何成功运行程序,失败,报错。
2.那么系统加载的原理或者执行逻辑又是什么呢?
答:
加载器:简单执行流程如下
1.内存映射
2.修复IAT
3.修复重定位
tip:本次程序并没有重定位,只需要考虑前2步即可。
3.PE文件如何构建?还可以做哪些优化呢?
答:
构建:
1.Dos头:IMAGE_DOS_HEADER;
1.1->WORD e_magic; //标识符:MZ
1.2->LONG e_lfanew; //Dos头部的大小
2.NT头:IMAGE_NT_HEADERS32
2.1->DWORD Signature;
2.2->IMAGE_FILE_HEADER FileHeader;
2.3->IMAGE_OPTIONAL_HEADER32 OptionalHeader;
3.区段头:IMAGE_SECTION_HEADER
3.1 ->.text
3.2 ->.rdata
4.区段数据:数据
4.1 ->代码数据(OpCode) == 200+
4.2 ->导入数据(IAT、导入表、INT、HitName) == 200+
优化:
1.DosStub是个历史遗留问题,在一定情况下也可在该区域填写代码,但在本次程序可以将该区域删除。
2.区段减少到2个,.text、.rdata。(ps:其实还可以减少到只有一个区段.text)
3.对程序运行无影响的字段用0填充。
构造PE步骤
1.整理以上思路大致可以得到以下思维导图
仔细观察PE文件的格式,就不难发现到处都体现出了头部+身体,目录+内容的管理数据的思想,那么对于我们自己手工建立PE文件,同样也可以以此作为思路开始构建PE文件。(在构建时最好同时去做这两件事,有助于加快进度)
1.组成头部:
1.1-Dos头
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
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; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
关键字段:
1.WORD e_magic; ==> MZ => 4D 5A (标识符)
2.LONG e_lfanew; ==> 40h => 40 00 00 00(DosStub去除后,IMAGE_DOS_HEADER 的结构体大小就是Dos头大小)
-----------------------------------------------------------------------------------------------------
4D 5A
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
40 00 00 00
----------------------------------------------------------------------------------------------------
1.2-NT头
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
关键字段:
1.2.1.DWORD Signature; ==> PE ==>50 45 00 00 (标识符)
1.2.2IMAGE_FILE_HEADER FileHeader;
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
关键字段
2.1 WORD Machine; ==> x86 ==> 4c 01 (0x014c 应用的机器型号)
2.2 WORD NumberOfSections; ==> 2个 ==> 02 00 (1.text 2.rdata)
2.3 WORD SizeOfOptionalHeader; ==> E0 ==> E0 00 (NT扩展头大小)
2.4 WORD Characteristics; ==> 10F ==> 0F 01 (可以自定义)
1.2.3. IMAGE_OPTIONAL_HEADER32 OptionalHeader;
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
关键字段
3.1 WORD Magic; ==> 10B ==>0B 01 (文件类型:PE32文件)
3.2 DWORD AddressOfEntryPoint; ==> 1000 ==>00 10 00 00(.text在文件中200h位置,转内存偏移后为1000h)
3.2 DWORD ImageBase; ==> 40000 ==>00 00 04 00 (PE文件在内存的优先加载起始地址VA)
3.3 DWORD SectionAlignment; ==> 1000h ==>00 10 00 00 (内存对齐)
3.4 DWORD FileAlignment; ==> 200h ==>00 02 00 00 (文件对齐)
3.5 WORD MajorOperatingSystemVersion; ==> 6 ==>06 00
3.6 WORD MajorSubsystemVersion; ==> 6 ==>06 00
3.7 DWORD SizeOfImage; ==>207E ==> 7E 20 00 00 (加载到内存中镜像大小)
3.8 DWORD SizeOfHeaders; ==>200h ==> 00 02 00 00 (Dos+NT+Section :在文件中未超过200h)
3.9 WORD Subsystem; ==> 3 ==>03 00 (使用界面的子系统:(CUI) subsystem)
3.10 WORD DllCharacteristics; ==> 8100 ==>00 81 (DLL文件属性)
3.11 DWORD NumberOfRvaAndSizes; ==> 10h ==>10 00 (表示数据目录结构的数量.默认16个)
3.12 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
==>Import Table ==> 10 20 00 00 (.rdata 在400h处,rva: 2000h+10h(IAT字节数))
==> 3C 00 00 00 (有2个DLL,即2个导入表结构体(结尾为20个00),即3Ch个字节)
-------------------------------------------- 50 45 00 00
4c 01
02 00
00 00 00 00 00 00 00 00 00 00 00 00
E0 00
0F 01
0B 01
00
00
00 00 00 00
00 00 00 00
00 00 00 00
00 10 00 00
00 00 00 00
00 00 00 00 00 00 40 00
00 10 00 00
00 02 00 00 06 00
00 00
00 00
00 00
06 00
00 00
00 00 00 00
7E 20 00 00
00 02 00 00
00 00 00 00
03 00
00 81 00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
10 00 00 00 00 00 00 00 00 00 00 00
10 20 00 00 3C 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
--------------------------------------------
1.3-区段头
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
关键字段
1.text
1.1 BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; ==>.text ==> 2E 74 65 78 74 00 00 00(区段名称)
1.2 DWORD VirtualSize; ==>22h ==> 22 00 00 00 (有效的OpCode数目)
1.3 DWORD VirtualAddress; ==>1000h ==> 00 10 00 00 (区段rva:200h映射到内存中为1000h)
1.4 DWORD SizeOfRawData; ==>200h ==> 00 02 00 00 (在文件中的对齐后大小)
1.5 DWORD PointerToRawData; ==>200h ==> 00 02 00 00 (在文件中相对与0的偏移)
1.6 DWORD Characteristics; ==>E00000E0 ==> E0 00 00 E0 (区段属性可以自定义)
2.rdata
1.1 BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; ==>.rdata ==> 2E 72 64 61 74 61 00 00(区段名称) 1.2 DWORD VirtualSize; ==>7Eh ==> 7E 00 00 00 (有效的字节数目) 1.3 DWORD VirtualAddress; ==>2000h ==> 00 20 00 00 (区段rva:400h映射到内存中为2000h) 1.4 DWORD SizeOfRawData; ==>200h ==> 00 02 00 00 (在文件中的对齐后大小) 1.5 DWORD PointerToRawData; ==>400h ==> 00 04 00 00 (在文件中相对与0的偏移) 1.6 DWORD Characteristics; ==>E00000E0 ==> E0 00 00 E0 (区段属性可以自定义)
--------------------------------------------
2E 74 65 78 74 00 00 00
22 00 00 00
00 10 00 00
00 02 00 00
00 02 00 00
00 00 00 00
00 00 00 00
00 00
00 00
E0 00 00 E0
2E 72 64 61 74 61 00 00
7E 00 00 00
00 20 00 00
00 02 00 00
00 04 00 00
00 00 00 00
00 00 00 00
00 00
00 00
E0 00 00 E0
--------------------------------------------
tip:200对齐其余填0
2.组成身体:
2.1-区段1.text
伪代码如下:
1
2
3
4
5
6
7
| <b>push 0x0;</b>
<b>push 0x0;</b>
<b>push 0x0;</b>
<b>push 0x0;</b>
<b>call MessageBoxA;</b>
<b>push 0x0;</b>
<b>call ExitProcess;</b>
|
vs程序IAT调用为:FF 15 XXXXXXXX(IAT地址)(有2个dll)
IAT1:FOA:400h ==> RVA: 2000h ==>VA:400000h + 2000h==>402000h
IAT2:FOA:408h ==> RVA: 2008h ==>VA:400000h + 2008h==>402008h
--------------------------------------------
6A 00
6A 00
6A 00
6A 00
FF 15 00 20 40 00
6A 00
FF 15 08 20 40 00
--------------------------------------------
tip:200对齐其余填0
2.2-区段2.rdata
1.IAT:
typedef struct _IMAGE_THUNK_DATA {
union {
DWORD ForwarderString;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData;
} u1;
} IMAGE_THUNK_DATA;
关键字段
1.DWORD AddressOfData1; ==> MessageBoxA ==>5C 20 00 00 00 00 00 00 (以0结尾)
2.DWORD AddressOfData2; ==> ExitProcess ==>75 20 00 00 00 00 00 00 (以0结尾)
tip:加载到内存后IAT里面的内容更新为API的为VA,不在和INT的内容相同。
2.Import Table:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 0 if not bound,
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name;
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
3.INT:
typedef struct _IMAGE_THUNK_DATA {
union {
DWORD ForwarderString;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData;
} u1;
} IMAGE_THUNK_DATA;
关键字段 1.DWORD AddressOfData1; ==> MessageBoxA ==>5C 20 00 00 00 00 00 00 (以0结尾) 2.DWORD AddressOfData2; ==> ExitProcess ==>75 20 00 00 00 00 00 00 (以0结尾) tip:75、5C为字节偏移。
4.HitName:
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
CHAR Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
关键字段
1.MessageBoxA
1.1 WORD Hint; ==> API名称在DLL中的编号==>00 00 (名称导入)
1.2 CHAR Name[1]; ==>MessageBoxA名称 ==>4D 65 73 73 61 67 65 42 6F 78 41
==>USER32.dll ==>00 55 53 45 52 33 32 2E 64 6C 6C 00
2.ExitProcess
2.1 WORD Hint; ==> API名称在DLL中的编号==>00 00 (名称导入)
2.2 CHAR Name[1]; ==> ExitProcess名称 ==>45 78 69 74 50 72 6F 63 65 73 73
==>KERNEL32.dll ==>00 4B 45 52 4E 45 4C 33 32 2E 64 6C 6C 00
tip:DLL函数结尾处,会加以DLL名称结尾
--------------------------------------------
5C 20 00 00 00 00 00 00
75 20 00 00 00 00 00 00
4C 20 00 00
00 00 00 00
00 00 00 00
6A 20 00 00
00 20 00 00
54 20 00 00
00 00 00 00
00 00 00 00
83 20 00 00
08 20 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
5C 20 00 00 00 00 00 00
75 20 00 00 00 00 00 00
00 00
4D 65 73 73 61 67 65 42 6F 78 41
00 55 53 45 52 33 32 2E 64 6C 6C 00
00 00
45 78 69 74 50 72 6F 63 65 73 73
00 4B 45 52 4E 45 4C 33 32 2E 64 6C 6C 00
--------------------------------------------
tip:200对齐其余填0
结语以上将16进制码复制到010Editor中保存为.exe格式即可运行,运行环境为win10,并未考虑兼容性的问题,发帖的本意为0到1质变,1往后的变化希望大家自行发挥,例如:添加新的区段,在弹窗中添加文字,将代码复杂化等等。
|