News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Handcrafted PE binary

Started by japheth, September 06, 2009, 09:11:29 AM

Previous topic - Next topic

japheth

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

BlackVortex


dedndave

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

japheth


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