没有VC如何用CodeBlocks运行shellcode
在《0day安全:软件漏洞分析技术(第二版)》的第3章中,作者给出了一种便捷运行shellcode的方法:
char shellcode[]="\x66\x81\xEC\x40\x04\x33\xDB......";//欲调试的十六进制机器码
void main()
{
__asm
{
lea eax, shellcode
push eax
ret
}
}
原书作者是用Visual C++6.0编译代码的,操作系统是Windows XP SP2。我在Windows XP SP3下用CodeBlocks编译如上代码时则会出错,因为Visual C++6.0中的编译器和CodeBlocks中的默认编译器gcc关于内联汇编的语法有很大不同,可参考64位ubuntu中c与intel汇编混合编程之C语言内联汇编,虽然这篇文章是介绍Linux环境下的,但gcc编译器C语言内嵌汇编的语法在Windows下和Linux下是一样的。简单尝试后得到如下可行代码:
int main() {
char popup_general[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1"
"\x4F\x68\x32\x74\x91\x0C\x8B\xF4\x8D\x7E"
"\x0C\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33"
"\x32\x53\x68\x75\x73\x65\x72\x54\x33\xD2"
"\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B\x49\x1C"
"\x57\x56\x8B\x69\x08\x8B\x79\x20\x8B\x09"
"\x66\x39\x57\x18\x75\xF2\x5E\x5F\xAD\x3D"
"\x6A\x0A\x38\x1E\x75\x05\x95\xFF\x57\xF8"
"\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03"
"\xCD\x8B\x59\x20\x03\xDD\x33\xFF\x47\x8B"
"\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A\xC4"
"\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1"
"\x3B\x54\x24\x1C\x75\xE4\x8B\x59\x24\x03"
"\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD"
"\x03\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A"
"\x0A\x38\x1E\x75\xA9\x33\xDB\x53\x68\x57"
"\x21\x21\x21\x68\x5A\x5A\x5A\x47\x8B\xC4"
"\x53\x50\x50\x53\xFF\x57\xFC\x53\xFF\x57"
"\xF8";
char *p=popup_general;
// the asm code:
asm (
"push %0;\n"
"ret;"
:"=r"(p)
:
:
);
return 0;
}
编译命令是:
gcc -masm=intel shellcode.c -o shellcode
参数-masm=intel表明内联汇编采用intel语法,而不是gcc默认的AT&T语法。内嵌汇编中的%0指的便是第一个参数p,gcc编译时会把p保存到某个通用寄存器中(关键字r指明这一点),故可以直接push %0,之后紧接着ret指令将EIP寄存器的值修改为刚刚压入栈中的值,即p的值,这样shellcode便会紧接着运行。这段shellcode会弹出一个消息框,之后正常退出,运行结果如下图所示:
补充说明一下gcc编译器的参数问题,你可以在codeblocks中通过图形化界面设置参数,也可以和我一样找到gcc所在的路径,设置环境变量,然后在cmd命令行窗口中手动输入整个编译命令。我的机器上gcc所在的目录是:C:\Program Files\CodeBlocks\MINGW\bin\。嫌每次都输命令麻烦可以写一个简单的批处理shellcode.bat:
gcc -masm=intel shellcode.c -o shellcode
pause
(2016年8月3日更新)
在exploit-db看到shellcode是这样运行的:
#include<stdio.h>
char shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\x0C\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x57\x56\x8B\x69\x08\x8B\x79\x20\x8B\x09\x66\x39\x57\x18"
"\x75\xF2\x5E\x5F\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95\xFF\x57\xF8"
"\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59\x20\x03\xDD"
"\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A\xC4\x74\x08"
"\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75\xE4\x8B\x59"
"\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03\x2C\xBB\x95"
"\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB\x53\x68\x57"
"\x21\x21\x21\x68\x5A\x5A\x5A\x47\x8B\xC4\x53\x50\x50\x53\xFF\x57"
"\xFC\x53\xFF\x57\xF8"//181
;
main()
{
(* (int(*)()) shellcode)();
}
我的理解如下: int()() 是一个函数指针,该函数的参数为空,返回值为int型,( int()() ) shellcode 是强制类型转换,将原本为char [ ]的变量转换为函数指针,* ( int(*)() shellcode )取出该函数,最后的()表示函数调用。这样不用内联汇编,便可以执行shellcode,果然是人外有人,天外有天。