PE操作—结构重叠
结构重叠是指在不影响 PE 文件正常加载的前提下,将某些数据结构进行重叠。结构重叠可以缩小 PE 文件。比如新增一个节的时可能节表后没有足够的空间容纳新的节表,那么我们就可以考虑使用结构重叠来获得大量空间。现在我们以重叠 IMAGE_DOS_HEADER 和 IMAGE_NT_HEADERS 结构为例。IMAGE_DOS_HEADER.e_lfanew 成员指向 IMAGE_NT_HEADERS 结构的位置,如果我们将 IMAGE_NT_HEADERS 结构提前到 IMAGE_DOS_HEADER 结构中,那么 e_lfanew 就会覆盖在 IMAGE_NT_HEADERS 中,就需要使用 IMAGE_NT_HEADERS 结构中某个 字段来 代替 e_lfanew。IMAGE_DOS_HEADER.BaseOfData 这个字段 windows 加载器并不使用,我们可以把这个字段重叠到 IMAGE_DOS_HEADER.e_lfanew 中。介绍完基本原理,我们再来看一下实际操作。
以下是 PE 文件的 IMAGE_DOS_HEADER 和 IMAGE_NT_HEADERS 重叠后的字节码

先模拟 windows 读取 IMAGE_DOS_HEADER 和 IMAGE_NT_HEADERS 查看下每个字段都是些什么值
IMAGE_DOS_HEADER (DOS文件头)结构如下:
e_magic: 0x5A4D]
e_cblp: 0x0090
e_cp: 0x0003
e_crlc: 0x0000
e_cparhdr: 0x0004
e_minalloc: 0x0000
e_maxalloc: 0x4550
e_ss: 0x0000
e_sp: 0x014C
e_csum: 0x0003
e_ip: 0x5287
e_cs: 0x4802
e_lfarlc: 0x0000
e_ovno: 0x0000
e_res[4]: 0x0000000000E0010F
e_oemid: 0x010B
e_oeminfo: 0x0A07
e_res2[10]: 780000008800000000000000739D000010000000
e_lfanew: 0x0000000C
e_lfanew 指向 0x0000000C,我们从 0x0000000C 处读取 IMAGE_NT_HEADERS 结构
Signature: 0x00004550
Machine: 0x014C
NumberOfSections: 0x0003
TimeDateStamp: 0x48025287
PointerOfSymbolTable: 0x00000000
NumberOfSymbols: 0x00000000
SizeOfOptionHeader: 0x00E0
Characteristics: 0x010F
Magic: 0x010B
MajorLinkerVersion: 0x07
MinorLinkerVersion: 0x0A
SizeOfCode: 0x00007800
SizeOfInitializedData: 0x00008800
SizeOfUninitializedData: 0x00000000
AddressOfEntryPoint(RVA): 0x0000739D
BaseOfCode: 0x00001000
BaseOfData: 0x0000000C
ImageBase: 0x01000000
SectionAlignment: 0x00001000
FileAlignment: 0x00000200
....
IMAGE_DOS_HEADER.BaseOfData 和 IMAGE_DOS_HEADER.e_lfanew 进行了重叠
伪代码
1 bool overlap_dos_file_header(void* image) 2 { 3 auto dos_nt = (PIMAGE_DOS_HEADER)image; 4 auto nt_ptr = get_pe_nt32(image); 5 6 //----------------------------> 重叠公用字段 7 nt_ptr->OptionalHeader.BaseOfData = 0xC; 8 9 //----------------------------> move 10 auto nt_section_size = file_headers_size(image); 11 memmove((byte*)dos_nt + 0xC, nt_ptr, nt_section_size); 12 13 return true; 14 }

浙公网安备 33010602011771号