linux - 在 64 位 Linux 上使用中断 0x80

标签 linux assembly stack x86-64 system-calls

<分区>

我有一个简单的 64 位汇编程序,用于打印“O”和“K”后跟一个换行符。

但是,'K' 永远不会被打印出来。这些程序的目标之一是将 rax 寄存器的低位中的值打印为 ASCII 字母。该程序专门为 64 位 Linux 编写,用于教育目的,因此无需使用 C 风格的系统调用。

我怀疑问题出在 mov QWORD [rsp], raxmov rcx, rsp 上。

目前,该程序仅输出“O”后跟一个换行符。

如何更改程序以使其使用 rax 中的值然后打印一个“K”以便完整的输出为“OK”后跟一个换行符?

bits 64

section .data

o:  db "O"      ; 'O'
nl: dq 10       ; newline

section .text

;--- function main ---
global main         ; make label available to the linker
global _start       ; make label available to the linker
_start:             ; starting point of the program
main:               ; name of the function

;--- call interrupt 0x80 ---
mov rax, 4          ; function call: 4
mov rbx, 1          ; parameter #1 is 1
mov rcx, o          ; parameter #2 is &o
mov rdx, 1          ; parameter #3 is length of string
int 0x80            ; perform the call

;--- rax = 'K' ---
mov rax, 75         ; rax = 75

;--- call interrupt 0x80 ---
sub rsp, 8          ; make some space for storing rax on the stack
mov QWORD [rsp], rax        ; move rax to a memory location on the stack
mov rax, 4          ; function call: 4
mov rbx, 1          ; parameter #1 is 1
mov rcx, rsp            ; parameter #2 is rsp
mov rdx, 1          ; parameter #3 is length of string
int 0x80            ; perform the call
add rsp, 8          ; move the stack pointer back

;--- call interrupt 0x80 ---
mov rax, 4          ; function call: 4
mov rbx, 1          ; parameter #1 is 1
mov rcx, nl         ; parameter #2 is nl
mov rdx, 1          ; parameter #3 is length of string
int 0x80            ; perform the call

;--- exit program ---
mov rax, 1          ; function call: 1
xor rbx, rbx            ; return code 0
int 0x80            ; exit program

更新:请注意,这是一个使用 int 80h 的 64 位 x86 汇编程序,与使用 int 80h 的 32 位 x86 汇编程序有很大不同。

最佳答案

很明显,您编写了一个 64 位程序并使用了“int 0x80”指令。然而,“int 0x80”只能在 32 位程序中正常工作。

栈的地址在32位程序无法访问的范围内。因此,“int 0x80”式系统调用很可能不允许访问该内存区域。

解决这个问题有两种可能:

  • 编译为 32 位应用程序(使用像 EAX 这样的 32 位寄存器而不是像 RAX 这样的 64 位寄存器)。当您在不使用任何共享库的情况下进行链接时,32 位程序将在 64 位 Linux 上完美运行。
  • 使用“syscall”风格的系统调用而不是“int 0x80”风格的系统调用。这些的使用与“int 0x80”风格的有很大不同!

32 位代码:

mov eax,4    ; In "int 0x80" style 4 means: write
mov ebx,1    ; ... and the first arg. is stored in ebx
mov ecx,esp  ; ... and the second arg. is stored in ecx
mov edx,1    ; ... and the third arg. is stored in edx
int 0x80

64 位代码:

mov rax,1    ; In "syscall" style 1 means: write
mov rdi,1    ; ... and the first arg. is stored in rdi (not rbx)
mov rsi,rsp  ; ... and the second arg. is stored in rsi (not rcx)
mov rdx,1    ; ... and the third arg. is stored in rdx
syscall

--- 编辑 ---

背景资料:

“int 0x80”适用于 32 位程序。从 64 位程序调用时,它的行为方式与从 32 位程序调用时的行为方式相同(使用 32 位调用约定)。

这也意味着“int 0x80”的参数将在32 位寄存器中传递,而忽略 64 位寄存器的高 32 位。

(我刚刚在 64 位 Ubuntu 16.10 上测试过。)

然而,这意味着您在使用“int 0x80”时只能访问低于 2^32(甚至低于 2^31)的内存,因为您不能在 32 位寄存器中传递高于 2^32 的地址。

如果要写入的数据位于 2^31 以下的地址,您可以使用“int 0x80”来写入数据。如果它位于 2^32 以上,则不能。堆栈 (RSP) 很可能位于 2^32 以上,因此您不能使用“int 0x80”在堆栈上写入数据。

因为您的程序很可能会使用 2^32 以上的内存,所以我写了:“int 0x80 does not work with 64-bit programs.”

关于linux - 在 64 位 Linux 上使用中断 0x80,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22503944/

相关文章:

c++ - 将汇编代码与 c/c++ 混合

debugging - 为什么 64 位 gdb 在 ARM 32 代码中永远不会到达断点?

c++ - 如何控制socket速率?

linux - 正确的/etc/hosts 格式是什么?

linux - 交叉编译开源软件的专用方法

linux - 从 makefile 并行运行两个进程

java - 如何从堆栈中获得真正的 LIFO 顺序?

linux - 无法将linux中的本地文件复制到hadoop

assembly - 近跳自动改为短跳

java - 带循环的深度优先搜索