Linux Shellcode“Hello,World!”

我有以下工作NASM代码:

global _start section .text _start: mov eax, 0x4 mov ebx, 0x1 mov ecx, message mov edx, 0xF int 0x80 mov eax, 0x1 mov ebx, 0x0 int 0x80 section .data message: db "Hello, World!", 0dh, 0ah 

在屏幕上打印“Hello,World!\ n”。 我也有以下C包装,其中包含以前的NASM目标代码:

 char code[] = "\xb8\x04\x00\x00\x00" "\xbb\x01\x00\x00\x00" "\xb9\x00\x00\x00\x00" "\xba\x0f\x00\x00\x00" "\xcd\x80\xb8\x01\x00" "\x00\x00\xbb\x00\x00" "\x00\x00\xcd\x80"; int main(void) { (*(void(*)())code)(); } 

但是,当我运行代码,似乎汇编代码不执行,但程序退出罚款。 有任何想法吗?

谢谢

Solutions Collecting From Web of "Linux Shellcode“Hello,World!”"

当你注入这个shellcode时,你不知道什么是message

 mov ecx, message 

在注入的过程中,它可以是任何东西,但它不会是"Hello world!\r\n"因为它在数据部分,而您只是倾销文本部分。 你可以看到你的shellcode没有"Hello world!\r\n"

 "\xb8\x04\x00\x00\x00" "\xbb\x01\x00\x00\x00" "\xb9\x00\x00\x00\x00" "\xba\x0f\x00\x00\x00" "\xcd\x80\xb8\x01\x00" "\x00\x00\xbb\x00\x00" "\x00\x00\xcd\x80"; 

这是shellcode开发中的常见问题,解决这个问题的方法是这样的:

 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!\r\n" 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!\r\n", is pushed into the stack. db "Hello, World!", 0dh, 0ah section .data 

现在转储文本部分:

 $ 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 <-+ $ 

我标记的行是我们的"Hello, World!\r\n"字符串:

 $ printf "\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0d\x0a" Hello, World! $ 

所以我们的C包装将是:

 char code[] = "\xe9\x1e\x00\x00\x00" // jmp 8048083 <MESSAGE> "\xb8\x04\x00\x00\x00" // mov $0x4,%eax "\xbb\x01\x00\x00\x00" // mov $0x1,%ebx "\x59" // pop %ecx "\xba\x0f\x00\x00\x00" // mov $0xf,%edx "\xcd\x80" // int $0x80 "\xb8\x01\x00\x00\x00" // mov $0x1,%eax "\xbb\x00\x00\x00\x00" // mov $0x0,%ebx "\xcd\x80" // int $0x80 "\xe8\xdd\xff\xff\xff" // call 8048065 <GOBACK> "Hello wolrd!\r\n"; // OR "\x48\x65\x6c\x6c\x6f\x2c\x20\x57" // "\x6f\x72\x6c\x64\x21\x0d\x0a" int main(int argc, char **argv) { (*(void(*)())code)(); return 0; } 

让我们测试一下:

 $ gcc test.c -o test $ ./test Hello wolrd! $ 

有用。

正如BSH提到的,你的shellcode不包含消息字节。 在定义msg字节之前跳转到MESSAGE标签并调用GOBACK例程是一个好的举动,因为msg的地址将位于堆栈的顶部,作为可以弹出到ecx返回地址,其中存储msg的地址。

但是你和BSH的代码都有一个小小的限制。 它包含NULL bytes ( \x00 ) ,当被函数指针取消引用时,它将被视为字符串的结尾。

有一个聪明的方法来解决这个问题。 您存储到eax, ebx and edx中的值足够小,可以分别通过访问al, bl and dl直接写入相应寄存器的低位半字节。 上半字节可能包含垃圾值,因此可以进行着色。

 b8 04 00 00 00 ------ mov $0x4,%eax 

 b0 04 ------ mov $0x4,%al 31 c0 ------ xor %eax,%eax 

与之前的指令集不同,新的指令集不包含任何NULL字节。

所以最后的程序看起来像这样:

 global _start section .text _start: jmp message proc: xor eax, eax mov al, 0x04 xor ebx, ebx mov bl, 0x01 pop ecx xor edx, edx mov dl, 0x16 int 0x80 xor eax, eax mov al, 0x01 xor ebx, ebx mov bl, 0x01 ; return 1 int 0x80 message: call proc msg db " y0u sp34k 1337 ? " section .data 

组装和连接:

 $ nasm -f elf hello.asm -o hello.o $ ld -s -m elf_i386 hello.o -o hello $ ./hello y0u sp34k 1337 ? $ 

现在从hello二进制文件中提取shellcode:

 $ for i in `objdump -d hello | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\\x$i" ; done 

输出:

 \xeb\x19\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x59\x31\xd2\xb2\x12\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xb3\x01\xcd\x80\xe8\xe2\xff\xff\xff\x20\x79\x30\x75\x20\x73\x70\x33\x34\x6b\x20\x31\x33\x33\x37\x20\x3f\x20 

现在我们可以有我们的驱动程序来启动shellcode。

 #include <stdio.h> char shellcode[] = "\xeb\x19\x31\xc0\xb0\x04\x31\xdb" "\xb3\x01\x59\x31\xd2\xb2\x12\xcd" "\x80\x31\xc0\xb0\x01\x31\xdb\xb3" "\x01\xcd\x80\xe8\xe2\xff\xff\xff" "\x20\x79\x30\x75\x20\x73\x70\x33" "\x34\x6b\x20\x31\x33\x33\x37\x20" "\x3f\x20"; int main(int argc, char **argv) { (*(void(*)())shellcode)(); return 0; } 

现代编译器中有一些安全特性,如NX保护 ,可防止数据段或堆栈中的代码执行。 所以我们应该明确指定编译器来禁用这些。

 $ gcc -g -Wall -fno-stack-protector -z execstack launcher.c -o launcher 

现在可以调用启动launcher来启动shellcode。

 $ ./launcher y0u sp34k 1337 ? $ 

对于更复杂的shellcode,会有另一个障碍。 现代Linux内核具有ASLR或Address Space Layout Randomization您可能需要在注入shellcode之前禁用它,尤其是在通过缓冲区溢出时。

 root@localhost:~# echo 0 > /proc/sys/kernel/randomize_va_space