重复调用 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整除
我最近开始学习汇编,为了练习,我想做一个小游戏。 为了制作游戏的边框图形,我需要打印 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整除