The MASM Forum Archive 2004 to 2012

Specialised Projects => Assembler/Compiler Technology => Topic started by: japheth on September 06, 2009, 09:11:29 AM

Title: Handcrafted PE binary
Post by: japheth on September 06, 2009, 09:11:29 AM
Hello,

recently support for the IMAGEREL operator was added to JWasm's binary output format. This makes it relatively easy to create PE binaries from scratch, without linker, using this format. A nice toy, and should help the paranoids who don't trust linkers, in peculiar MS link.

Here's the code ( it's also included in JWasm's sample directory )


;--- Win32 "hello world" console application.
;--- Uses JWasm's bin output format, so no linker needed.
;--- assemble: JWasm -bin -Fo Win32_5.exe Win32_5.ASM

    .386
    option casemap:none

    .nolist
    include winnt.inc   ;some common PE struct definitions
    .list

STD_OUTPUT_HANDLE equ -11

IMAGEBASE equ 400000h

PEHDR segment dword FLAT

;--- define the DOS "MZ" header

    ORG IMAGEBASE

    IMAGE_DOS_HEADER <"ZM", 80h, 1, 0,4,0,-1,0,200h,0,0,0,0,0,<0>,0,0,<0>,IMAGEREL PEHdr>

    db 0Eh         ;push cs
    db 1Fh         ;pop ds
    db 0BAh,0Eh,0  ;mov dx,text
    db 0B4h,09h    ;mov ah,9
    db 0CDh,21h    ;int 21h
    db 0B8h,01h,4Ch;mov ax,4c01h
    db 0CDh,21h    ;int 21h
    db "This program cannot be run in DOS mode",13,10,'$'

    ORG IMAGEBASE+80h

;--- define the Win32 "PE" header

PEHdr label byte
    db "PE",0,0
    IMAGE_FILE_HEADER <IMAGE_FILE_MACHINE_I386, num_sections, 0, 0, 0, sizeof IMAGE_OPTIONAL_HEADER32,
        IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_LOCAL_SYMS_STRIPPED>

    IMAGE_OPTIONAL_HEADER32 { 10Bh, ;magic
        6,0,                        ;linker major, minor
        1000h,1000h,0,              ;sizeof code, initialized data, uninitialized data
        IMAGEREL mainCRTStartup,    ;entry point
        IMAGEREL start_text, IMAGEREL start_const,  ;baseof code, data
        IMAGEBASE,    ;imagebase
        1000h,200h,   ;section alignment, file alignment
        4,0,          ;OS major, minor
        0,0,          ;Image major, minor
        4,0,          ;Subsys major, minor
        0,            ;win32 version
        3000h,        ;sizeof image
        1000h,        ;sizeof header
        0,            ;checksum
        IMAGE_SUBSYSTEM_WINDOWS_CUI,
        0,            ;dll characteristics
        100000h,1000h,;stack res,com
        100000h,1000h,;heap res, com
        0,            ;loader flags
        16,           ;number of directories
        <<0,0>,                               ;exports
        < IMAGEREL imports, sizeof_imports >, ;imports
        <0,0>,<0,0>,                          ;resource, exception
        <>,<>,<>,<>,                          ;security, baserelocs, debug, architecture
        <>,<>,<>,<>,                          ;globalptr, tls, load_config, bound_import
        <>,<>,<>,<>>}                         ;iat, delay_import, com descriptor, reserved

;--- define the section table

sectiontable label byte
    IMAGE_SECTION_HEADER <".text", <sizeof_text>, IMAGEREL start_text, sizeof_text,
        200h, 0, 0, 0, 0, 060000020h >
    IMAGE_SECTION_HEADER <".rdata", <sizeof_const>, IMAGEREL start_const, sizeof_const,
        400h, 0, 0, 0, 0, 040000040h >
num_sections equ ( $ -  sectiontable ) / sizeof IMAGE_SECTION_HEADER

    ORG IMAGEBASE+200h   ;forces physical size of header to 200h and sets VA to 400200h

PEHDR ends

;--- do some VA arithmetic. This is needed because
;--- section alignment and file alignment are different

_TEXT segment dword public FLAT 'CODE'
    ORG 0E00h   ; changes VA from 400200 to 401000h
_TEXT ends
CONST segment dword public FLAT 'DATA'
    ORG 0E00h   ; changes VA from 401200 to 402000h
CONST ends

CONST segment

start_const label byte

DefineImport macro name
lp&name typedef ptr pr&name
name    lp&name IMAGEREL n&name
    endm
DefineName macro name
n&name dw 0
    db @CatStr(!",name, !"),0
    align 4
    endm

imports label byte
    IMAGE_IMPORT_DESCRIPTOR <<IMAGEREL importILT>,0,0,IMAGEREL kernel32, IMAGEREL importIAT>
    IMAGE_IMPORT_DESCRIPTOR <<0>,0,0,0,0>

prWriteConsoleA typedef proto stdcall :dword, :dword, :dword, :dword, :dword
prGetStdHandle  typedef proto stdcall :dword
prExitProcess   typedef proto stdcall :dword

importILT label dword
    dd IMAGEREL nExitProcess
    dd IMAGEREL nWriteConsoleA
    dd IMAGEREL nGetStdHandle
    dd 0

importIAT label dword
    DefineImport ExitProcess
    DefineImport WriteConsoleA
    DefineImport GetStdHandle
    dd 0

kernel32 db "kernel32.dll",0
    align 4

    DefineName ExitProcess
    DefineName WriteConsoleA
    DefineName GetStdHandle

sizeof_imports equ $ - imports

CONST ends

CONST segment

string  db 13,10,"hello, world.",13,10

sizeof_const equ $ - start_const

    ORG 1000h    ;align size of CONST to next 512 byte boundary

CONST ends

_TEXT segment

start_text label near

;--- start of program

main proc

local dwWritten:dword
local hConsole:dword

    invoke  GetStdHandle, STD_OUTPUT_HANDLE
    mov     hConsole,eax

    invoke  WriteConsoleA, hConsole, addr string, sizeof string, addr dwWritten, 0

    xor     eax,eax
    ret
main endp

;--- entry

mainCRTStartup proc c

    invoke  main
    invoke  ExitProcess, eax

mainCRTStartup endp

sizeof_text equ $ - start_text

    ORG 1000h    ;align size of _TEXT to next 512 byte boundary

_TEXT ends

    END


restriction: no fixups are generated, so it's probably not a good idea to create Win32 dlls this way.

japheth
Title: Re: Handcrafted PE binary
Post by: BlackVortex on September 06, 2009, 12:29:15 PM
Wow, pretty impressive !
Title: Re: Handcrafted PE binary
Post by: dedndave on September 06, 2009, 01:09:27 PM
i have had to browse through the header a few times
of course, they don't make it easy - lol
everything is in structure form - rather than offsets (i.e. you have to caclulate offsets, typically)
i don't know if i will ever need this to make a pe header, but it sure is a nice reference to find your way around in one   :U
Title: Re: Handcrafted PE binary
Post by: japheth on September 07, 2009, 01:33:21 PM

I agree, it's a bit complicated currently.

But once operator SECTIONREL is accepted with binary format, the sample can be simplified significantly. Oh well, it's a toy ...