返回

Windows 64 位 NASM 汇编:为何使用变量后代码无法打印?

windows

Windows 64 位 NASM 汇编:为何使用变量后代码无法打印?

许多汇编语言初学者在接触 Windows 64 位 NASM 时,常会遇到一个令人困惑的障碍:明明定义了变量,却无法将其内容打印出来。本文将深入剖析这一问题的根源,为你提供清晰易懂的解决方案和代码示例,助你扫清 NASM 汇编学习路上的障碍。

直击核心:寻址方式的迷雾

代码无法正常打印的症结在于对 STD_OUTPUT_HANDLE 变量的寻址方式。

  • mov rcx, STD_OUTPUT_HANDLE: 这一行代码的目的是将 STD_OUTPUT_HANDLE 变量的地址 送入 rcx 寄存器。然而,WriteConsoleA 函数期待的参数是标准输出句柄的 (-11),并非其地址。
  • mov rcx, [STD_OUTPUT_HANDLE]: 此处使用方括号 [] 意图访问 STD_OUTPUT_HANDLE 变量的值。但在 64 位模式下,NASM 默认采用 相对寻址 方式,无法直接访问数据段中的变量。

拨云见日:RIP 相对寻址

想要突破这一困境,我们需要借助 RIP 相对寻址 方式来获取 STD_OUTPUT_HANDLE 变量的值。RIP 寄存器始终指向当前指令的下一条指令,利用这一特性,我们可以计算出变量相对于当前指令的偏移量,进而访问变量的值。

以下是经过修改后的代码:

extern GetStdHandle, WriteConsoleA, ExitProcess

section .text

_main:
    mov rcx, [rel STD_OUTPUT_HANDLE] ; RIP 相对寻址
    call GetStdHandle

    mov rcx, rax    ; hConsoleOutput
    mov rdx, msg    ; *lpBuffer
    mov r8, len     ; nNumberOfCharsToWrite
    call WriteConsoleA

    mov rcx, 0
    call ExitProcess

section .data

STD_OUTPUT_HANDLE: dq -11

msg:
    db "Hello World!", 0x0A
    len equ $-msg

代码解读

  • [rel STD_OUTPUT_HANDLE]: rel 指示 NASM 采用 RIP 相对寻址方式。汇编器会自动计算 STD_OUTPUT_HANDLE 变量相对于当前指令的偏移量,并将该偏移量与 RIP 寄存器的值相加,最终得到变量的实际地址。

通过 RIP 相对寻址,我们成功地将 STD_OUTPUT_HANDLE 变量的值传递给 GetStdHandle 函数,解决了代码无法打印输出的问题。

融会贯通:寻址方式的重要性

在 NASM 汇编语言中,深刻理解寻址方式至关重要。尤其在 64 位模式下,使用 RIP 相对寻址可以便捷地访问数据段中的变量。

常见问题解答

  1. Q: 为什么 64 位模式下不能直接使用变量地址?
    A: 64 位模式下,代码和数据段通常被加载到不同的地址空间,无法直接使用变量地址进行访问。

  2. Q: RIP 相对寻址的优势是什么?
    A: RIP 相对寻址与代码位置无关,即使代码段被加载到不同的地址,依然能够正确访问变量。

  3. Q: rel 关键字还有什么其他用途?
    A: rel 关键字还可以用于跳转指令,实现与位置无关的代码。

  4. Q: 如何在代码中调试寻址问题?
    A: 可以使用调试器查看寄存器和内存的值,以确定寻址是否正确。

  5. Q: 还有哪些常见的寻址方式?
    A: 除了 RIP 相对寻址外,还有寄存器寻址、立即数寻址、基址寻址、索引寻址等多种寻址方式。