Build MASM32 windows x86 GetCommandLineToArgvW

I would like to ask how to use CommandLineToArgvW function in x86 assembly. I'm having trouble with that. For today, I can print the number of arguments along with the program execution in cmd. I would like to store the arguments in different variables. How can i do this?

My code looks like this:

include \masm32\include\masm32rt.inc

.data
  Format db "%d", 10, 0

.data?
  Arguments db 100 dup(?)

.code

start:

  mov esi, offset Arguments

  push ebp
  mov ebp, esp
  sub esp, 4

  call GetCommandLineW

  lea ecx, dword ptr[ebp - 4]
  push ecx
  push eax
  call CommandLineToArgvW

  mov esi, eax
  push offset Arguments
  call StdOut

  push dword ptr [ebp - 4]
  push offset Format
  call crt_printf
  add esp, 8


  push 0
  call ExitProcess

end start

      

My conclusion at the moment is the number of arguments. For example:

  • D: \ masm32> Hello.exe I greet
  • 4
  • D: \ masm32>
+3


source to share


1 answer


CommandLineToArgvW

has at least three quirks that you should look out for:

  • The result is an array of pointers to wide- character strings.

    The MASM32 crt_printf

    function uses a function printf

    from the Microsoft VC Runtime Library (msvcrt.dll). Thus, you can use an uppercase "S" as the type field character. Take a look at printf

    Enter Field Characters on MSDN
    .

  • The result is the address of the first element of the array of pointers to string.

    Most print functions expect a pointer to a string, not a pointer to a pointer to a string. You must dereference this address to get a pointer to a string. The command line "Hello.exe I am Hello" will be split into four lines: "Hello.exe", "I", "am", "Hello". The pointers to these strings must be found in an array with 4 pointers: [pointer to "Hello.exe"], [pointer to "I"], etc. Suppose the function CommandLineToArgvW

    has a return value of EAX = 0x001445A8. Hexdump looks like

    Address   Hex dump                                         ASCII
    001445A8  B8 45 14 00|CC 45 14 00|D0 45 14 00|D6 45 14 00| ΒΈE.ÌE.ÐE.Γ–E.
    001445B8  48 00 65 00|6C 00 6C 00|6F 00 2E 00|65 00 78 00| H.e.l.l.o...e.x.
    001445C8  65 00 00 00|49 00 00 00|61 00 6D 00|00 00 48 00| e...I...a.m...H.
    001445D8  65 00 6C 00|6C 00 6F 00|00 00 00 00|00 00 00 00| e.l.l.o.........
    
          

    At address 0x001445A8 there is a pointer to 0x001445B8 (displayed in the dump in small tail format), and this is the start of "Hello.exe" in wide format. The next pointer - 4 bytes behind 0x001445A8: 0x001445CC - points to "I". The next pointer is 4 bytes, etc. You can quickly step through this array by simply adding 4. And you can easily get the address of the line in the middle of the list by multiplying the index by 4 - the pointer to the third line ("am", index: 2) is 0x001445A8 + 2 * 4 = 0x001445B0 = > 0x001445D0 => "am".

  • The function allocates memory that must be manually deallocated with LocalFree

    .

I changed your program as little as possible:



include \masm32\include\masm32rt.inc

.data
    Format db "argc: %d", 10, 0
    fmt db "%S",10,0                ; %S: printf wide-character string / wprintf single-character string
    szArglist dd ?

.code

start:

    push ebp
    mov ebp, esp
    sub esp, 4

    ; https://msdn.microsoft.com/library/windows/desktop/ms683156.aspx
    call GetCommandLineW        ; EAX = pointer to the command line

    ; https://msdn.microsoft.com/library/windows/desktop/bb776391.aspx
    lea ecx, dword ptr[ebp - 4] ; Get the current address of [ebp-4]
    push ecx                    ; int *pNumArgs (Pointer to a SDWORD, here at ebp-4)
    push eax                    ; LPCWSTR lpCmdLine (from GetCommandLineW)
    call CommandLineToArgvW

    mov [szArglist], eax        ; Store the result of CommandLineToArgvW (at least for LocalFree)

    mov esi, eax                ; ESI = address of a pointer (the first element in szArglist)
    mov ebx, [ebp-4]            ; Countdown the number of arguments

    @@:                         ; Loop
    push dword ptr [esi]        ; Pointer to a string (dereferenced esi)
    push OFFSET fmt             ; Format string
    call crt_printf             ; printf (""%S\n", esi)
    add esp, 8                  ; Clear the stack after printf
    add esi, 4                  ; Next address of a pointer (next element of szArglist)
    dec ebx                     ; Countdown the number of arguments
    jne @B                      ; Loop to the last @@

    push dword ptr [szArglist]
    call LocalFree              ; Free the memory occupied by CommandLineToArgvW

    push dword ptr [ebp - 4]    ; Value that is stored in [ebp-4]
    push offset Format          ; Pointer to format string
    call crt_printf             ; printf ("argc: %d\n", [ebp-4])
    add esp, 8                  ; Clear the stack after printf

    push 0
    call ExitProcess

end start

      

The MASM32 function StdOut

cannot handle wide character strings. You must convert them to ANSI strings first. Windows function for this purpose WideCharToMultiByte

:

include \masm32\include\masm32rt.inc

.data
    szArglist dd ?
    buf db 1024 DUP (?)
    crlf db 13, 10, 0           ; New line

.code

start:

    push ebp
    mov ebp, esp
    sub esp, 4

    ; https://msdn.microsoft.com/library/windows/desktop/ms683156.aspx
    call GetCommandLineW        ; EAX = pointer to the command line

    ; https://msdn.microsoft.com/library/windows/desktop/bb776391.aspx
    lea ecx, dword ptr[ebp - 4] ; Get the current address of [ebp-4]
    push ecx                    ; int *pNumArgs (Pointer to a SDWORD, here at ebp-4)
    push eax                    ; LPCWSTR lpCmdLine (from GetCommandLineW)
    call CommandLineToArgvW

    mov [szArglist], eax        ; Store the result of CommandLineToArgvW (at least for LocalFree)

    mov esi, eax                ; ESI = address of a pointer (the first element in szArglist)
    mov ebx, [ebp-4]            ; Countdown the number of arguments

    @@:                         ; Loop

    ; https://msdn.microsoft.com/library/windows/desktop/dd374130.aspx
    push NULL                   ; LPBOOL  lpUsedDefaultChar
    push NULL                   ; LPCSTR  lpDefaultChar
    push SIZEOF buf             ; int     cbMultiByte
    push OFFSET buf             ; LPSTR   lpMultiByteStr
    push -1                     ; int     cchWideChar
    push [esi]                  ; LPCWSTR lpWideCharStr (dereferenced esi)
    push 0                      ; DWORD   dwFlags
    push 0                      ; UINT    CodePage
    call WideCharToMultiByte

    push OFFSET buf             ; Pointer to an ANSI string
    call StdOut
    push OFFSET crlf            ; New line
    call StdOut

    add esi, 4                  ; Next address of a pointer (next element of szArglist)
    dec ebx                     ; Countdown the number of arguments
    jne @B                      ; Loop to the last @@

    push dword ptr [szArglist]
    call LocalFree              ; Free the memory occupied by CommandLineToArgvW

    push OFFSET buf
    push dword ptr [ebp - 4]
    call dwtoa
    push OFFSET buf             ; Pointer to a string
    call StdOut                 ; printf (""%S\n", esi)
    push OFFSET crlf
    call StdOut

    push 0
    call ExitProcess

end start

      

+3


source







All Articles