Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the smallest possible Windows (PE) executable?

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.

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').

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

like image 694
Matthew Murdoch Avatar asked Feb 16 '09 11:02

Matthew Murdoch


People also ask

What is a Windows Portable Executable?

The Portable Executable (PE) format is a file format for executables, object code, DLLs and others used in 32-bit and 64-bit versions of Windows operating systems. The PE format is a data structure that encapsulates the information necessary for the Windows OS loader to manage the wrapped executable code.

What two characters does every Windows Portable Executable PE file start with?

After the MS-DOS stub, at the file offset specified at offset 0x3c, is a 4-byte signature that identifies the file as a PE format image file. This signature is "PE\0\0" (the letters "P" and "E" followed by two null bytes).

What is PE32 executable?

Portable Executable (PE, PE/COFF, PE32, PE32+) is a member of the EXE family of executable file formats. It is used by the Microsoft Windows family of operating systems (starting with Windows 95 and Win32s), EFI and sometimes in other environments. It is an extension/hybrid of MS-DOS EXE, and a successor to NE.

What is entry point of a PE executable?

The PE entry point is defined in the IMAGE_OPTIONAL_HEADER structure, in the AddressOfEntryPoint field: A pointer to the entry point function, relative to the image base address. For executable files, this is the starting address. For device drivers, this is the address of the initialization function.


2 Answers

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

  • Smallest possible PE file: 97 bytes
  • Smallest possible PE file on Windows 2000: 133 bytes
  • Smallest PE file that downloads a file over WebDAV and executes it: 133 bytes

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

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.

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.

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

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).


  1. Tiny PE; Alexander Sotirov; viewed 15/11/2017 @ 17:50 SAST
like image 162
Agi Hammerthief Avatar answered Oct 21 '22 06:10

Agi Hammerthief


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.

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.

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

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

like image 38
DrakoPensulo Avatar answered Oct 21 '22 06:10

DrakoPensulo