作为编写编译器的先驱,我试图了解 Windows(32 位)可移植可执行文件格式。特别是我想看一个简单的可执行文件的例子,它除了正确加载、运行和退出之外什么都不做。
我尝试编写和编译一个简单的 C 主函数,它只执行生成的 .exe 约 22KB 并包含许多来自 KERNEL32.DLL 的导入(可能由 LIBC 用于设置环境、堆等)。甚至 DOS 标题也可能更小(它当前打印默认的“此程序无法在 DOS 模式下运行”)。
最小的 Windows 32 位可执行文件的结构是什么?
作为编写编译器的先驱,我试图了解 Windows(32 位)可移植可执行文件格式。特别是我想看一个简单的可执行文件的例子,它除了正确加载、运行和退出之外什么都不做。
我尝试编写和编译一个简单的 C 主函数,它只执行生成的 .exe 约 22KB 并包含许多来自 KERNEL32.DLL 的导入(可能由 LIBC 用于设置环境、堆等)。甚至 DOS 标题也可能更小(它当前打印默认的“此程序无法在 DOS 模式下运行”)。
最小的 Windows 32 位可执行文件的结构是什么?
引用自源代码(创建尽可能小的 PE 可执行文件):1
- 最小的 PE 文件:97 字节
- Windows 2000 上最小的 PE 文件:133 字节
- 通过 WebDAV 下载文件并执行的最小 PE 文件:133 字节
由于 PE 文件格式的要求,上述文件是可能的最小 PE 文件,无法进一步改进。
这个结果是通过一些巧妙的 NASM 技巧实现的,例如删除链接到 C 的步骤stdlib
并删除一些标题字段和数据目录。
完整的源代码如下。它实际上与具有这些修改的文章相同:
sectalign
标签重命名为sect_align
. 自从编写此汇编代码以来,sectalign
它就成为了 NASM 关键字。重命名它以避免警告和错误。代码如下:
; tiny97.asm, copyright Alexander Sotirov
BITS 32
;
; MZ header
; The only two fields that matter are e_magic and e_lfanew
mzhdr:
dw "MZ" ; e_magic
dw 0 ; e_cblp UNUSED
; PE signature
pesig:
dd "PE" ; e_cp, e_crlc UNUSED ; PE signature
; PE header
pehdr:
dw 0x014C ; e_cparhdr UNUSED ; Machine (Intel 386)
dw 1 ; e_minalloc UNUSED ; NumberOfSections
; dd 0xC3582A6A ; e_maxalloc, e_ss UNUSED ; TimeDateStamp UNUSED
; Entry point
start:
push byte 42
pop eax
ret
codesize equ $ - start
dd 0 ; e_sp, e_csum UNUSED ; PointerToSymbolTable UNUSED
dd 0 ; e_ip, e_cs UNUSED ; NumberOfSymbols UNUSED
dw sections-opthdr ; e_lsarlc UNUSED ; SizeOfOptionalHeader
dw 0x103 ; e_ovno UNUSED ; Characteristics
; PE optional header
; The debug directory size at offset 0x94 from here must be 0
filealign equ 4
sect_align equ 4 ; must be 4 because of e_lfanew
%define round(n, r) (((n+(r-1))/r)*r)
opthdr:
dw 0x10B ; e_res UNUSED ; Magic (PE32)
db 8 ; MajorLinkerVersion UNUSED
db 0 ; MinorLinkerVersion UNUSED
; PE code section
sections:
dd round(codesize, filealign) ; SizeOfCode UNUSED ; Name UNUSED
dd 0 ; e_oemid, e_oeminfo UNUSED ; SizeOfInitializedData UNUSED
dd codesize ; e_res2 UNUSED ; SizeOfUninitializedData UNUSED ; VirtualSize
dd start ; AddressOfEntryPoint ; VirtualAddress
dd codesize ; BaseOfCode UNUSED ; SizeOfRawData
dd start ; BaseOfData UNUSED ; PointerToRawData
dd 0x400000 ; ImageBase ; PointerToRelocations UNUSED
dd sect_align ; e_lfanew ; SectionAlignment ; PointerToLinenumbers UNUSED
dd filealign ; FileAlignment ; NumberOfRelocations, NumberOfLinenumbers UNUSED
dw 4 ; MajorOperatingSystemVersion UNUSED ; Characteristics UNUSED
dw 0 ; MinorOperatingSystemVersion UNUSED
dw 0 ; MajorImageVersion UNUSED
dw 0 ; MinorImageVersion UNUSED
dw 4 ; MajorSubsystemVersion
dw 0 ; MinorSubsystemVersion UNUSED
dd 0 ; Win32VersionValue UNUSED
dd round(hdrsize, sect_align)+round(codesize,sect_align) ; SizeOfImage
dd round(hdrsize, filealign) ; SizeOfHeaders
dd 0 ; CheckSum UNUSED
db 2 ; Subsystem (Win32 GUI)
hdrsize equ $ - $$
filesize equ $ - $$
要构建到可执行文件中,请使用:
nasm -f bin tiny97.asm -o tiny97.exe
对于 GNU/Linux ELF 可执行文件,请参阅文章“Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux”。TL;DR :1340
字节,使用 NASM
注意:此答案是 J... 在 2016 年 12 月 3 日 17:31 的评论的扩展,以保留链接中找到的信息(以防万一也失效)。
在 Windows XP (x32) 上,最小的 PE 可执行文件是 97 字节。在 32 位版本的 Vista 和 7 上,最小的 PE 可执行文件是 252 字节。在 64 位版本的 Windows 上,最小的 32 位可执行文件是 268 字节。在这个论坛上,您可以找到此类可执行文件的位图。
最小的 x64 PE 可执行文件是 268 字节。甚至可以执行这种大小的可执行文件中的每个字节。您也可以在此论坛上找到链接。
下面的代码是一个大小为 268 字节的 x64 PE(又名 PE32+)可执行文件。
; PE64smallest.asm Aug 19, 2018 (c) DrakoPensulo
; A smallest PE32+ executable (x64)
;
; Features:
; - Windows Vista/7/8/10 compatible
; - Size: 268 bytes (an executable file on x64 Windows cannot be smaller)
; - No sections
; - No Data Directories (in particular no imports and no TLS callbacks)
; - Exits with code 0x2a (this executable does nothing else than that)
;
;
; Compile using FASM (https://flatassembler.net) command line: fasm.exe PE64smallest.asm
format binary as 'exe'
use64
EntryPoint:
db 'MZ' ; DOS signature
dw 0faceh
dd 00004550h ; Signature PE\0\0
dw 8664h ; Machine
dw 0000h ; NumberOfSections
dd 0facefaceh ; TimeDateStamp
dd 0facefaceh ; PointerToSymbolTable
dd 0facefaceh ; NumberOfSymbols
dw 0 ; SizeOfOptionalHeader ; must be multiple of 8 not too large
dw 002fh ; Characteristics ; must be bit 1=1 bit 13=0
dw 020Bh ; PE32+ Magic
db 0fah ; MajorLinkerVersion
db 0fah ; MinorLinkerVersion
dd 0facefaceh ; SizeOfCode
dd 0facefaceh ; SizeOfInitializedData
dd 0facefaceh ; SizeOfUninitializedData
dd start ; AddressOfEntryPoint ; cannot be smaller than SizeOfHeaders
dd 0facefaceh ; BaseOfCode
dq 0000000100000000h ; ImageBase ; must be multiple of 64k
dd 4 ; SectionAlignment and e_lfanew ; PE header offset in file
dd 4 ; FileAlignment
dw 0faceh ; MajorOperatingSystemVersiom
dw 0faceh ; MinorOperatingSystemVersion
dw 0faceh ; MajorImageVersion
dw 0faceh ; MinorImageVersion
dw 5 ; MajorSubsystemVersion ; >3.1 or 4
dw 0h ; MinorSubsystemVersion
dd 0facefaceh ; Win32VersionValue
dd 0400h ; SizeOfImage ; MSB has to be small, must be >0200h
dd start ; SizeOfHeaders ; SizeOfHeaders has to be < SizeOfImage
dd 0facefaceh ; CheckSum
dw 0002h ; Subsystem 2-GUI 3-CUI
dw 0 ; DllCharacteristics
dd 000cefaceh
dd 0 ; SizeOfStackReserve upper dword has to be 0, MSB of lower dword has to be small
dd 000cefaceh
dd 0 ; SizeOfStackCommit upper dword has to be 0, MSB of lower dword has to be small
dd 000cefaceh
dd 0 ; SizeOfHeapReserve upper dword has to be 0, MSB of lower dword has to be small
dd 000cefaceh
dd 0 ; SizeOfHeapCommit upper dword has to be 0, MSB of lower dword has to be small
dd 0facefaceh ; LoaderFlags
dd 0 ; NumberofRvaAndSizes
dd 0facefaceh
dd 0facefaceh ; Export Directory Address and Size
dd 0facefaceh
dd 0facefaceh ; Import Directory Address and Size
dd 0facefaceh
dd 0facefaceh ; Resource Directory Address and Size
dd 0facefaceh
dd 0facefaceh ; Exception Directory Address and Size
dd 0facefaceh
dd 0facefaceh ; Security Directory Address and Size
dd 0facefaceh
dd 0facefaceh ; Base Relocation Table Address and Size
dd 0facefaceh
dd 0facefaceh ; Debug Directory Address and Size
dd 0facefaceh
dd 0facefaceh ; Architecture Specific Data Address and Size
dd 0facefaceh
dd 0facefaceh ; RVA of GlobalPtr Directory Address and Size
dd 0facefaceh
dd 0facefaceh ; TLS Directory Address and Size
dd 0facefaceh
dd 0facefaceh ; Load Configuration Directory Address and Size
dd 0facefaceh
dd 0facefaceh ; Bound Import Directory Address and Size
dd 0facefaceh
dd 0facefaceh ; Import Address Table Address and Size
dd 0facefaceh
dd 0facefaceh ; Delay Load Import Descriptors Address and Size
dd 0facefaceh
dd 0facefaceh ; COM runtime Descriptors Address and Size
dd 0facefaceh
start:
push 2ah
pop rax
ret ; Reserved Descriptor
顺便说一句,在此博客条目中,您可以找到一个小型(316 字节)x32 可执行文件,其中包含汇编源代码和许多技术细节。