在 64 位 Linux 上使用中断 0x80Linux

2023-09-07 02:54:55 作者:一生醉笑

我有一个简单的 64 位汇编程序,旨在打印一个 'O' 和 'K' 后跟一个换行符.

I have a simple 64-bit assembly program which is intended to print an 'O' and 'K' followed by a newline.

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

However, the 'K' is never printed. One of the goals of the programs is to print the value in the lower bits of the rax register as ASCII letter. The program is specifically for 64-bit Linux, written for educational purposes, so there is no need to use C-style system calls.

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

I suspect that the problem either lies with mov QWORD [rsp], rax or mov rcx, rsp.

目前,程序仅输出 'O' 后跟换行符.

Currently, the program only outputs 'O' followed by a newline.

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

How can one change the program to make it use the value in rax and then print a 'K' so that the complete output is 'OK' followed by a newline?

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 汇编程序有很大不同.

Update: Note that this is a 64-bit x86 Assembly program that uses int 80h, and is very different from a 32-bit x86 Assembly program that uses int 80h.

推荐答案

显然你写了一个 64 位的程序,你使用了int 0x80"指令.但是int 0x80"只能在 32 位程序中正常工作.

Obviously you write a 64-bit program and you use the "int 0x80" instruction. "int 0x80" however only works correctly in 32-bit programs.

堆栈的地址在 32 位程序无法访问的范围内.因此很有可能int 0x80"风格的系统调用不允许访问这个内存区域.

The address of the stack is in a range that cannot be accessed by 32-bit programs. Therefore it is quite probable that "int 0x80"-style system calls do not allow accessing this memory area.

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

To solve this problem there are two possibilities:

编译为 32 位应用程序(使用 32 位寄存器,如 EAX,而不是 64 位寄存器,如 RAX).如果您在不使用任何共享库的情况下进行链接,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" is intended for 32-bit programs. When called from a 64-bit program it behaves the same way it would behave like if it has been called from a 32-bit program (using the 32-bit calling convention).

这也意味着int 0x80"的参数将传入32位寄存器,而忽略64位寄存器的高32位.

This also means that the parameters for "int 0x80" will be passed in 32-bit registers and the upper 32 bits of the 64-bit registers are ignored.

(我刚刚在 64 位的 Ubuntu 16.10 上进行了测试.)

(I just tested that on Ubuntu 16.10, 64 bit.)

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

This however means that you can only access memory below 2^32 (or even below 2^31) when using "int 0x80" because you cannot pass an address above 2^32 in a 32-bit register.

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

If the data to be written is located at an address below 2^31 you may use "int 0x80" to write the data. If it is located above 2^32 you can't. The stack (RSP) is very likely located above 2^32 so you cannot write data on the stack using "int 0x80".

因为你的程序很可能会使用 2^32 以上的内存,所以我写了:int 0x80 不适用于 64 位程序."

Because it is very likely that your program will use memory above 2^32 I have written: "int 0x80 does not work with 64-bit programs."