Skip to content

Instantly share code, notes, and snippets.

@jaburns
Last active October 29, 2019 21:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaburns/cfe3440bfc6dde0c89b830752e5130c6 to your computer and use it in GitHub Desktop.
Save jaburns/cfe3440bfc6dde0c89b830752e5130c6 to your computer and use it in GitHub Desktop.
Minimalist functional Windows PE file
;==================================================================================================
; Ultra-small EXE layout forked from KeyJ's console clipboard app
; https://keyj.emphy.de/win32-pe/
;
; Assembled with yasm 1.3.0 for Win64
; https://yasm.tortall.net/Download.html
;
; .\yasm-1.3.0-win64.exe -fbin -o"out.exe" source.asm
;
bits 32
BASE equ 0x00400000
ALIGNMENT equ 4
SECTALIGN equ 4
%define ROUND(v, a) (((v + a - 1) / a) * a)
%define ALIGNED(v) (ROUND(v, ALIGNMENT))
%define RVA(obj) (obj - BASE)
org BASE
mz_hdr:
dw "MZ" ; DOS magic
dw "jb" ; filler to align the PE header
pe_hdr:
dw "PE",0 ; PE magic + 2 padding bytes
dw 0x014c ; i386 architecture
dw_zero:
dw 0 ; no sections
N_user32:
db "user32.dll",0,0 ; 12 bytes of data collapsed into the header
; dd 0 ; [UNUSED-12] timestamp
; dd 0 ; [UNUSED] symbol table pointer
; dd 0 ; [UNUSED] symbol count
dw 8 ; optional header size
dw 0x0102 ; characteristics: 32-bit, executable
opt_hdr:
dw 0x010b ; optional header magic
; 12 bytes of main entry point + 2 bytes of jump
main_part_1:
mov eax, [fs:0x30] ; get PEB pointer from TEB
mov eax, [eax+0x0C] ; get PEB_LDR_DATA pointer from PEB
mov eax, [eax+0x14] ; go to first LDR_DATA_TABLE_ENTRY
jmp main_part_2
align 4, db 0
; db 13,37 ; [UNUSED-14] linker version
; dd RVA(the_end) ; [UNUSED] code size
; dd RVA(the_end) ; [UNUSED] size of initialized data
; dd 0 ; [UNUSED] size of uninitialized data
dd RVA(main_part_1) ; entry point address
; another 6 bytes of code + 2 bytes of jump
main_part_2:
push ebp ; set up stack frame for local variables
%define DummyVar ebp-4
%define kernel32base ebp-8
%define user32base ebp-12
sub esp, 12
mov eax, [eax] ; go to where ntdll.dll typically is
jmp main_part_3
align 4, db 0
; dd RVA(main) ; [UNUSED-8] base of code
; dd RVA(main) ; [UNUSED] base of data
dd BASE ; image base
dd SECTALIGN ; section alignment (collapsed with the
; PE header offset in the DOS header)
dd ALIGNMENT ; file alignment
; another 5 bytes of code + 2 bytes of jump
main_part_3:
mov eax, [eax] ; go to where kernel32.dll typically is
mov ebx, [eax+0x10] ; load base address of the library
jmp main_part_4
align 4, db 0
; dw 4,0 ; [UNUSED-8] OS version
; dw 0,0 ; [UNUSED] image version
dw 4,0 ; subsystem version
dd 0 ; [UNUSED-4] Win32 version
dd RVA(the_end) ; size of image
dd RVA(opt_hdr) ; size of headers (must be small enough
; so that entry point inside header is accepted)
dd 0 ; [UNUSED-4] checksum
dw 2 ; subsystem = GUI
dw 0 ; [UNUSED-2] DLL characteristics
dd 0x00100000 ; maximum stack size
dd 0x00001000 ; initial stack size
dd 0x00100000 ; maximum heap size
dd 0x00001000 ; initial heap size
dd 0 ; [UNUSED-4] loader flags
dd 0 ; number of data directory entries (= none!)
; FUNCTION that calls procedure [esi] in library at base [ebx]
call_import:
mov edx, [ebx+0x3c] ; get PE header pointer (w/ RVA translation)
add edx, ebx
mov edx, [edx+0x78] ; get export table pointer RVA (w/ RVA translation)
add edx, ebx
push edx ; store the export table address for later
mov ecx, [edx+0x18] ; ecx = number of named functions
mov edx, [edx+0x20] ; edx = address-of-names list (w/ RVA translation)
add edx, ebx
name_loop:
push esi ; store the desired function name's hash (we will clobber it)
mov edi, [edx] ; load function name (w/ RVA translation)
add edi, ebx
cmp_loop:
movzx eax, byte [edi] ; load a byte of the name ...
inc edi ; ... and advance the pointer
xor esi, eax ; apply xor-and-rotate
rol esi, 7
or eax, eax ; last byte?
jnz cmp_loop ; if not, process another byte
or esi, esi ; result hash match?
jnz next_name ; if not, this is not the correct name
; if we arrive here, we have a match!
pop esi ; restore the name pointer (though we don't use it any longer)
pop edx ; restore the export table address
sub ecx, [edx+0x18] ; turn the negative counter ECX into a positive one
neg ecx
mov eax, [edx+0x24] ; get address of ordinal table (w/ RVA translation)
add eax, ebx
movzx ecx, word [eax+ecx*2] ; load ordinal from table
mov eax, [edx+0x1C] ; get address of function address table (w/ RVA translation)
add eax, ebx
mov eax, [eax+ecx*4] ; load function address (w/ RVA translation)
add eax, ebx
jmp eax ; jump to the target function
next_name:
pop esi ; restore the name pointer
add edx, 4 ; advance to next list item
dec ecx ; decrease counter
jmp name_loop
main_part_4:
mov [kernel32base], ebx ; store kernel32's base address
;===== Beginning of application code ============================================================
; user32base = kernel32base.LoadLibraryA( "user32.dll" );
; assume ebx = [kernel32base]
mov esi, 0x01364564 ; hash of "LoadLibraryA"
push N_user32
call call_import
mov [user32base], eax
; MessageBoxA( NULL, message, "", 0 );
push 0
push dw_zero
push message
push 0
mov ebx, eax ; assumes eax still has [user32base] from above
mov esi, 0x36AEF1A0 ; hash of "MessageBoxA"
call call_import
push 0
mov ebx, [kernel32base]
mov esi, 0x665640AC ; hash of "ExitProcess"
call call_import
;===== Data section =============================================================================
message: db "Hello world", 0
;==================================================================================================
align ALIGNMENT, db 0
the_end:
/**
* This function will generate the hashes used to compare strings in call_import.
* i.e. hash( "LoadLibraryA" ) === 0x01364564
*/
const hash = string => {
const ror7 = x => ((x >>> 7) | ((x & 0x7F) << (32 - 7))) >>> 0;
let hash = 0;
for( let index = string.length - 1; index >= 0; index-- )
{
hash = ror7( hash );
hash ^= string.charCodeAt( index );
}
let ret = hash.toString( 16 );
while( ret.length < 8 ) ret = '0' + ret;
return '0x' + ret.toUpperCase();
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment