flat assembler
Message board for the users of flat assembler.

Index > Windows > Get command line args on Windows

Author
Thread Post new topic Reply to topic
rc



Joined: 03 Nov 2019
Posts: 55
Location: Germany
rc 15 Feb 2022, 14:15
Does somebody know how to access the command line arguments on windows? In linux they get pushed onto the stack when the program launches and i just have to save esp before doing anything else. But in windows they dont get pushed onto the stack.

This is the test code i have written:

Code:
format PE console
include 'WIN32A.INC'
entry _start

_start:


; --- Section Text/Code ---
; Program code
section '.code'    code    readable    executable

;--- Load app args pointer ---
    mov [args_ptr], esp    ; args_ptr must be defined in segment bss

;--- Get argc ---
    mov eax, [args_ptr]
    mov eax, [eax]
    push eax

; --- Printf ---
    call    [printf]   ; output is garbage but expected is 1 (the executable name by default)
    add esp, 8


; --- Section Bss ---
section '.bss'    readable    writeable
; --- Reserve args pointer (dword = 32bit) ---
    args_ptr    rd 1


; --- Section Import data ---
; Import data
section '.idata'    import    data    readable    writeable
    library kernel32, 'kernel32.dll',\
            msvcrt, 'msvcrt.dll'

    include 'API/KERNEL32.INC'\

    import msvcrt,\
        printf, 'printf',\
        getChar, '_fgetchar'

    


I have debugged the code with x32dbg and i saw that no arguments get automatically pushed onto the stack in windows. So the above code cannot work.
How do i get the command line args on Windows?
Post 15 Feb 2022, 14:15
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20768
Location: In your JS exploiting you and your system
revolution 15 Feb 2022, 14:36
Code:
invoke GetCommandLineA
invoke GetCommandLineW
;or
invoke GetCommandLine    
https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getcommandlinea
Post 15 Feb 2022, 14:36
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 1198
Location: Russia
macomics 15 Feb 2022, 19:38
+ CommandLineToArgvW
Post 15 Feb 2022, 19:38
View user's profile Send private message Reply with quote
FlierMate



Joined: 21 Jan 2021
Posts: 219
FlierMate 16 Feb 2022, 05:04
There is an example "Command line parameters" (Handling the command line parameters in Windows)

https://flatassembler.net/examples.php
Post 16 Feb 2022, 05:04
View user's profile Send private message Reply with quote
Walter



Joined: 26 Jan 2013
Posts: 158
Walter 16 Feb 2022, 17:22
Not anything better than those already mentioned but just an example of an additional way to do it.

Code:
;************
;* Args.asm *
;************

format PE CONSOLE
entry start

include 'win32ax.inc'

section '.text' code readable executable

  start:

    cinvoke __getmainargs,argc,argv,env,0,NULL

    cinvoke printf,<"Argc count is %d",13,10>,[argc]
    mov     eax,[argv]
    cinvoke printf,<"Program name is %s",13,10>,dword [eax]

    mov     ebx,1

.while ebx < [argc]
    add     [argv],4
    mov     eax,[argv]
    cinvoke printf,<"argv[%d] = %s",13,10>,ebx,dword [eax]
    inc     ebx
.endw

    invoke  ExitProcess,0

section '.data' data readable writeable

  argc dd ?
  argv dd ?
  env dd ?

section '.idata' import data readable writeable

  library kernel32,'kernel32.dll',\
          msvcrt,'msvcrt.dll'

  import kernel32,\
         ExitProcess,'ExitProcess'

  import msvcrt,\
         __getmainargs,'__getmainargs',\
         printf,'printf'
    


Image
Post 16 Feb 2022, 17:22
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8466
Location: Kraków, Poland
Tomasz Grysztar 16 Feb 2022, 19:35
OK, then we are missing an example using CommandLineToArgvW, so let me try:
Code:
format PE GUI 4.0
include 'win32w.inc'

ID_DIALOG = 100
ID_LIST   = 101

section '.text' code readable executable

        entry   $
        invoke  GetModuleHandle,0
        invoke  DialogBoxParam,eax,ID_DIALOG,HWND_DESKTOP,DialogProc,0
        invoke  ExitProcess,0

proc DialogProc uses ebx esi, hwnd,msg,wparam,lparam
    locals
       num dd ?
    endl
        push    ebx esi edi
        cmp     [msg],WM_INITDIALOG
        je      .wminitdialog
        cmp     [msg],WM_COMMAND
        je      .wmcommand
        cmp     [msg],WM_CLOSE
        je      .close
        xor     eax,eax
        jmp     .finish
  .wminitdialog:
        invoke  GetDlgItem,[hwnd],ID_LIST
        mov     ebx,eax
        invoke  GetCommandLine
        lea     edx,[num]
        invoke  CommandLineToArgv,eax,edx
        test    eax,eax
        jz      .close
        mov     esi,eax
      .add:
        lodsd
        invoke  SendMessage,ebx,LB_ADDSTRING,0,eax
        dec     [num]
        jnz     .add
        jmp     .processed
  .wmcommand:
        cmp     [wparam],BN_CLICKED shl 16 + IDCANCEL
        jne     .processed
  .close:
        invoke  EndDialog,[hwnd],0
  .processed:
        mov     eax,1
  .finish:
        pop     edi esi ebx
        ret
endp

section '.idata' import data readable writeable

  library kernel,'KERNEL32.DLL',\
          user,'USER32.DLL',\
          shell,'SHELL32.DLL'

  import kernel,\
         GetModuleHandle,'GetModuleHandleW',\
         GetCommandLine,'GetCommandLineW',\
         ExitProcess,'ExitProcess'

  import user,\
         DialogBoxParam,'DialogBoxParamW',\
         GetDlgItem,'GetDlgItem',\
         SendMessage,'SendMessageW',\
         EndDialog,'EndDialog'

  import shell,\
         CommandLineToArgv,'CommandLineToArgvW'

section '.rsrc' resource data readable

  directory RT_DIALOG,dialogs

  resource dialogs,\
           ID_DIALOG,LANG_ENGLISH+SUBLANG_DEFAULT,main

  dialog main,'Argv',80,80,220,200,WS_CAPTION+WS_POPUP+WS_SYSMENU+DS_MODALFRAME
    dialogitem 'LISTBOX','',ID_LIST,10,10,200,180,WS_VISIBLE+WS_VSCROLL+LBS_DISABLENOSCROLL
  enddialog    
Post 16 Feb 2022, 19:35
View user's profile Send private message Visit poster's website Reply with quote
Walter



Joined: 26 Jan 2013
Posts: 158
Walter 16 Feb 2022, 22:47
Very eloquent example!
That should make for some good choices for the OP.
Post 16 Feb 2022, 22:47
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 1198
Location: Russia
macomics 17 Feb 2022, 10:12
I got alternative algorithms from my library to parse the command line. The main function transfers the text to the stack and clearing the memory can be done by simply loading the saved stack top when exiting the calling function. The archive contains versions for 32 and 64 bit sources, as well as versions of functions for ANSI and Unicode. I also cleaned the code from my macros, so it no longer requires any additional include files.
Code:
section 'text' code readable executable
entry $
    push rbp
    mov rbp, rsp
    sub rsp, 32
    call [GetCommandLineA]
    mov rsp, rbp
    call CmdLine2ArgvA ; return rax = rsp = argv
    mov r8, rax

; word [rax + 0] = offset argv[0] = argc * 2
; word [rax + 2] = offset argv[1]
; . . .
; word [rax + 2 * N] = offset argv[argc - 1]
; byte [rax + 2 * N + 2] = copy GetCommandLineA

    call CmdLineCount ; return rax = argc;
    call main
    mov ecx, eax
    lea rsp, [rbp - 32]
    call [ExitProcess]

main: ; rax = argc, r8 = argv
    push rbp
    mov rbp, rsp
    mov rax, r8
    mov edx, 1
    call CmdLineIndex ; return rax = argv[1] or CPU_FL_CF
    mov rax, r8
    xor edx, edx
    call CmdLineRmov ; remove argv[0]
