Author Topic: Executable becomes invalid as data section exceeds 1 page  (Read 6295 times)

Offline alexbnc

  • Jr. Member
  • *
  • Posts: 6
Executable becomes invalid as data section exceeds 1 page
« on: September 02, 2013, 05:37:23 PM »
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:

Code: [Select]
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.html
Corrupt exe: http://www.buhnicialexandru.info/workspace/test/PEInfo_Test_Corrupt.exe.html

Maybe you can spot the problem from the difference. I am unable to see why. Thanks