我有以下工作的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)();
}
但是,当我运行代码时,似乎未执行汇编代码,但程序退出正常。 有任何想法吗?
谢谢
注入此shellcode时,您不知道message的含义:
mov ecx, message
在注入的过程中,它可以是任何值,但不能为"Hello world!
",因为当您仅转储文本部分时,它位于数据部分中。您可以看到您的shellcode没有"Hello world!
":
"\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!
"
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
现在转储文本部分:
$ 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
08048065 :
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 :
8048083: e8 dd ff ff ff call 8048065
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 |
8048093: 64 fs |
8048094: 21 .byte 0x21 |
8048095: 0d .byte 0xd |
8048096: 0a .byte 0xa
$
我标记的行是我们的"Hello, World!
"字符串:
$ 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
"\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
"Hello wolrd!
"; // 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!
$
有用。
不知道为什么您没有得到任何投票,但这是一个很好的答案。 谢谢您的帮助。
应该删除空字节以执行Shellcode
@REALFREE如果您使用的函数需要以null结尾的字符串(如字符串函数strcpy),则空字节将是一个问题,它将无法读取整个shellcode,字符串。 否则就可以了。
如BSH所述,您的shellcode不包含消息字节。跳转到message标签并在定义msg字节之前调用GOBACK例程是一个不错的选择,因为msg的地址将在栈顶作为返回地址,可以弹出到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
与先前的指令集不同,新的指令集不包含任何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 ' ' '
' | 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
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