什么是最小的Windows(PE)可执行文件?

时间:2022-02-25 13:16:38

As a precursor to writing a compiler I'm trying to understand the Windows (32-bit) Portable Executable format. In particular I'd like to see an example of a bare-bones executable which does nothing except load correctly, run and exit.

作为编写编译器的先驱,我试图理解Windows(32位)可移植可执行格式。特别是我想看到一个简单的可执行文件的例子,除了正确加载,运行和退出之外什么都不做。

I've tried writing and compiling a simple C main function which does nothing but the resulting .exe is ~22KB and contains many imports from KERNEL32.DLL (presumably used by LIBC to set up environment, heaps etc.). Even the DOS Header could probably be smaller (it currently prints the default 'This program cannot be run in DOS mode').

我已经尝试编写和编译一个简单的C main函数,除了生成的.exe是〜22KB并且包含许多来自KERNEL32.DLL的导入(可能由LIBC用于设置环境,堆等)。甚至DOS标题也可能更小(它当前打印默认的'此程序无法在DOS模式下运行')。

What is the structure of the smallest possible Windows 32-bit executable?

最小的Windows 32位可执行文件的结构是什么?

2 个解决方案

#1


As quoted from source (Creating the smallest possible PE executable): 1

引自source(创建最小的可执行PE可执行文件):1

  • Smallest possible PE file: 97 bytes
  • 最小的PE文件:97个字节

  • Smallest possible PE file on Windows 2000: 133 bytes
  • Windows 2000上最小的PE文件:133字节

  • Smallest PE file that downloads a file over WebDAV and executes it: 133 bytes
  • 通过WebDAV下载文件并执行它的最小PE文件:133个字节

The files above are the smallest possible PE files due to requirements of the PE file format and cannot be improved further.

由于PE文件格式的要求,上述文件是可能的最小PE文件,无法进一步改进。

This result was achieved with some clever NASM tricks, such as removing the step that links to C stdlib and removing a number of header fields and data directories.

这个结果是通过一些聪明的NASM技巧实现的,例如删除链接到C stdlib的步骤并删除了许多头字段和数据目录。

The full source code is below. It is effectively the same as the article with these modification:

完整的源代码如下。它实际上与具有这些修改的文章相同:

  • Removal of blank lines
  • 删除空行

  • sectalign label renamed to sect_align. Since the time this assembly code was written sectalign became a NASM keyword. Rename it to avoid warnings and errors.
  • sectalign标签重命名为sect_align。自编写此汇编代码以来,sectalign成为NASM关键字。重命名它以避免警告和错误。

The code is as follows:

代码如下:

; 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 $ - $$

To build into an executable use:

构建可执行文件使用:

nasm -f bin tiny97.asm -o tiny97.exe

For GNU/Linux ELF executables, See the article "Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux". TL;DR: 1340 bytes, using NASM

对于GNU / Linux ELF可执行文件,请参阅文章“为Linux创建真正的Teensy ELF可执行文件的旋风教程”。 TL; DR:1340字节,使用NASM

Note: This answer is an expansion of J...'s comment on Dec 3 '16 at 17:31, in order to preserve the information found in the link (in case that too goes dead).

注意:这个答案是J ...在16年12月3日17:31发表的评论的扩展,以保留链接中的信息(如果太死了)。


  1. Tiny PE; Alexander Sotirov; viewed 15/11/2017 @ 17:50 SAST
  2. 微小的PE;亚历山大索蒂罗夫;观看了15/11/2017 @ 17:50 SAST

#2


On Windows XP (x32) the smallest PE executable is 97 bytes. On 32bit versions of Vista and 7 the smallest PE executable is 252 bytes. On 64bit versions of Windows the smallest 32bit executable is 268 bytes. On this forum you find a bit-map of such executable.

在Windows XP(x32)上,最小的PE可执行文件是97个字节。在32位版本的Vista和7上,最小的PE可执行文件是252字节。在64位版本的Windows上,最小的32位可执行文件是268字节。在这个论坛上,您可以找到这种可执行文件的位图。

The smallest x64 PE executable is 268 bytes. It is even possible to execute every byte in an executable of this size. You can find a link on this forum as well.

最小的x64 PE可执行文件是268个字节。甚至可以执行此大小的可执行文件中的每个字节。你也可以在这个论坛上找到一个链接。

The code below is a x64 PE (aka PE32+) executable file of size 268 bytes.

下面的代码是一个大小为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

BTW On this blog entry you find a small (316 bytes) x32 executable with assembler source code and many technical details.

BTW在这篇博客文章中,你会发现一个带有汇编源代码和许多技术细节的小型(316字节)x32可执行文件。

#1


As quoted from source (Creating the smallest possible PE executable): 1

引自source(创建最小的可执行PE可执行文件):1

  • Smallest possible PE file: 97 bytes
  • 最小的PE文件:97个字节

  • Smallest possible PE file on Windows 2000: 133 bytes
  • Windows 2000上最小的PE文件:133字节

  • Smallest PE file that downloads a file over WebDAV and executes it: 133 bytes
  • 通过WebDAV下载文件并执行它的最小PE文件:133个字节

The files above are the smallest possible PE files due to requirements of the PE file format and cannot be improved further.

由于PE文件格式的要求,上述文件是可能的最小PE文件,无法进一步改进。

This result was achieved with some clever NASM tricks, such as removing the step that links to C stdlib and removing a number of header fields and data directories.

这个结果是通过一些聪明的NASM技巧实现的,例如删除链接到C stdlib的步骤并删除了许多头字段和数据目录。

The full source code is below. It is effectively the same as the article with these modification:

完整的源代码如下。它实际上与具有这些修改的文章相同:

  • Removal of blank lines
  • 删除空行

  • sectalign label renamed to sect_align. Since the time this assembly code was written sectalign became a NASM keyword. Rename it to avoid warnings and errors.
  • sectalign标签重命名为sect_align。自编写此汇编代码以来,sectalign成为NASM关键字。重命名它以避免警告和错误。

The code is as follows:

代码如下:

; 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 $ - $$

To build into an executable use:

构建可执行文件使用:

nasm -f bin tiny97.asm -o tiny97.exe

For GNU/Linux ELF executables, See the article "Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux". TL;DR: 1340 bytes, using NASM

对于GNU / Linux ELF可执行文件,请参阅文章“为Linux创建真正的Teensy ELF可执行文件的旋风教程”。 TL; DR:1340字节,使用NASM

Note: This answer is an expansion of J...'s comment on Dec 3 '16 at 17:31, in order to preserve the information found in the link (in case that too goes dead).

注意:这个答案是J ...在16年12月3日17:31发表的评论的扩展,以保留链接中的信息(如果太死了)。


  1. Tiny PE; Alexander Sotirov; viewed 15/11/2017 @ 17:50 SAST
  2. 微小的PE;亚历山大索蒂罗夫;观看了15/11/2017 @ 17:50 SAST

#2


On Windows XP (x32) the smallest PE executable is 97 bytes. On 32bit versions of Vista and 7 the smallest PE executable is 252 bytes. On 64bit versions of Windows the smallest 32bit executable is 268 bytes. On this forum you find a bit-map of such executable.

在Windows XP(x32)上,最小的PE可执行文件是97个字节。在32位版本的Vista和7上,最小的PE可执行文件是252字节。在64位版本的Windows上,最小的32位可执行文件是268字节。在这个论坛上,您可以找到这种可执行文件的位图。

The smallest x64 PE executable is 268 bytes. It is even possible to execute every byte in an executable of this size. You can find a link on this forum as well.

最小的x64 PE可执行文件是268个字节。甚至可以执行此大小的可执行文件中的每个字节。你也可以在这个论坛上找到一个链接。

The code below is a x64 PE (aka PE32+) executable file of size 268 bytes.

下面的代码是一个大小为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

BTW On this blog entry you find a small (316 bytes) x32 executable with assembler source code and many technical details.

BTW在这篇博客文章中,你会发现一个带有汇编源代码和许多技术细节的小型(316字节)x32可执行文件。