Reputation: 41
I'm trying to write my first "Hello world" shellcode on my Ubuntu 64-bit, but it doesn't work.
I have file hello.asm:
; 64-bit "Hello World!" in Linux NASM
global _start ; global entry point export for ld
section .text
_start:
; sys_write(stdout, message, length)
mov rax, 1 ; sys_write
mov rdi, 1 ; stdout
mov rsi, message ; message address
mov rdx, length ; message string length
syscall
; sys_exit(return_code)
mov rax, 60 ; sys_exit
mov rdi, 0 ; return 0 (success)
syscall
section .data
message: db 'Hello, world!',0x0A ; message and newline
length: equ $-message ; NASM definition pseudo-instruction
I used this commands:
nasm -felf64 hello.asm -o hello.o
ld -o hello hello.o
objdump -d hello
I put shellcode from objdump into my C program:
char code[] = "\xb8\x01\x00\x00\x00\xbf\x01\x00\x00\x00\x48\xbe\xd8\x00\x60\x00\x00\x00\x00\x00\xba\x0e\x00\x00\x00\x0f\x05\xb8\x3c\x00\x00\x00\xbf\x00\x00\x00\x00\x0f\x05";
int main(int argc, char **argv)
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
return 0;
}
and compile it in gcc, but after run I've got "Segmentation fault (core dumped)".
I have no idea what I'm doing wrong. Assembler code seems to work, because when i run ./hello it prints "Hello world".
Upvotes: 2
Views: 9774
Reputation: 84642
You can simply, compile with nasm -felf64
and link with ld
. There is no need for gcc
. I believe the issue you stumbled on was the x86
style equ $-message
calculation in your code. For x86_64, that is not valid. Here is a quick example:
section .data
string1 db 0xa, " Hello StackOverflow!!!", 0xa, 0xa, 0
section .text
global _start
_start:
; calculate the length of string
mov rdi, string1 ; string1 to destination index
xor rcx, rcx ; zero rcx
not rcx ; set rcx = -1
xor al,al ; zero the al register (initialize to NUL)
cld ; clear the direction flag
repnz scasb ; get the string length (dec rcx through NUL)
not rcx ; rev all bits of negative results in absolute value
dec rcx ; -1 to skip the null-terminator, ecx contains length
mov rdx, rcx ; put length in rdx
; write string to stdout
mov rsi, string1 ; string1 to source index
mov rax, 1 ; set write to command
mov rdi,rax ; set destination index to rax (stdout)
syscall ; call kernel
; exit
xor rdi,rdi ; zero rdi (rdi hold return value)
mov rax, 0x3c ; set syscall number to 60 (0x3c hex)
syscall ; call kernel
Compile/Output
$ nasm -felf64 -o hello-stack_64.o hello-stack_64.asm
$ ld -o hello-stack_64 hello-stack_64.o
$ ./hello-stack_64
Hello StackOverflow!!!
Upvotes: 1
Reputation: 140856
There's two layers of problem here.
1) char data[] = ...
will be put in the data segment, which is not executable. You need const char data[] = ...
so that it is put in the text segment instead. (Technically, it will be in the read-only data segment, which could be marked non-executable, but currently no one does that as far as I know.)
2) This is what I get when I disassemble the contents of data
:
0: b8 01 00 00 00 mov eax, 0x1
5: bf 01 00 00 00 mov edi, 0x1
a: 48 be d8 00 60 00 00 movabs rsi, 0x6000d8
11: 00 00 00
14: ba 0e 00 00 00 mov edx, 0xe
19: 0f 05 syscall
1b: b8 3c 00 00 00 mov eax, 0x3c
20: bf 00 00 00 00 mov edi, 0x0
25: 0f 05 syscall
and this is what I get when I try to run it:
$ strace ./a.out
execve("./a.out", ["./a.out"], [/* 38 vars */]) = 0
write(1, 0x6000d8, 14) = -1 EFAULT (Bad address)
_exit(0) = ?
+++ exited with 0 +++
You can see that this is not that far off from what you wanted, but it clearly does not work, there being nothing at absolute address 0x6000d8
. The problem is that you only put the code into data
, not the string. You need to put the string immediately after the code and use PC-relative addressing to get at it. nighter's answer shows how that's done (under the additional constraint that no \x00
bytes may appear in data
, which would be necessary if you were actually exploiting a typical buffer overflow here instead of just goofing around with machine language).
Upvotes: 0
Reputation: 31
It's good practice to remove nullbytes in case you want to inject it into a buffer, but main issue is probably that you have the string in the data segment?
I rewrote it like this and got it to work.
global _start
_start:
jmp short string
code:
pop rsi
xor rax, rax
mov al, 1
mov rdi, rax
mov rdx, rdi
add rdx, 14
syscall
xor rax, rax
add rax, 60
xor rdi, rdi
syscall
string:
call code
db 'Hello, world!',0x0A
$ nasm -felf64 hello.asm -o hello.o
$ ld -s -o hello hello.o
$ for i in $(objdump -d hello |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo
\xeb\x1e\x5e\x48\x31\xc0\xb0\x01\x48\x89\xc7\x48\x89\xfa\x48\x83\xc2\x0e\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x0a
char code[] = "\xeb\x1e\x5e\x48\x31\xc0\xb0\x01\x48\x89\xc7\x4\x89\xfa \x48\x83\xc2\x0e\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x0a";
int main(int argc, char **argv)
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
return 0;
}
$ gcc -fno-stack-protector -z execstack -o code code.c
$./code
Hello, world!
Upvotes: 3
Reputation: 768
Hope is not too late to answer to this question :)
You must use this code to compile your C code (To disable stack protection and make it executable):
gcc -fno-stack-protector -z execstack -o hello hello.c
Good luck
Upvotes: 0