flat assembler
Message board for the users of flat assembler.

Index > Windows > [fasm2] Unknown pause when running simple win 64bit console

Author
Thread Post new topic Reply to topic
neeraj9



Joined: 03 Apr 2025
Posts: 12
neeraj9 03 Apr 2025, 23:53
I am just starting out with assembly in fasm and trying to play with a bit before investing time looking at some of the finer details. It also means that I am trying to run before crawling and might miss anything obvious (so please excuse for asking basic questions).

My 32bit version on windows works like a charm in console and terminates quickly. So, no issues there but pasting here for reference.

Code:
format PE large NX console 4.0
entry start

include 'win32a.inc'

section '.data' data readable writeable
    hello db 'Hello, World!', 0
    hello_len = $ - hello - 1 ; Calculate the length of the string excluding the null terminator

section '.text' code readable executable
start:
    invoke GetStdHandle, STD_OUTPUT_HANDLE
    mov [stdout], eax

    ; Write the string to stdout
    invoke WriteFile, [stdout], hello, hello_len, NULL, NULL

    ; Exit the program
    invoke ExitProcess, 0

section '.bss' readable writeable
    stdout dw ?

section '.idata' import data readable writeable
    library kernel32, 'kernel32.dll'
    include 'api/kernel32.inc'
    


I worked on 64bit on similar lines and the program compiles, but when I try to run it on console then after priniting "Hello, World!" the program freezes and then after 5-10 seconds terminates. I tried to run this in x64dbg and stepped through it withing any pause there. I stepped through and did not see any weird instruction resulting into unnecessary pause, so I am confused at this point. Any, pointers or debugging steps will help.

Note: I did see two processes lauched in windows TaskManager when running the 64 bit flavour in cmd.exe (terminal) and one is suspended while other runs (both are detected as 64bit).

Code:
format PE64 console 5.0
entry start

include 'win64a.inc'

section '.data' data readable writeable
    hello db 'Hello, World!', 0
    hello_len = $ - hello - 1 ; Calculate the length of the string excluding the null terminator

section '.text' code readable executable
start:
    invoke GetStdHandle, STD_OUTPUT_HANDLE
    mov [stdout], rax

    ; Write the string to stdout
    invoke WriteFile, [stdout], hello, hello_len, NULL, NULL

    ; Exit the program
    invoke ExitProcess, 0

section '.bss' readable writeable
    stdout dq ?

section '.idata' import data readable writeable
    library kernel32, 'kernel32.dll'
    include 'api/kernel32.inc'
    
Post 03 Apr 2025, 23:53
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1708
Location: Toronto, Canada
AsmGuru62 04 Apr 2025, 00:05
I think after "start:" you need to align the stack to 16 with PUSH RAX, can you try it?
Also, I just tried your code and it does not freeze. I did not change anything. Strange.


Last edited by AsmGuru62 on 04 Apr 2025, 00:11; edited 1 time in total
Post 04 Apr 2025, 00:05
View user's profile Send private message Send e-mail Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4227
Location: vpcmpistri
bitRAKE 04 Apr 2025, 00:09
The common occurrences of this problem is stack alignment. The ABI requires the shadow-space to be aligned, 0 (mod 16) - the least four bits of the address need to be zero. This is because the OS is using XMM registers and aligned move instructions. You can verify this within the debugger: on entry you'll see the RSP register value is congruent 8 (mod 16).
Post 04 Apr 2025, 00:09
View user's profile Send private message Visit poster's website Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1708
Location: Toronto, Canada
AsmGuru62 04 Apr 2025, 00:11
I did not align the stack and ran the code as is --- no freeze.
Post 04 Apr 2025, 00:11
View user's profile Send private message Send e-mail Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4227
Location: vpcmpistri
bitRAKE 04 Apr 2025, 00:15
Every build of the OS can use the stack differently.
It's certainly possible another issue accounts for the freeze.
Post 04 Apr 2025, 00:15
View user's profile Send private message Visit poster's website Reply with quote
Mat-Quasar



Joined: 02 Mar 2025
Posts: 82
Mat-Quasar 04 Apr 2025, 01:05
I think the parameter lpNumberOfBytesWritten cannot be null.

Also, you don't need a null terminating character.
Post 04 Apr 2025, 01:05
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4227
Location: vpcmpistri
bitRAKE 04 Apr 2025, 02:09
Mat-Quasar wrote:
I think the parameter lpNumberOfBytesWritten cannot be null.
Very good observation.
WriteFile
Quote:
This parameter can be NULL only when the lpOverlapped parameter is not NULL.
Windows 7: This parameter can not be NULL.

_________________
¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup
Post 04 Apr 2025, 02:09
View user's profile Send private message Visit poster's website Reply with quote
neeraj9



Joined: 03 Apr 2025
Posts: 12
neeraj9 04 Apr 2025, 04:20
Thanks. I will try the suggestions.

In the meantime, I used chatgpt to generate a simple hello world for fasm on 64 bit and the generated code (see below) froze at the end after printing hello world as well.

Code:
format PE64 console
entry main

include 'win64a.inc'

section '.text' code readable executable

main:
    sub     rsp, 40                 ; Shadow space for Win64 calling convention
    lea     rcx, [msg]              ; First argument: pointer to string
    call    [printf]                ; Call printf function
    add     rsp, 40                 ; Restore stack
    
    xor     ecx, ecx                ; Exit code 0
    call    [ExitProcess]           ; Terminate program

section '.data' data readable writeable
    msg db 'Hello, World!', 10, 0   ; String with newline and null terminator

section '.idata' import data readable writeable
    library kernel32, 'kernel32.dll', msvcrt, 'msvcrt.dll'
    import kernel32, ExitProcess, 'ExitProcess'
    import msvcrt, printf, 'printf'
    


The following is noticeable in TaskManager, since the binary lingers on a bit for me to capture the info. I am not sure if this is normal or must be investigated.

| Process Name | PID | Status | User | CPU | Memory | Arch | Description |
|-----------------|-------|----------|------|----|--------|------|------------------|
| hello64bit.exe | 80928 | Running | neeraj9 | 00 | 20K | x64 | hello64.exe |
| hello64bit.exe | 76456 | Suspended | neeraj9 | 00 | 20K | x64 | Hello64 |
Post 04 Apr 2025, 04:20
View user's profile Send private message Reply with quote
Mat-Quasar



Joined: 02 Mar 2025
Posts: 82
Mat-Quasar 04 Apr 2025, 06:48
bitRAKE wrote:
Mat-Quasar wrote:
I think the parameter lpNumberOfBytesWritten cannot be null.
Very good observation.
WriteFile
Quote:
This parameter can be NULL only when the lpOverlapped parameter is not NULL.
Windows 7: This parameter can not be NULL.


Thank you. I experienced it myself as last time I did a lot of console apps.
Post 04 Apr 2025, 06:48
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20632
Location: In your JS exploiting you and your system
revolution 04 Apr 2025, 06:54
AsmGuru62 wrote:
I think after "start:" you need to align the stack to 16 with PUSH RAX, can you try it?
Just use push ebp. Not any other registers, not sub esp,4, nor some other instruction. This will make the code much easier to debug in a debugger. It can then unwind the stack and just generally make things work easier. There is no reason to use anything else IMO.

Also note that this code is 32-bit so stack alignment is not needed at all.
Post 04 Apr 2025, 06:54
View user's profile Send private message Visit poster's website Reply with quote
neeraj9



Joined: 03 Apr 2025
Posts: 12
neeraj9 04 Apr 2025, 08:27
The following code generated by better LLM model from google does a good job of handling the arguments and other things, but the binary still freezes at the end. I turned off my real-time protection for virus and threat protection in windows as well, but that doesnt change a thing.


Code:
; hello.asm (v2 - with stack cleanup before ExitProcess)
; A simple 64-bit Windows console application using FASM
; Prints "Hello, World!" and exits.

format PE64 Console    ; Target: 64-bit Portable Executable for Console subsystem
entry start           ; Define the entry point label

include 'win64a.inc'  ; Include FASM's standard Windows 64-bit definitions

; === Data Section ===
section '.rdata' data readable
  message db 'Hello, World!', 0Dh, 0Ah ; The string with CR LF (Windows newline)
  message_len = $ - message           ; Calculate string length at assembly time

; === Code Section ===
section '.code' code readable executable

  start:
    ; Allocate shadow space (32) + local variable space (8 for bytesWritten)
    ; = 40 bytes. Align to 16 bytes -> 48 bytes.
    sub rsp, 48

    ; Stack layout:
    ; [rsp+40] - [rsp+47]: bytesWritten (local)
    ; [rsp+32] - [rsp+39]: 5th argument space (lpOverlapped)
    ; [rsp+0]  - [rsp+31]: Shadow space

    ; 1. Get StdOut Handle
    mov ecx, STD_OUTPUT_HANDLE
    call [GetStdHandle]        ; RAX = handle
    mov rbx, rax               ; Save handle in a non-volatile register (optional, just for clarity)

    ; 2. Write Message
    mov rcx, rbx                ; Arg 1: hFile (saved handle)
    lea rdx, [message]          ; Arg 2: lpBuffer
    mov r8d, message_len        ; Arg 3: nNumberOfBytesToWrite
    lea r9, [rsp+40]            ; Arg 4: lpNumberOfBytesWritten (local var address)
    mov qword [rsp+32], 0       ; Arg 5: lpOverlapped = NULL (on stack)
    call [WriteFile]

    ; 3. Prepare for Exit
    ; Restore stack pointer (clean up space allocated at the beginning)
    ; Although ExitProcess won't return, this follows the calling convention
    ; for the code path leading up to the final call.
    add rsp, 48

    ; 4. Exit Process
    xor ecx, ecx                ; Arg 1: uExitCode = 0
    call [ExitProcess]          ; Terminate

; === Import Section ===
section '.idata' import data readable writeable
  library kernel32, 'KERNEL32.DLL'
  import kernel32, \
         GetStdHandle, 'GetStdHandle', \
         WriteFile,    'WriteFile',    \
         ExitProcess,  'ExitProcess'
    
Post 04 Apr 2025, 08:27
View user's profile Send private message Reply with quote
neeraj9



Joined: 03 Apr 2025
Posts: 12
neeraj9 04 Apr 2025, 08:29
Can one of you come up with a complete example which according to you should work.


In the meantime I am investigating exception visible in x64dbg for all of the 64 bit executable which I posted earlier (even the ones auto-generated by LLM).

Quote:

Debugging: G:\fasm2\test64bit.exe
Database file: G:\x64dbg\release\x64\db\test64bit.exe.dd64
Process Started: 0000000000400000 G:\fasm2\test64bit.exe
"G:\fasm2\test64bit.exe"
argv[0]: G:\fasm2\test64bit.exe
Breakpoint at 0000000000402000 (entry breakpoint) set!
DLL Loaded: 00007FF95EB60000 C:\Windows\System32\ntdll.dll
DLL Loaded: 00007FF95CD60000 C:\Windows\System32\kernel32.dll
DLL Loaded: 00007FF95C440000 C:\Windows\System32\KernelBase.dll
System breakpoint reached!
INT3 breakpoint "entry breakpoint" at <test64bit.OptionalHeader.AddressOfEntryPoint> (0000000000402000)!
EXCEPTION_DEBUG_INFO:
dwFirstChance: 1
ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
ExceptionFlags: 00000000
ExceptionAddress: kernelbase.00007FF95C49C3B6
NumberParameters: 2
ExceptionInformation[00]: 0000000000000000 Read
ExceptionInformation[01]: FFFFFFFFFFFFFFFF Inaccessible Address
First chance exception on 00007FF95C49C3B6 (C0000005, EXCEPTION_ACCESS_VIOLATION)!


