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.