;TinyPE builder
;How to use:
;	include 'tiny'			;need include for macros
;	import KERNEL32,ExitProcess...	;State imports here before we can build header
;	import USER32,MessageBoxA...
;	format 				;Build small PE header, default console, use format PE GUI to build GUI
;	;--------------- user code start here ---------------
;	Start:		;Program Entry point
;	;--------------- user code ends here ----------------
;	EndPE				;Setup filesize etc for PE header
;Problems
;	VirusTotal will give some warnings
;	Does not work under Win95

; ----------------------------- macro's to build file header ---------------------------
macro	ddround n,r {	dd (n+r-1) and -r }

dll_tables equ
dll_imports equ dd 0,0,0,0,0
dll_strings equ

filealign = 4
sectalign = 4   ; must be 4 because of e_lfanew
adjust = sectalign - filealign
image_base = 0x400000
dll_count = 0

macro import dll,[func] {
    common
	IMPORT_DIR = 1
	macro dll_tables \{
	    dll_tables
	    if dll_count > 0
		dd 0
	    end if
	    dll_count = dll_count + 1
	    dll#_table: 
	        forward
		    func: dd _#func-image_base+adjust-2
		common
	\}
	macro dll_imports \{
	    dd	0,0,0,dll#_name-image_base+adjust, dll#_table-image_base+adjust
	    dll_imports
	\}
	macro dll_strings \{
	    dll_strings
	    dll#_name	db `dll,0
		forward
		    _#func	db `func,0
		common
	\}
}

section_count equ 0
_code = 0x20
_data = 0x40
_readable = 0x40000000
_writeable = 0x80000000
_executable = 0x20000000
_shareable = 0x10000000
_discardable = 0x02000000
_notpageable = 1 shl 27
_resource = _readable + _data

macro store_section_header header,name,size,addr,flag {
	store qword name at header					; Name
	store dword (size+sectalign-1) and -sectalign at header+8	; VirtualSize           
	store dword RVA addr at header+12				; VirtualAddress        
	store dword (size+filealign-1) and -filealign at header+16      ; SizeOfRawData         
	store dword RVA addr at header+20				; PointerToRawData      
	store dword flag at header+36                                   ; Characteristics UNUSED
}

macro section args {			; setup SECTION_#
	common
	align sectalign
	match count,section_count \{
		SECTION_\#count:
                rept 2 j:count \\{
			section_count equ j
			sect_size = SECTION_\\#j - $
		\\}
		SECTION_HEADER = secthdr + count * 40
		if defined IMPORT_DIR
			SECTION_HEADER = SECTION_HEADER + 40
		end if
		match name flags,args \\{
			SECTION_FLAG = 0
			irps flag, flags \\\{
				SECTION_FLAG = SECTION_FLAG or _\\\#flag
				match =resource,flag \\\\{
					RESOURCE_DIR=1
					store dword RVA $ at RESOURCE_DIRECTORY_HEADER
					store dword sect_size at RESOURCE_DIRECTORY_HEADER+4
				\\\\}
			\\\}
			store_section_header SECTION_HEADER,name,sect_size,$,SECTION_FLAG
		\\}
	\}
}

