MASM32 x86 Windows汇编:GetCommandLineToArgvW

3
我希望你能够翻译如何在x86汇编中使用CommandLineToArgvW函数。我遇到了困难。目前我只能打印出参数的数量和在cmd中执行程序。我想将参数保存在不同的变量中。我该怎么做?
我的代码看起来像这样:
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

目前我的输出结果是参数的数量。例如:

  • D:\masm32>Hello.exe I am Hello
  • 4
  • D:\masm32>
1个回答

5

CommandLineToArgvW至少有三个你必须注意的怪癖:

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

    The MASM32 function crt_printf uses the function printf from the Microsoft VC runtime library (msvcrt.dll). Therefore, you can use an uppercase 'S' as the type field character. Take a look at printf Type Field Characters on MSDN.

  2. The result is the address of the first element of an array of pointers to a string.

    Most print functions expect a pointer to a string, not a pointer to a pointer to a string. You have to dereference that address to get a pointer to the string. A command line "Hello.exe I am Hello" will be splitted into four strings: "Hello.exe", "I", "am", "Hello". The pointers to these strings are to find in an array with 4 pointers: [pointer to "Hello.exe"], [pointer to "I"], and so on. Assume the function CommandLineToArgvW has a return value EAX=0x001445A8. The 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 is a pointer to 0x001445B8 (displayed in the dump in little endian format) and this is the beginning of "Hello.exe" in wide-character format. The next pointer is 4 bytes behind 0x001445A8: 0x001445CC - points to "I". The next pointer is 4 bytes away and so on. You can quickly go through that array just by adding 4. And you can easily get the address of a string in the middle of the list by multiplying the index by 4 - the pointer to the third string ("am", index: 2) is at 0x001445A8 + 2 * 4 = 0x001445B0 => 0x001445D0 => "am".

  3. The function allocates memory, which has to be manually freed with LocalFree.

我尽可能少地更改了您的程序:

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

MASM32函数StdOut无法处理宽字符字符串。您需要先将其转换为ANSI字符串。为此目的的Windows函数是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

我可以问一下如何将每个参数保存到变量中吗?我似乎无法将它们保存在变量中。提前感谢! - Pentagon
@五角大楼:坦白地说,我不知道你想要实现什么。这些参数已经被保存了。szArglist是一个指向字符串的指针数组。只要你不用LocalFree销毁它们,它们就会一直保存下去。进一步处理——比如与常量字符串进行比较——并不像高级编程语言那样容易,并且你不需要将它们传输到“变量”中。根据你的意图,方法是完全不同的。如果你能更详细地描述你的意图,我很乐意给你展示一个例子。 - rkhb
@五角大楼:请看一下我重新修改的答案。我尽力了;-) - rkhb
谢谢你的时间,@rkhb!我现在明白了。再次感谢,兄弟! - Pentagon
我有一个问题。似乎保存的字符串没有以空字符结尾。我该怎么做? - Pentagon
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接