. . .
    xor eax, eax
    mov rsp, rbp
    pop rbp
    retn

include 'WinCmdLn\x64.ASM'
    


Description: Fixed a couple of typos.
Download
Filename: WinCmdLn.zip
Filesize: 1.83 KB
Downloaded: 532 Time(s)

Post 17 Feb 2022, 10:12
View user's profile Send private message Reply with quote
chastitywhiterose



Joined: 13 Oct 2025
Posts: 18
chastitywhiterose 06 Nov 2025, 05:50
Here is my way of handling command line arguments in Windows. It is a command line program that first prints the entire command line from the GetCommandLineA W32 API function. Then it prints the number of bytes of the string. Then it prints each argument on a separate line. It uses the same filter I wrote for my DOS programs to turn spaces into zeroes and then finds the next beginning of a string to loop through them all until the end. It uses my own IO library to display strings to the console so I will attach that to this post too.

Code:
format PE console
include 'win32ax.inc'
include 'chastelibw32.asm'

main:

mov [radix],10 ; Choose radix for integer output.
mov [int_width],1

;get command line argument string
call [GetCommandLineA]

mov [arg_start],eax ;back up eax to restore later

;short routine to find the length of the string
;and whether arguments are present
mov ebx,eax
find_arg_length:
cmp [ebx], byte 0
jz found_arg_length
inc ebx
jmp find_arg_length
found_arg_length:
;at this point, ebx has the address of last byte in string which contains a zero
;we will subtract to get and store the length of the string
mov [arg_end],ebx
sub ebx,eax
mov eax,ebx
mov [arg_length],eax

;display the arg string to make sure it is working correctly
mov eax,[arg_start]
call putstring
call putline

;print the length in bytes of the arg string
mov eax,[arg_length]
call putint

;this loop will filter the string, replacing all spaces with zero
mov ebx,[arg_start]
arg_filter:
cmp byte [ebx],' '
ja notspace ; if char is above space, leave it alone
mov byte [ebx],0 ;otherwise it counts as a space, change it to a zero
notspace:
inc ebx
cmp ebx,[arg_end]
jnz arg_filter

arg_filter_end:

;optionally print first arg (name of program)
mov eax,[arg_start]
call putstring
call putline

;this loop is very safe because it only prints arguments if they are valid
;if the end of the args are reached by comparison of eax with [arg_end]
;then it will jump to args_none and proceed from there
args_list:
call get_next_arg
cmp eax,[arg_end]
jz args_none
call putstring
call putline
jmp args_list
args_none:

;Exit the process with code 0
push 0
call [ExitProcess]

.end main

arg_start  dd 0 ;start of arg string
arg_end    dd 0 ;address of the end of the arg string
arg_length dd 0 ;length of arg string
arg_spaces dd 0 ;how many spaces exist in the arg command line

;function to move ahead to the next art
;only works after the filter has been applied to turn all spaces into zeroes
get_next_arg:
mov ebx,[arg_start]
find_zero:
cmp byte [ebx],0
jz found_zero
inc ebx
jmp find_zero ; this char is not zero, go to the next char
found_zero:

find_non_zero:
cmp ebx,[arg_end]
jz arg_finish ;if ebx is already at end, nothing left to find
cmp byte [ebx],0
jnz arg_finish ;if this char is not zero we have found the next string!
inc ebx
jmp find_non_zero ;otherwise, keep looking

arg_finish:
mov [arg_start],ebx ; save this index to variable
mov eax,ebx ;but also save it to ax register for use
ret
;we can know that there are no more arguments when
;the either [arg_start] or eax are equal to [arg_end]
    


Description:
Download
Filename: chastelibw32.asm
Filesize: 5.65 KB
Downloaded: 2 Time(s)

Post 06 Nov 2025, 05:50
View user's profile Send private message Send e-mail Reply with quote
Mаt Quasar



Joined: 29 Jun 2025
Posts: 45
Mаt Quasar 06 Nov 2025, 15:34
Need to process command-line argument in quotes too.
Post 06 Nov 2025, 15:34
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.