汇编编译的可执行文件在 Windows 上的 Ubuntu 上使用 INT 0x80 Linux 子系统不产生输出

Posted

技术标签:

【中文标题】汇编编译的可执行文件在 Windows 上的 Ubuntu 上使用 INT 0x80 Linux 子系统不产生输出【英文标题】:Assembly compiled executable using INT 0x80 on Ubuntu on Windows Subsystem for Linux doesn't produce output 【发布时间】:2017-12-10 05:14:35 【问题描述】:

我一直在查看组装教程,并且正在尝试运行一个 hello world 程序。我在 Windows 上的 Ubuntu 上使用 Bash。

这是程序集:

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
    mov edx,len     ;message length
    mov ecx,msg     ;message to write
    mov ebx,1       ;file descriptor (stdout)
    mov eax,4       ;system call number (sys_write)
    int 0x80        ;call kernel

    mov eax,1       ;system call number (sys_exit)
    int 0x80        ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;string to be printed
    len equ $ - msg     ;length of the string

我正在使用这些命令来创建可执行文件:

nasm -f elf64 hello.asm -o hello.o
ld -o hello hello.o -m elf_x86_64

我使用以下方式运行它:

./hello

然后程序似乎在没有分段错误或错误的情况下运行,但它没有产生任何输出。

我不明白为什么代码不会产生输出,但我想知道在 Windows 上的 Ubuntu 上使用 Bash 是否与它有关?为什么它不产生输出,我该如何解决?

【问题讨论】:

您在 64 位可执行文件中使用 32 位系统调用接口这一事实可能与此有关。 我听说 Ubuntu-on-Windows 根本不支持 32 位可执行文件;也许他们也不支持 64 位可执行文件中的 int 0x80 32 位 ABI。您的代码看起来可以在 Linux 上运行,the int 0x80 ABI is supported (but not recommended) in 64-bit mode. 哦,或者msg 在您的 64 位二进制文​​件中位于不适合 32 位的地址?这可以解释它,虽然这不是通常的布局。尝试使用strace ./hello 或单步与GDB 来查看eax 中的错误返回值 【参考方案1】:

问题在于适用于 Windows 的 Ubuntu(适用于 Linux 的 Windows 子系统)。它只支持64位syscall接口和not the 32-bit x86 int 0x80系统调用机制。

除了不能在 64 位二进制文​​件中使用 int 0x80(32 位兼容性)之外,Windows 上的 Ubuntu (WSL) doesn't support running 32-bit executables 也一样。


您需要从使用int 0x80 转换为syscall。这并不难。 syscall 使用了一组不同的寄存器,并且系统调用号与它们的 32 位对应项不同。 Ryan Chapman's blog 包含有关syscall 接口、系统调用及其参数的信息。 Sys_writeSys_exit 是这样定义的:

%rax  System call  %rdi               %rsi              %rdx          %r10 %r8 %r9
----------------------------------------------------------------------------------
0     sys_read     unsigned int fd    char *buf         size_t count          
1     sys_write    unsigned int fd    const char *buf   size_t count
60    sys_exit     int error_code     

使用syscall 也会破坏RCXR11 寄存器。它们被认为是易变的。不要依赖它们在syscall 之后是相同的值。

您的代码可以修改为:

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
    mov edx,len     ;message length
    mov rsi,msg     ;message to write
    mov edi,1       ;file descriptor (stdout)
    mov eax,edi     ;system call number (sys_write)
    syscall         ;call kernel

    xor edi, edi    ;Return value = 0
    mov eax,60      ;system call number (sys_exit)
    syscall         ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;string to be printed
    len equ $ - msg     ;length of the string

注意:在 64 位代码中,如果指令的 destination 寄存器是 32 位(如 EAXEBXEDIESI 等)64 位寄存器的processor zero extends the result into the upper 32-bits。 mov edi,1mov rdi,1 效果相同。


这个答案不是编写 64 位代码的入门书,只是关于使用 syscall 接口。如果您对编写调用 C 库并符合 64 位 System V ABI 的代码的细微差别感兴趣,那么有一些合理的教程可以帮助您入门,例如 Ray Toal's NASM tutorial。他讨论了堆栈对齐、红色区域、寄存器使用以及 64 位 System V 调用约定的基本概述。

【讨论】:

我还要警告 64b 中的不同 ABI,将参数放入寄存器可能是容易掌握的部分,但对于刚开始学习的人来说,堆栈对齐和红色区域可能有点棘手部件。一般来说,我宁愿建议编译为 32b 二进制来回答这样的问题,但这在 windows 中不是一个选项,只支持 64b linux)【参考方案2】:

正如 Ross Ridge 在 cmets 中已经指出的,在编译 64 位时不要使用 32 位内核函数调用。

要么编译 32 位,要么将代码“翻译”成 64 位系统调用。 这可能是这样的:

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
    mov rdx,len     ;message length
    mov rsi,msg     ;message to write
    mov rdi,1       ;file descriptor (stdout)
    mov rax,1       ;system call number (sys_write)
    syscall         ;call kernel

    mov rax,60      ;system call number (sys_exit)
    mov rdi,0       ;add this to output error code 0(to indicate program terminated without errors)
    syscall         ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;string to be printed
    len equ $ - msg     ;length of the string

【讨论】:

与大多数 Linux 系统不同,构建为 32 位不是 WSL 上的选项:int 0x80 ABI 完全不受支持,即使对于 32 位可执行文件也是如此。所以它就像一个没有CONFIG_IA32_EMULATION的Linux内核。

以上是关于汇编编译的可执行文件在 Windows 上的 Ubuntu 上使用 INT 0x80 Linux 子系统不产生输出的主要内容,如果未能解决你的问题,请参考以下文章

带注释的可执行文件反汇编

mingw 包含哪些

汇编学习第四章

PWN

反汇编基本算法

如何将在windows下写的程序编译成linux下的可执行程序文件?