The exception for code generated by google gemini (last one).

Quote:

Debugging: G:\fasm2\hello64_v2.exe
Database file: G:\x64dbg\release\x64\db\hello64_v2.exe.dd64
Process Started: 0000000000400000 G:\git\other\dev\fasm2\hello64_v2.exe
"G:\fasm2\hello64_v2.exe"
argv[0]: G:\fasm2\hello64_v2.exe
Breakpoint at 0000000000402000 (entry breakpoint) set!
DLL Loaded: 00007FF95EB60000 C:\Windows\System32\ntdll.dll
DLL Loaded: 00007FF95CD60000 C:\Windows\System32\kernel32.dll
DLL Loaded: 00007FF95C440000 C:\Windows\System32\KernelBase.dll
System breakpoint reached!
INT3 breakpoint "entry breakpoint" at <hello64_v2.OptionalHeader.AddressOfEntryPoint> (0000000000402000)!
EXCEPTION_DEBUG_INFO:
dwFirstChance: 1
ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
ExceptionFlags: 00000000
ExceptionAddress: kernelbase.00007FF95C49C3B6
NumberParameters: 2
ExceptionInformation[00]: 0000000000000000 Read
ExceptionInformation[01]: FFFFFFFFFFFFFFFF Inaccessible Address
First chance exception on 00007FF95C49C3B6 (C0000005, EXCEPTION_ACCESS_VIOLATION)!


Last edited by neeraj9 on 04 Apr 2025, 08:40; edited 3 times in total
Post 04 Apr 2025, 08:29
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20632
Location: In your JS exploiting you and your system
revolution 04 Apr 2025, 08:30
Now it is 64-bit code so stack alignment is mandatory. The stack is unaligned.
Code:
start:
push rbp ; align
sub rsp,6*8 ; enough room for 6 qwords    
Post 04 Apr 2025, 08:30
View user's profile Send private message Visit poster's website Reply with quote
neeraj9



Joined: 03 Apr 2025
Posts: 12
neeraj9 04 Apr 2025, 08:43
revolution wrote:
Now it is 64-bit code so stack alignment is mandatory. The stack is unaligned.
Code:
start:
push rbp ; align
sub rsp,6*8 ; enough room for 6 qwords    


Hey, this worked!
Its been a really long time since I did assembly, so took a while. Thanks everyone for awesome support.
Post 04 Apr 2025, 08:43
View user's profile Send private message Reply with quote
neeraj9



Joined: 03 Apr 2025
Posts: 12
neeraj9 04 Apr 2025, 08:51
bitRAKE wrote:
The common occurrences of this problem is stack alignment. The ABI requires the shadow-space to be aligned, 0 (mod 16) - the least four bits of the address need to be zero. This is because the OS is using XMM registers and aligned move instructions. You can verify this within the debugger: on entry you'll see the RSP register value is congruent 8 (mod 16).


It was indeed stack alignment and eventually the following worked for my previous code as well even though `lpNumberOfBytesWritten` was set to NULL.

Code:
start:
    push rbp
    


Note: In my specific case (very simple code) it `call [ExitProcess]` which was invoked with misaligned stack (in my case it was aligned to 8-byte and NOT 16-byte as required by win64 calling convention poined out by people here.

Leassons:

1. Run your code without stepping through the debugger first and see any exceptions. In my case I stepped through and did not see an exception (not sure why).
2. Read messages carefully and dig deeped into each of the aspects (I missed deep dive into bitRAKE message about stack alignment and revolution mentioned it a couple of times until the last one when I looked at it and fixed it eventually).
3. Dont blindly trust LLM. (every LLMs I asked generated wrong code although ChatGPT reserved space for win64 calling convention but then restored stack just before calling ExitProcess which was wrong).


Thanks everyone! Smile
Post 04 Apr 2025, 08:51
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.