系统调用仅读/写 1 个字节?

Sk6*_*Sk6 1 linux assembly buffer-overflow arm64

我是汇编新手,并试图编写一个内置的“echo”版本,但一次只能对 1 个字节进行操作。

我有以下按我想要的方式工作的方法,除了它在读取和写入时溢出超过 1 个字节,即使我在两个系统调用中都明确表示 x2 中的 1 个字节。我究竟做错了什么?

示例运行:

sh-4.2$ ./echo1b
f
f
o
o
b
b
bar
bar
bazbazbaz
bazbazbaz
q
sh-4.2$
Run Code Online (Sandbox Code Playgroud)

这是代码:

.data
temp:   .byte 1  

.text
.globl _start
_start:
    /* read one byte from stdin, store to temp */
    mov x0, #0x0
    adr x1, temp
    mov x2, #0x1
    mov x8, #0x3F
    svc #0x0

    /* write newline to stdout */
    mov x0, #0x1
    mov x1, #0xA
    mov x2, #0x1
    mov x8, #0x40
    svc #0x0
    
    /* if byte in temp is "q", exit */
    mov x5, #0x71
    ldr x1, temp
    cmp x1, x5
    beq exit

    /* otherwise, write it to stdout and repeat */
    mov x0, #0x1
    adr x1, temp
    mov x2, #0x1
    mov x8, #0x40
    svc #0x0
    b _start

exit:
    /* exit cleanly */  
    eor x0, x0, x0 
    eor x1, x1, x1
    eor x2, x2, x2
    mov x8, #0x5D
    svc #0x0
Run Code Online (Sandbox Code Playgroud)

fcd*_*cdt 5

您的代码中有几个问题:

  • 正如评论中提到的,输出缓冲区的地址必须在x1调用时sys_write像你一样temp
  • 比较时 temp以换行符,你不得不使用ldrb w1, [x0],而不是ldr x1, temp在那里x0temp。后者将读取 4 个字节,但不能保证高三个字节为零。

我还改进了您的代码的某些部分:

  • cmp可以与 12 位立即数一起使用,因此无需放入0x71寄存器。
  • 移动第二个sys_write调用之前_start避免无条件跳转。
  • sys_exit 只使用 x0作参数,因此无需设置x1x2为零。

这是在 Raspbian 4.19(基于 debian)上测试的最终代码:

.data
    temp:    .byte 1
    newline: .byte 0x0A

.text
.globl _start

loop:
    // 4: Otherwise, write it to stdout and repeat
    mov  x0, #0x1    // int    fd
    adr  x1, temp    // void*  buf
    mov  x2, #0x1    // size_t count
    mov  x8, #0x40   // sys_write
    svc  #0x0
    
_start:
    // 1: Read one byte from stdin and store to temp (including newline)
    mov  x0, #0x0   // int    fd
    adr  x1, temp   // void*  buf
    mov  x2, #0x1   // size_t count
    mov  x8, #0x3F  // sys_read
    svc  #0x0
    
    // 2: If byte in temp is 'q', exit
    adr  x0, temp
    ldrb w1, [x0] // instead of temp
    cmp  x1, #0x71
    bne  loop

    // 5: Exit cleanly
    eor  x0, x0, x0  // int status
    mov  x8, #0x5D   // sys_exit
    svc  #0x0
Run Code Online (Sandbox Code Playgroud)

评论后编辑:要在退出时刷新 stdin,您可以在第 5 步之前添加以下行:

    // 5: Flush stdin (read until newline)
flush:
    mov  x0, #0x0   // int    fd
    adr  x1, temp   // void*  buf
    mov  x2, #0x1   // size_t count
    mov  x8, #0x3F  // sys_read
    svc  #0x0

    adr  x0, temp
    ldrb w1, [x0]
    cmp  x1, #0x0A
    bne flush       // loop until x0 == 0x0A
Run Code Online (Sandbox Code Playgroud)