【问题标题】:CreateProcessA function doesn't work in MASM64 (ml64.exe)CreateProcessA 函数在 MASM64 (ml64.exe) 中不起作用
【发布时间】:2016-07-17 10:37:08
【问题描述】:

我想编写启动另一个程序(进程)的程序。我正在使用 Visual Studio 2015 中的 MASM64 (ml64.exe)。

程序不工作。什么都没有显示。在调试器中我得到访问冲突。

我不知道我的代码有什么问题。

我的代码:

extrn ExitProcess : proc
extrn MessageBoxA : proc
extrn CreateProcessA : proc

PROCESS_INFORMATION    struct 
hProcess    DWORD    ?
hThread    DWORD    ?
dwProcessId    DWORD    ?
dwThreadId    DWORD    ?
PROCESS_INFORMATION    ends

STARTUPINFOA    struct 
cb    DWORD    ?
lpReserved    DWORD    ?
lpDesktop    DWORD    ?
lpTitle    DWORD    ?
dwX    DWORD    ?
dwY    DWORD    ?
dwXSize    DWORD    ?
dwYSize    DWORD    ?
dwXCountChars    DWORD    ?
dwYCountChars    DWORD    ?
dwFillAttribute    DWORD    ?
dwFlags    DWORD    ?
wShowWindow    WORD    ?
cbReserved2    WORD    ?
lpReserved2    DWORD    ?
hStdInput    DWORD    ?
hStdOutput    DWORD    ?
hStdError    DWORD    ?
STARTUPINFOA    ends

.const
MB_ICONINFORMATION equ 40h
ERROR_ALREADY_EXISTS equ 0B7h
NORMAL_PRIORITY_CLASS equ 020h

.data
szText db "This is first application which creates new process using CreateProcessA.", 00h
szCaption db "Information",00h
processInfo PROCESS_INFORMATION <>
startupInfo STARTUPINFOA <>
szProcName db "D:\Apps\SampleApp.exe", 00h

.code
    Main proc
        ;not sure if correct - begin
        lea rax, processInfo
        lea rbx, startupInfo


        sub rsp, 60h 
        push rax
        push rbx
        push 00h
        push 00h
        push NORMAL_PRIORITY_CLASS
        push 00h
        mov r9, 00h
        mov r8, 00h
        mov rdx, 00h
        lea rcx, szProcName
        call CreateProcessA
        add rsp, 60h 
        ;not sure if correct - end

        sub rsp, 28h
        mov r9, MB_ICONINFORMATION
        lea r8, szCaption
        lea rdx, szText
        xor rcx, rcx
        call MessageBoxA
        add rsp, 28h

        Exit:
        xor rcx, rcx
        call ExitProcess
    Main endp
end

build.bat

@echo off

ml64.exe prog1.asm /link /entry:Main /subsystem:windows /defaultlib:"kernel32.Lib" /defaultlib:"user32.Lib"

pause

提前感谢您的帮助。

【问题讨论】:

  • 堆栈指针必须在 16 个字节上对齐,在任何 API 调用之前,但在调用 CreateProcessA 之前不要这样做 - 已经足够崩溃了
  • 我有sub rsp, 60h。我已将其更改为sub rsp, 58h,但它仍然不起作用......应该有什么价值?
  • 调试器已经不存在,看看哪里崩溃了?
  • 启动信息你也没有初始化..
  • In debugger I get Access Violation. 在哪条指令上?使用调试器的全部意义在于获取详细信息并进一步调查(例如检查寄存器内容)。

标签: windows winapi assembly masm


【解决方案1】:

您应该检查 PROCESS_INFORMATION 和 STARTUPINFO 结构的定义,因为它们在 x86 和 x64 之间可能不同。例如,句柄被定义为指针,而不是 DWORD(32 位整数)。

