I've got the following simple assembly code in hello.asm:

section .text:
    global _start

    _start:

    jmp short ender

    starter:

    ; clean registers
    xor rax, rax
    xor rbx, rbx
    xor rcx, rcx
    xor rdx, rdx

    ; writes 'hello\n' to stdout
    mov al, 0x04 ; 4 = syscall number of sys_write
    mov bl, 0x01 ; 1 = file descriptor of stdout
    pop rcx      ; points to 'hello\n'
    mov dl, 0x06 ; writes 6 characters
    int 0x80

    ; exits with status 5
    xor rax, rax
    mov al, 0x01 ; 1 = syscall number of sys_exit
    xor rbx, rbx
    mov bl, 0x05 ; 5 = exit status number
    int 0x80

    ender:

    call starter
    db 'hello',0x0a

Inspired by an article on shellcoding, the assembly code pushes hello\n on the stack, writes it to the standard outputs, then exits with code 5.

I can compile it and run it successfully:

$ nasm -f elf64 hello.asm
$ ld hello.o -o hello
$ ./hello
hello

Using objdump -D hello and a short python script, I can extract the related shellcode string and test it with this simple C code:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

char* shellcode = "\xeb\x21\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\xb0\x04\xb3\x01\x59\xb2\x06\xcd\x80\x48\x31\xc0\xb0\x01\x48\x31\xdb\xb3\x05\xcd\x80\xe8\xda\xff\xff\xff\x68\x65\x6c\x6c\x6f\x0a";

void main() {
    (*(void(*)()) shellcode)();
}

The issue

The expected output of ./test-shellcode is hello\n with exit status 5, just like when running the compiled assembly. However, the program simply doesn't output anything, but still exits with a status of 5!

After debugging it with GDB, I observe that the program executes the shellcode as expected. Particularly, this section that is supposed to write hello\n to stdout is executed without errors and the content of rcx is the address of the hello\n string:

   0x5555555546d6:  mov    $0x4,%al
   0x5555555546d8:  mov    $0x1,%bl
   0x5555555546da:  pop    %rcx
   0x5555555546db:  mov    $0x6,%dl
   0x5555555546dd:  int    $0x80

But despite the fact that sys_write is apparently correctly called and is executed without errors, nothing is written to the standard output.

Why?

0 Answers