使用只接受char *参数的x86_64函数的sys_write?

时间:2018-04-09 08:25:55

标签: linux assembly nasm x86-64

我目前正在学习装配,我需要一些这方面的帮助。

say_something()函数是由x86_64程序集(在64位Linux上使用NASM)创建的,并且应该打印出短语和换行符。

主程序用C语言编写,如下所示:

extern void say_something(char *);

int main(void) {
    say_something("First line.");
    say_something("Second sentence on another line.");

    return 0;
}

... * .asm文件的内容是:

section .text
    global say_something

say_something:
    push rbp
    mov rbp, rsp

    mov rax, 4 ; syscall for write
    mov rbx, 1
    mov rcx, rdi
    int 0x80

    pop rbp
    ret

但显然我的ASM功能有问题,因为当我编译程序并运行它时,输出只是一些垃圾,而它应该打印出来:

First line.
Second sentence on another line.

我应该对装配件进行哪些更改才能使其正常工作?

1 个答案:

答案 0 :(得分:0)

这里有一个主要问题,以及这个特定来电者可能看不到的另外两个主要问题:

  • 您忘记将edx设置为字符串长度。系统呼叫您正在使用的是 sys_write(int fd, const char *buf, size_t len);,因此您传递的len是您的来电者在edx留下的任何垃圾。

如果这不会使sys_write返回-EFAULT而不打印任何内容,那么可能在您检测到问题之前打印了一堆二进制垃圾,如果您最终一个非常大的len?嗯,write可能在写任何东西之前检查整个范围,所以也许你最终在edx中得到了一个中等大小的值,并在你的字符串之后打印了一些二进制垃圾。不过,你的字符串应该在输出的开头就可以了。

使用strace ./my_program查找,或使用GDB在通话前查看edx。 (strace实际上无法从64位代码正确地使用int 0x80;它会像使用本机64位syscall ABI一样进行解码。

  • 你破坏了调用者的rbx而没有保存/恢复它,违反了调用约定(x86-64 System V ABI),其中rbx是一个保持呼叫的寄存器。有关调用约定的详细文档,请参阅http://agner.org/optimize/。这可能没有效果;您的main可能无法编译为使用rbx进行任何操作的代码,因此您销毁的rbx已被"拥有"通过调用main的CRT启动函数。

    奇怪的是,即使您根本不使用它,也可以保存/恢复rbp

最重要的是,你using the 32-bit int 0x80 ABI from 64-bit code !!这就是为什么我一直在说write将其length置于edx而不是rdx,因为32位int 0x80 ABI是与32位用户空间可以使用的ABI完全相同,它仅查看低32位寄存器。

您的代码将破坏虚拟地址空间低32位之外的指针。例如如果使用gcc -fPIE -pie编译,即使.rodata部分中的字符串文字也不在低32位,sys_write可能会返回-EFAULT而不做任何事情。

-pie is the default for gcc on many modern Linux distros,所以你很幸运"(?)您的程序根本打印了任何内容,而不仅仅write失败-EFAULT。您没有检查错误返回值,并且它不会以您尝试从用户空间中的坏点读取/写入的方式对程序进行分段,因此它无声地失败。

正如@fuz指出的那样,Why cant i sys_write from a register?显示了一些示例代码,用于将syscall用于sys_write,但它会对一段长度进行硬编码。您需要使用libc函数或循环来strlen字符串repe scasb。 (或者是缓慢而紧凑的java.lang.NullPointerException: Attempt to read from field 'java.lang.String com.example.juang.trainingplan.Objects.Edit.full_name' on a null object reference at com.example.juang.trainingplan.Edit_athlete$4.onDataChange(Edit_athlete.java:100) )。