For months have I been trying to finish this and still I have some frustrating problems. If you manage to remain sane after trying to create PE files with assembly then nothing else can kill you. And is what I struggle to achieve. So, I have been trying to create a PE file with NASM. Finally I managed to build a nicely small-sized running executable showing a message on a console. At that time, the .data section had less than 512 bytes but now, as I added some bigger variables that exceed 512 bytes I get a 'This is not a valid Win32 application' error when trying to execute it.
In first place, my code:
BYTES_LAST_PAGE equ EXE_HEADER - DOS_HEADER
SECTION_ALIGNMENT equ 4096
FILE_ALIGNMENT equ 512
PREFERRED_ADDRESS equ 4194304
SECTIONS_COUNT equ 4
HEADERS_SIZE equ HEADERS_END - DOS_HEADER
CODE_SIZE equ CODE_END - CODE
DATA_SIZE equ DATA_END - DATA
IDATA_SIZE equ IDATA_END - IDATA
ITABLE_SIZE equ ITABLE_END - ITABLE
BSS_SIZE equ 531
; Round to the next multiple of the second argument
%define Round(N, M) (N + M - 1)/M * M
; Calculates the RVA to the sections beginning
%define RVA(BaseAddress) (BaseAddress - CODE)/FILE_ALIGNMENT * SECTION_ALIGNMENT + \
SECTION_ALIGNMENT
; Calculates the RVA to the specified address
%define RVA(Address, BaseAddress) RVA(BaseAddress) + (Address- BaseAddress)
; The size of the image rounded up to the next multiple of SECTION_ALIGNMENT
%define ImageSize RVA(IMAGE_END) + Round(BSS_SIZE, SECTION_ALIGNMENT)
; Fill with argument 1 argument 2 times
%macro Fill 2
times %1 db %2
%endmacro
DOS_HEADER:
db "MZ"
dw BYTES_LAST_PAGE
dw 1
dw 4
dw 4
dw 0
dw 65535
dw 0
dw 0x00B8
dw 0
dw 0
dw 0
dw 0x0040
dw 0
dq 0
dw 0
dw 0
Fill 20, 0
dd EXE_HEADER
DOS_PROGRAM:
push cs
pop ds
mov dx, DOSMessage - DOS_PROGRAM
mov ah, 9
int 21h
mov ax, 0x4C01
int 21h
DOSMessage :
db "This program wasn't created for your system!", 0Dh, 0Dh, 0Ah, '$'
Fill 64-$+DOS_PROGRAM, 0
EXE_HEADER:
db "PE", 0, 0
dw 0x014C
dw SECTIONS_COUNT
dd 1371668450
dd 0
dd 0
dw SECTIONS_HEADERS_TABLE - OPTIONAL_HEADER
dw 0x0002|0x0004|0x0008|0x0100|0x0200
OPTIONAL_HEADER:
dw 0x010B
db 0
db 0
dd Round(CODE_SIZE, SECTION_ALIGNMENT)
dd Round(DATA_SIZE, SECTION_ALIGNMENT)
dd Round(BSS_SIZE, SECTION_ALIGNMENT)
dd RVA(CODE)
dd RVA(CODE)
dd RVA(DATA)
dd PREFERRED_ADDRESS
dd SECTION_ALIGNMENT
dd FILE_ALIGNMENT
dw 4
dw 0
dw 0
dw 0
dw 3
dw 10
dd 0
dd ImageSize
dd Round(HEADERS_SIZE, FILE_ALIGNMENT)
dd 0
dw 3 ; subsys interface
dw 0 ; no dll
dd 4096 ; reserved stack
dd 4096
dd 65536
dd 0
dd 0
dd 16
dd 0
dd 0
dd RVA(ITABLE, IDATA)
dd ITABLE_SIZE
Fill 112, 0
SECTIONS_HEADERS_TABLE:
CODE_SECTION_HEADER:
db ".code", 0, 0, 0
dd Round(CODE_SIZE, SECTION_ALIGNMENT)
dd RVA(CODE)
dd Round(CODE_SIZE, FILE_ALIGNMENT)
dd CODE
dd 0
dd 0
dw 0
dw 0
dd 0x00000020|0x20000000|0x40000000|0x80000000
IDATA_SECTION_HEADER:
db ".idata", 0, 0
dd Round(IDATA_SIZE, SECTION_ALIGNMENT)
dd RVA(IDATA)
dd Round(IDATA_SIZE, FILE_ALIGNMENT)
dd IDATA
dd 0
dd 0
dw 0
dw 0
dd 0x00000040|0x40000000|0x80000000
DATA_SECTION_HEADER:
db ".data", 0, 0, 0
dd Round(DATA_SIZE, SECTION_ALIGNMENT)
dd RVA(DATA)
dd Round(DATA_SIZE, FILE_ALIGNMENT)
dd DATA
dd 0
dd 0
dw 0
dw 0
dd 0x00000040|0x40000000|0x80000000
BSS_SECTION_HEADER:
db ".bss", 0, 0, 0, 0
dd Round(BSS_SIZE, SECTION_ALIGNMENT)
dd RVA(BSS)
dd 0
dd 0
dd 0
dd 0
dw 0
dw 0
dd 0x00000080|0x40000000|0x80000000
HEADERS_END:
align FILE_ALIGNMENT
use32
; BSS variables relative address
StdHandle equ RVA(BSS) + 0
WrittenChars equ RVA(BSS) + 4
BigVar equ RVA(BSS) + 8
CODE:
call GetStdHandle
push dword PREFERRED_ADDRESS + RVA(Message, DATA)
push dword 6
call ShowText
ApplicationEnd:
mov eax, 0
ret
GetStdHandle:
push -11
call dword [PREFERRED_ADDRESS + RVA(F_GetStdHandle, IDATA)]
mov dword [PREFERRED_ADDRESS + StdHandle], eax
ret
ShowText:
push ebp
mov ebp, esp
push 0
push dword PREFERRED_ADDRESS + WrittenChars
push dword [ebp + 8]
push dword [ebp + 12]
push dword [PREFERRED_ADDRESS + StdHandle]
call dword [PREFERRED_ADDRESS + RVA(F_WriteConsoleW, IDATA)]
pop ebp
ret 8
align FILE_ALIGNMENT
CODE_END:
IDATA:
KERNEL32 db 'kernel32.dll', 0
ITABLE:
.originalfthk dd 0
.datehour dd 0
.forwarder dd 0
.module_name dd RVA(KERNEL32, IDATA)
.start dd RVA(IFUNCTIONS, IDATA)
Fill 20, 0
ITABLE_END:
I_WriteConsoleW:
dw 0
db 'WriteConsoleW', 0
align 2
I_GetStdHandle:
dw 0
db 'GetStdHandle', 0
align 2
I_ExitProcess:
dw 0
db 'ExitProcess', 0
IFUNCTIONS:
F_WriteConsoleW: dd RVA(I_WriteConsoleW, IDATA)
F_GetStdHandle: dd RVA(I_GetStdHandle, IDATA)
F_ExitProcess dd RVA(I_ExitProcess, IDATA)
dd 0
align FILE_ALIGNMENT
IDATA_END:
DATA:
Message: dw __utf16__("Hello"), 0Ah
SomeBigVar:
Fill 512, 11h ; Make it exceed 512 bytes
align FILE_ALIGNMENT
DATA_END:
BSS:
IMAGE_END:
If you change the line below
SomeBigVar to
Fill 10, 11h you will see the message. So, for some unknown reason the executable becomes invalid as long as the data section becomes as big as 2 pages. I also analyzed the file with PEInfo and these are the results:
Working exe:
http://www.buhnicialexandru.info/workspace/test/PEInfo_Test_Working.exe.htmlCorrupt exe:
http://www.buhnicialexandru.info/workspace/test/PEInfo_Test_Corrupt.exe.htmlMaybe you can spot the problem from the difference. I am unable to see why. Thanks