macro format [subsys] {
    subsystem = 3
match =PE=GUI,subsys \{ subsystem = 2 \}
match =PE=GUI ver,subsys \{ subsystem = 2 \}

format	binary as 'exe'
use32
org	image_base

; MZ header. The only two fields that matter are e_magic and e_lfanew
    dw "MZ"       ; e_magic
    dw 0          ; e_cblp UNUSED

    dd "PE"       ; e_cp, e_crlc UNUSED       ; PE signature
; PE header
    dw 0x014C     ; e_cparhdr UNUSED          ; Machine (Intel 386)
    dw SECTION_COUNT
		  ; e_minalloc UNUSED         ; NumberOfSections
pe_entry:
    jmp near Start
    db 0,0,0
;   dd 0          ; e_maxalloc, e_ss UNUSED   ; TimeDateStamp UNUSED
;   dd 0          ; e_sp, e_csum UNUSED       ; PointerToSymbolTable UNUSED
    dd 0          ; e_ip, e_cs UNUSED         ; NumberOfSymbols UNUSED
    dw secthdr-opthdr ; e_lsarlc UNUSED       ; SizeOfOptionalHeader
    dw 0x103      ; e_ovno UNUSED             ; Characteristics

; PE optional header

opthdr:
    dw 0x010B     ; e_res UNUSED              ; Magic (PE32)
    db 8                                      ; MajorLinkerVersion UNUSED
    db 0                                      ; MinorLinkerVersion UNUSED

if ~defined SECTION_1
    secthdr:
end if
    ddround codesize, filealign               ; SizeOfCode UNUSED		; Name UNUSED
    dd 0          ; e_oemid, e_oeminfo UNUSED ; SizeOfInitializedData UNUSED
    dd codesize	  ; e_res2 UNUSED             ; SizeOfUninitializedData UNUSED	; VirtualSize
    dd pe_entry-image_base		      ; AddressOfEntryPoint		; VirtualAddress
    dd codesize				      ; BaseOfCode UNUSED		; SizeOfRawData
    dd pe_entry-image_base		      ; BaseOfData UNUSED		; PointerToRawData
    dd 0x400000                               ; ImageBase			; PointerToRelocations UNUSED
    dd sectalign  ; e_lfanew                  ; SectionAlignment		; PointerToLinenumbers UNUSED
    dd filealign                              ; FileAlignment			; NumberOfRelocations UNUSED
										; NumberOfLinenumbers UNUSED
    dw 4                                      ; MajorOSVersion UNUSED		; Characteristics UNUSED 
    dw 0                                      ; MinorOSVersion UNUSED
    dw 0                                      ; MajorImageVersion UNUSED
    dw 0                                      ; MinorImageVersion UNUSED
    dw 4                                      ; MajorSubsystemVersion
    dw 0                                      ; MinorSubsystemVersion UNUSED
    dd 0                                      ; Win32VersionValue UNUSED
    dd ((hdrsize + sectalign-1) and -sectalign) + ((codesize + sectalign-1) and -sectalign)
				              ; SizeOfImage

    ddround hdrsize, filealign                ; SizeOfHeaders
    dd 0                                      ; CheckSum UNUSED
    dw subsystem			      ; Subsystem (Win32 CONSOLE)	;GUI = 2, CONSOLE = 3
    dw 0x400                                  ; DllCharacteristics UNUSED
    dd 0x100000                               ; SizeOfStackReserve
    dd 0x1000                                 ; SizeOfStackCommit
    dd 0x100000                               ; SizeOfHeapReserve
    dd 0x1000                                 ; SizeOfHeapCommit UNUSED
    dd 0                                      ; LoaderFlags UNUSED
    dd DIRECTORY_COUNT			      ; NumberOfRvaAndSizes

; The debug directory size at offset 0x34 from here must be 0
if DIRECTORY_COUNT > 0
    dq 0			;export
    dd RVA idata		;import section rva
    dd idata.size		;import section size
    RESOURCE_DIRECTORY_HEADER:
    dd 0x0C dup 0		;can try remove / adjust this line & hope that the debug directory size will be zero
end if

if SECTION_COUNT > 1
    secthdr:    dd SECTION_COUNT * 10 dup 0
end if

hdrsize = $ - $$
	dll_tables
	idata:
if defined IMPORT_DIR
	store_section_header secthdr, '.idata', SECTION_0-pe_entry, pe_entry, _code + _readable + _executable + _writeable
	dll_imports
end if
	.size = $-idata
	dll_strings
}

macro EndPE {
	match count,section_count \{  SECTION_\#count: \}
	if defined IMPORT_DIR
		SECTION_COUNT = section_count + 1
	else
		SECTION_COUNT = section_count
	end if
	if defined RESOURCE_DIR
		DIRECTORY_COUNT = 3
	else if defined IMPORT_DIR
		DIRECTORY_COUNT = 2
	else
		DIRECTORY_COUNT = 0
	end if
	codesize = $ - pe_entry
	filesize = $ - $$ }

;; ---------------------- macros to supoort win32 invokes -----------------------------
macro	pushd [args] {
	reverse
	match any, args \{
		local ..continue
		if any eqtype ''
		    CALL ..continue		;use the opcode CALL and not the macro call
		    db any,0
		    ..continue:
		else
		    pushd any
		end if \}
}

macro	call proc,[args] {
	common 
	pushd args
	call proc }

macro	invoke proc,[args] {
	common
	pushd args
	if defined proc
		call dword [proc]
	else if defined proc#A
		call dword [proc#A]
	else if defined proc#W
		call dword [proc#W]
	end if
}

define	RVA -image_base+adjust+

;Basic WIN32 includes
struc TCHAR [val] { common match any, val \{ . db val \}
                           match , val \{ . db ? \} }
;include 'include/macro/struct.inc'
;include 'include/equates/kernel32.inc'
;include 'include/equates/user32.inc'
;include 'include/equates/gdi32.inc'
;include 'include/equates/comctl32.inc'
;include 'include/equates/comdlg32.inc'
;include 'include/equates/shell32.inc'
;include 'include/equates/wsock32.inc'
 