Winapi 重复调用WriteConsole(Win64上的NASM x64)
我最近开始学习组装,为了练习,我想做一个小游戏。 为了制作游戏的边框图形,我需要打印一个方块字符n次。 为了测试这一点,我编写了以下代码: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:
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文档对阴影空间的描述也非常模糊: