When you inject this shellcode, you don't know what is at message
:
mov ecx, message
in the injected process, it can be anything but it will not be "Hello world!
"
since it is in the data section while you are dumping only the text section. You can see that your shellcode doesn't have "Hello world!
"
:
"xb8x04x00x00x00"
"xbbx01x00x00x00"
"xb9x00x00x00x00"
"xbax0fx00x00x00"
"xcdx80xb8x01x00"
"x00x00xbbx00x00"
"x00x00xcdx80";
This is common problem in shellcode development, the way to work around it is this way:
global _start
section .text
_start:
jmp MESSAGE ; 1) lets jump to MESSAGE
GOBACK:
mov eax, 0x4
mov ebx, 0x1
pop ecx ; 3) we are poping into `ecx`, now we have the
; address of "Hello, World!
"
mov edx, 0xF
int 0x80
mov eax, 0x1
mov ebx, 0x0
int 0x80
MESSAGE:
call GOBACK ; 2) we are going back, since we used `call`, that means
; the return address, which is in this case the address
; of "Hello, World!
", is pushed into the stack.
db "Hello, World!", 0dh, 0ah
section .data
Now dump the text section:
$ nasm -f elf shellcode.asm
$ ld shellcode.o -o shellcode
$ ./shellcode
Hello, World!
$ objdump -d shellcode
shellcode: file format elf32-i386
Disassembly of section .text:
08048060 <_start>:
8048060: e9 1e 00 00 00 jmp 8048083 <MESSAGE>
08048065 <GOBACK>:
8048065: b8 04 00 00 00 mov $0x4,%eax
804806a: bb 01 00 00 00 mov $0x1,%ebx
804806f: 59 pop %ecx
8048070: ba 0f 00 00 00 mov $0xf,%edx
8048075: cd 80 int $0x80
8048077: b8 01 00 00 00 mov $0x1,%eax
804807c: bb 00 00 00 00 mov $0x0,%ebx
8048081: cd 80 int $0x80
08048083 <MESSAGE>:
8048083: e8 dd ff ff ff call 8048065 <GOBACK>
8048088: 48 dec %eax <-+
8048089: 65 gs |
804808a: 6c insb (%dx),%es:(%edi) |
804808b: 6c insb (%dx),%es:(%edi) |
804808c: 6f outsl %ds:(%esi),(%dx) |
804808d: 2c 20 sub $0x20,%al |
804808f: 57 push %edi |
8048090: 6f outsl %ds:(%esi),(%dx) |
8048091: 72 6c jb 80480ff <MESSAGE+0x7c> |
8048093: 64 fs |
8048094: 21 .byte 0x21 |
8048095: 0d .byte 0xd |
8048096: 0a .byte 0xa <-+
$
The lines I marked are our "Hello, World!
"
string:
$ printf "x48x65x6cx6cx6fx2cx20x57x6fx72x6cx64x21x0dx0a"
Hello, World!
$
So our C wrapper will be:
char code[] =
"xe9x1ex00x00x00" // jmp (relative) <MESSAGE>
"xb8x04x00x00x00" // mov $0x4,%eax
"xbbx01x00x00x00" // mov $0x1,%ebx
"x59" // pop %ecx
"xbax0fx00x00x00" // mov $0xf,%edx
"xcdx80" // int $0x80
"xb8x01x00x00x00" // mov $0x1,%eax
"xbbx00x00x00x00" // mov $0x0,%ebx
"xcdx80" // int $0x80
"xe8xddxffxffxff" // call (relative) <GOBACK>
"Hello wolrd!
"; // OR "x48x65x6cx6cx6fx2cx20x57"
// "x6fx72x6cx64x21x0dx0a"
int main(int argc, char **argv)
{
(*(void(*)())code)();
return 0;
}
Lets test it, using -z execstack
to enable read-implies-exec (process-wide, despite "stack" in the name) so we can executed code in the .data
or .rodata
sections:
$ gcc -m32 test.c -z execstack -o test
$ ./test
Hello wolrd!
It works. (-m32
is necessary, too, on 64-bit systems. The int $0x80
32-bit ABI doesn't work with 64-bit addresses like .rodata
in a PIE executable. Also, the machine code was assembled for 32-bit. It happens that the same sequence of bytes would decode to equivalent instructions in 64-bit mode but that's not always the case.)
Modern GNU ld
puts .rodata
in a separate segment from .text
, so it can be non-executable. It used to be sufficient to use const char code[]
to put executable code in a page of read-only data. At least for shellcode that doesn't want to modify itself.