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
Wow, pretty impressive !
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
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 ...