闲来无事,以前都是去exploit-db找shellcode使用,但是有的时候似乎并不那么顺利,找到了之后却由于一些问题(比如之前有一次,要求shellcode里边不能出现0b和0c之类制表符,否则会提前中断,但是一些打开shell的shellcode基本都有),想到如果自己掌握了提取方法,或许以后会方便一点,根据自己的要求进行一些更改或者调整指令,为此还专门学习了一下内联汇编的使用(发现并没有什么太大的用处)。
由于我只是进行一下shellcode提取的实验,主要目的是获得一个可以打开shell的shellcode,暂时不考虑简短性通用性等问题,只要达到目的即可,基本思路,是使用execve系统调用来打开一个shell。
首先说一下系统调用,系统调用是linux交流用户空间和内核空间的一个“通道”,用户应用程序通过系统调用可以得到一些只有内核才能够提供的功能。调用系统调用的方法为使用int 0x80汇编语句,也就是0x80号中断来进行,而系统调用又要涉及到参数传递规则和一般函数的不同:系统调用通过寄存器传参,eax代表调用号,然后ebx,ecx,edx等依次为系统调用所需要的参数1,参数2,参数三等。
我们需要用到的execve系统调用的原型为:
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);其原型定义于unistd.h头文件。 filename指向需要打开的程序的位置的字符串,argv为我们的main寒素的argv了,envp为环境变量。
通过execve调用/bin/sh即可。我们将/bin/sh字符串存在程序中,然后通过一个巧妙的方式获取到其地址,将其设置为参数,然后将argv和envp设置为NULL即可,然后执行系统调用即可。
这里用到的一个trick是通过jmp和call来获得内存,jmp到一个标号,call到原来的下一句位置,这样call会将当前位置的下一个语句的地址存在栈中作为返回值,这样通过pop指令,就可以将/bin/sh字符串的位置放到esi中了,然后清空eax,用eax清空ecx/edx,再添加系统调用号,把ebx设置成sh字符串即可。(这里其实有些冗余了,只需要直接清空ecx,然后用ecx清空edx可以少一条语句)
之后,编译链接
nasm -f elf xxx.asm ld -melf_i386 xxx.o我这里使用的32位的(我系统是64位)。 然后objdump -d即可将其提取出来。