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>
source to share
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 functionprintf
from the Microsoft VC Runtime Library (msvcrt.dll). Thus, you can use an uppercase "S" as the type field character. Take a look atprintf
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 likeAddress 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
source to share