【讨论】:

    【解决方案2】:

    除了初始化 STARTUPINFO 并确保使用正确的数据类型(在 64 位 Windows 中指针是 64 位宽,而 DWORD 始终是 32 位),您还需要正确分配 参数区域(有时也称为“阴影空间”):

    参数区总是在栈底(即使使用了alloca),所以在任何函数调用过程中它总是与返回地址相邻。
    它至少包含四个条目,但总是有足够的空间来保存可能被调用的任何函数所需的所有参数。
    请注意,总是为寄存器参数分配空间
    ,即使参数本身从未归入堆栈;被调用者保证已为其所有参数分配空间。

    强调我的,来自here

    假设所需的所有数据结构都正确初始化,CreateProcessA 的可能调用是:

    ; Stack is assumed aligned here
    
    push rax                     ; Not aligned (08h)
    push rbx                     ; Aligned     (10h)
    push 00h                     ; Not aligned (18h)                    
    push 00h                     ; Aligned     (20h)
    push NORMAL_PRIORITY_CLASS   ; Not aligned (28h)
    push 00h                     ; Aligned     (30h)
    
    ; Make room for the first four (register) parameters
    ; Stack is aligned, not need to subtract 28h, just 20h (4*8)
    
    sub rsp, 20h
    
    mov r9, 00h                  
    mov r8, 00h
    mov rdx, 00h
    lea rcx, szProcName
    call CreateProcessA
    
    add rsp, 50h
    

    请注意,调用者负责在调用后清理堆栈。


    您使用sub rsp, ... 在堆栈上保留空间的想法并非完全错误。
    当然,您必须正确地进行数学运算,但最重要的是,该技术无法立即与pushes 兼容。

    拥有sub rsp, ... 后,需要一个或多个mov [rsp+...], ... 类型的间接存储来设置参数。 pushes 只会再次移动堆栈指针,使之前的所有工作都无用。

    【讨论】:

    • “参数区域”通常被称为“阴影空间”。很好地发现 OP 的代码没有遵循 win64 调用约定。
    【解决方案3】:

    在你的函数中保留足够的空间来调用具有这么多参数的子函数。 在你的函数序言中使用.ALLOCSTACK

    然后简单地分配整个参数列表

    mov QWORD PTR [rsp+48h], rax                     
    mov QWORD PTR [rsp+40h], rbx                     
    mov QWORD PTR [rsp+38h], 00h                     
    mov QWORD PTR [rsp+30h], 00h                     
    mov QWORD PTR [rsp+28h], NORMAL_PRIORITY_CLASS
    mov QWORD PTR [rsp+20h], 00h
    
    xor r9, r9    ; pass 0              
    xor r8, r8    ; pass 0
    xor edx, edx  ; pass 0  (higher DWORD becomes always also zero, saving the REX-byte)
    lea rcx, szProcName
    call CreateProcessA  
    

    【讨论】:

      【解决方案4】:

      这是一个有点老的问题,但因为我刚刚解决了它,所以把它写下来。 现在我正在努力解决同样的问题,但使用的是 NASM。 问题是一样的,但语法会有点不同。 您的问题是您的 STARTUPINFOA 和 PROCESS_INFORMATION 不正确,原因有两个:

      1. 指针在 x64 系统上是 DWORD64
      2. 您尚未考虑 64 位系统上的结构填充

      NASM 语法中的正确结构在这里:

      ; https://msdn.microsoft.com/library/windows/desktop/ms686331.aspx
      STRUC _STARTUPINFOA
          .cb:                resq 1
          .lpReserved:        resq 1
          .lpDesktop:         resq 1
          .lpTitle:           resq 1
          .dwX:               resd 1
          .dwY:               resd 1
          .dwXSize:           resd 1
          .dwYSize:           resd 1
          .dwXCountChars:     resd 1
          .dwYCountChars:     resd 1
          .dwFillAttribute:   resd 1
          .dwFlags:           resd 1
          .wShowWindow:       resd 1
          .cbReserved2:       resd 1
          .lpReserved2:       resq 1
          .hStdInput:         resq 1
          .hStdOutput:        resq 1
          .hStdError:         resq 1
      ENDSTRUC
      
      ; https://msdn.microsoft.com/library/windows/desktop/ms684873.aspx
      STRUC _PROCESS_INFORMATION
          .hProcess:          resq 1
          .hThread:           resq 1
          .dwProcessId:       resd 1
          .dwThreadId:        resd 1
      ENDSTRUC
      

      一点解释:

      1. resq = DWORD64
      2. resd = DWORD32

      如果遵循结构,大多数字段都可以正确填写,但有几个字段不同。原因是结构填充。 MS 编译器选择结构中最大的元素,然后将所有其他字段填充到它。举个例子,STARTUPINFOA 案例如下所示:

      1. 编译器选择 DWORD64 作为最大元素
      2. cb 字段是 DWORD,但由于它不能用下一个字段 (lpReserved: DWORD64) 填充它,它会将它填充到 DWORD64
      3. lpReserved、lpDesktop 和 lpTitle 已经是 DWORD64
      4. 从 dwX 到 dwFlags 的大小可以用下一个元素填充,所以没有变化
      5. wShowWindow 和 cbReserved2 只是 WORD,因此编译器将它们一起填充为 8 个字节,因此每个字段都更改为 DWORD
      6. 从 lpReserved2 到 hStdError 已经是 DWORD64

      当我完成正确的填充时,它就像魅力一样。祝你好运! :)

      【讨论】:

      • 我从未听说过“DWORD64”这个词。在基于 8086 的体系结构上,双字(双字)总是 32 位宽。 64 位数字是一个 qword(四字)。
      • 然后请打开Visual Studio并检查“basestd.h”:typedef unsigned __int64 DWORD64, *PDWORD64;
      猜你喜欢
      • 2018-06-02
      • 1970-01-01
      • 2018-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多