Linux Shellcode“Hello,World!”
我有以下工作NASM代码:Linux Shellcode“Hello,World!”
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
它打印 “!您好,世界\ 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)();
}
然而,当我运行的代码,它似乎像汇编代码不被执行,但是在程序退出的罚款。有任何想法吗?
感谢
当你注入这个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不包含消息字节。跳到MESSAGE
标签,只是定义msg
字节之前调用GOBACK
程序是一个很好的举措作为味精的地址将是对堆栈返回地址的顶部可能被弹出到ecx
,其中味精的地址存储。
但无论你和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
不同于现有的指令集,所述新指令集不包含任何空字节。
所以,最后的方案是这样的:
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
组装和链接:
$ for i in `objdump -d hello | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\\x$i" ; done
输出:
$ nasm -f elf hello.asm -o hello.o
$ ld -s -m elf_i386 hello.o -o hello
$ ./hello
y0u sp34k 1337 ? $
现在从打招呼二进制提取的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
现在我们可以让我们的驱动程序启动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 protection这防止在数据段或堆栈代码执行某些安全功能。所以我们应该明确地指定编译器来禁用它们。
$ 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之前禁用它,尤其是在通过缓冲区溢出时。
[email protected]:~# echo 0 > /proc/sys/kernel/randomize_va_space
不知道为什么你没有得到任何upvotes,但这是一个很好的答案。谢谢您的帮助。 – 2013-05-03 13:48:31
空字节应该被移除以执行shellcode tho – REALFREE 2013-10-14 04:14:25
@REALFREE如果您使用函数需要空终止字符串(如字符串函数为“strcpy”),则空字节将会成为问题,它将不会读取整个shellcode字符串。否则就没关系。 – 2013-10-15 13:38:21