Winapi 重复调用WriteConsole(Win64上的NASM x64)

Winapi 重复调用WriteConsole(Win64上的NASM x64),winapi,assembly,64-bit,x86-64,nasm,Winapi,Assembly,64 Bit,X86 64,Nasm,我最近开始学习组装,为了练习,我想做一个小游戏。 为了制作游戏的边框图形,我需要打印一个方块字符n次。 为了测试这一点,我编写了以下代码: bits 64 global main extern ExitProcess extern GetStdHandle extern WriteConsoleA section .text main: mov rcx, -11 call GetStdHandle mov rbx, rax drawFrame:

我最近开始学习组装,为了练习,我想做一个小游戏。 为了制作游戏的边框图形,我需要打印一个方块字符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时,它工作正常。我还可以用writeconolea函数制作一个C等价物,它也可以正常工作。C代码的反汇编并没有让我走得更远


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

你不希望每次循环都从RSP中减去48。在循环之前和调用C库函数或WinAPI之前,只需分配一次该空间

主要问题是R9中的第四个参数。WriteConsole函数定义为:

R9应该是指向一个内存位置的指针,返回一个包含写入字符数的DWORD,但您可以:

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

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

重要提示:为了符合,在调用WinAPI或C库函数之前,必须保持堆栈指针的16字节对齐。调用main函数时,堆栈指针RSP是16字节对齐的。在主函数开始执行堆栈时,堆栈未对齐8,因为8字节的返回地址被推送到堆栈上。48+8=56不能让您回到16字节对齐的堆栈地址56不能被16整除,但56+8=64可以。64可以被16整除。

您不希望通过每个循环从RSP中减去48。在循环之前和调用C库函数或WinAPI之前,只需分配一次该空间

主要问题是R9中的第四个参数。WriteConsole函数定义为:

R9应该是指向一个内存位置的指针,返回一个包含写入字符数的DWORD,但您可以:

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

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

重要提示:为了符合,在调用WinAPI或C库函数之前,必须保持堆栈指针的16字节对齐。调用main函数时,堆栈指针RSP是16字节对齐的。在主函数开始执行堆栈时,堆栈未对齐8,因为8字节的返回地址被推送到堆栈上。48+8=56不能让您回到16字节对齐的堆栈地址56不能被16整除,但56+8=64可以。64可以被16整除。

非常感谢!我不知怎的认为函数返回后会弹出阴影空间和分配的堆栈空间,从而将堆栈指针向前移动48字节。WinAPI文档对阴影空间的描述也非常模糊:非常感谢!我不知怎的认为函数返回后会弹出阴影空间和分配的堆栈空间,从而将堆栈指针向前移动48字节。WinAPI文档对阴影空间的描述也非常模糊: