【问题标题】:MASM32 x86 windows assembly GetCommandLineToArgvWMASM32 x86 windows 程序集 GetCommandLineToArgvW
【发布时间】:2017-05-29 01:15:02
【问题描述】:

我想问一下如何在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 我是你好
  • 4
  • D:\masm32>

【问题讨论】:

    标签: assembly command-line command-line-arguments masm32


    【解决方案1】:

    CommandLineToArgvW 至少有三个你需要注意的怪癖:

    1. 结果是一个指向字符串的指针数组。

      MASM32 函数 crt_printf 使用来自 Microsoft VC 运行时库 (msvcrt.dll) 的函数 printf。因此,您可以使用大写的“S”作为类型字段字符。看看printf Type Field Characters on MSDN

    2. 结果是指向字符串的指针数组的第一个元素的地址。

      大多数打印函数都需要一个指向字符串的指针,而不是指向字符串指针的指针。您必须取消引用该地址才能获得指向该字符串的指针。命令行“Hello.exe I am Hello”将被拆分为四个字符串:“Hello.exe”、“I”、“am”、“Hello”。指向这些字符串的指针将在具有 4 个指针的数组中查找:[指向“Hello.exe”的指针]、[指向“I”的指针],等等。假设函数CommandLineToArgvW 有一个返回值EAX=0x001445A8。 Hexdump 看起来像

      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.........
      

      地址 0x001445A8 是指向 0x001445B8 的指针(以小端格式显示在转储中),这是宽字符格式的“Hello.exe”的开头。下一个指针在 0x001445A8 后面 4 个字节:0x001445CC - 指向“I”。下一个指针距离 4 个字节,依此类推。您只需添加 4 即可快速遍历该数组。您可以通过将索引乘以 4 轻松获得列表中间字符串的地址 - 指向第三个字符串的指针(“am”,索引:2)位于 0x001445A8 + 2 * 4 = 0x001445B0 => 0x001445D0 => "am"。

    3. 函数分配内存,必须使用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 销毁它们,您就可以保存它。进一步的处理——比如与常量字符串进行比较——并不像高级编程语言那么容易,而且您不需要将它们转移到“变量”中。根据您的意图,方法是完全不同的。如果您更详细地描述您的意图,我将很乐意向您展示一个示例。
    • 我想使用 StdOut 函数一一调用每个参数。我似乎无法使用 szArglist 数组来做到这一点。
    • @Pentagon:请看我修改后的答案。我尽力了;-)
    • 感谢您的宝贵时间,@rkhb!我现在明白了。再次感谢,brooooooo。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多