重复调用 WriteConsole(NASM x64 on Win64)

Repeated call of WriteConsole (NASM x64 on Win64)

我最近开始学习汇编,为了练习,我想做一个小游戏。 为了制作游戏的边框图形,我需要打印 n 次方块字符。 为了对此进行测试,我编写了以下代码:

bits 64

global main
extern ExitProcess
extern GetStdHandle
extern WriteConsoleA

section .text
    
main:
    mov rcx, -11
    call GetStdHandle   
    mov rbx, rax
drawFrame:
    mov r12, [sze]
    l:
    mov rcx, rbx
    mov rdx, msg
    mov r8, 1
    sub rsp, 48
    mov r9, [rsp+40]
    mov qword [rsp+32], 0
    call WriteConsoleA
    dec r12
    jnz l
    
    xor rcx, rcx
    call ExitProcess

section .data
    score dd 0
    sze dq 20
    msg db 0xdb

我想用 WinAPI 函数输出。 有趣的是,当使用 WriteConsoleA 时,这段代码在打印一个字符后停止,但是当我使用 C 的 putchar 时,它工作正常。我还可以设法创建一个与 WriteConsoleA 函数等效的 C 函数,它也可以正常工作。 C代码的反汇编并没有让我更进一步。

我怀疑我在使用堆栈时出现了一些我没有看到的错误。希望有人能解释或指出。

您不想在每个循环中一直从 RSP 中减去 48。您只需要在循环之前和调用 C 库函数或 WinAPI 之前分配一次 space。

主要问题出在 R9 中的第 4 个参数。 WriteConsole函数定义为:

BOOL WINAPI WriteConsole(
  _In_             HANDLE  hConsoleOutput,
  _In_       const VOID    *lpBuffer,
  _In_             DWORD   nNumberOfCharsToWrite,
  _Out_opt_        LPDWORD lpNumberOfCharsWritten,
  _Reserved_       LPVOID  lpReserved
);

R9 应该是指向内存位置的指针,该内存位置 return 是一个 DWORD ,其中包含写入的字符数,但您这样做:

mov r9, [rsp+40]

这会将内存地址 RSP+40 开始的 8 个字节移动到 R9。您想要的是 [rsp+40] 的地址,可以使用 LEA 指令完成:

lea r9, [rsp+40]

您的代码可能如下所示:

bits 64

global main
extern ExitProcess
extern GetStdHandle
extern WriteConsoleA

section .text
    
main:
    sub rsp, 56          ; Allocate space for local variable(s)
                         ; Allocate 32 bytes of space for shadow store
                         ; Maintain 16 byte stack alignment for WinAPI/C library calls
                         ; 56+8=64 . 64 is evenly divisible by 16.
    mov rcx, -11
    call GetStdHandle   
    mov rbx, rax
drawFrame:
    mov r12, [sze]
l:
    mov rcx, rbx
    mov rdx, msg
    mov r8, 1
    lea r9, [rsp+40]
    mov qword [rsp+32], 0
    call WriteConsoleA
    dec r12
    jnz l
    
    xor rcx, rcx
    call ExitProcess

section .data
    score dd 0
    sze dq 20
    msg db 0xdb

重要说明:为了符合 64-bit Microsoft ABI,您必须在调用 WinAPI 或 [=36 之前保持堆栈指针的 16 字节对齐=]C库函数。在调用 main 函数时,堆栈指针 (RSP) 是 16 字节对齐的。在 main 函数开始执行时,堆栈未对齐 8,因为 8 字节 return 地址被压入堆栈。 48+8=56 不会让你回到 16 字节对齐的堆栈地址(56 不能被 16 整除)但 56+8=64 可以。 64能被16整除