【实验内容】
利用栈溢出修改变量值
利用栈溢出修改函数返回地址,运行恶意代码
【实验类型】验证型实验
【实验学时】3学时
【实验原理】
如果向栈上声明的缓冲区中复制数据,但是复制到数据量又比缓冲区大的时候,就会发生栈溢出。在栈上声明的各种变量的位置紧临函数调用程序的返回地址。若用户输入的数据未经验证就传递给strcpy这样的函数,则会导致变量值被攻击者指定的值所改写或调用函数的返回地址将被攻击者选择的地址所覆盖,打乱程序正常运行流程,转而执行恶意代码。
防范栈溢出的有效方法有:禁止栈执行,从而阻止攻击者植入恶意代码;编写安全可靠的代码,始终对输入的内容进行验证;利用编译器的边界检查实现栈保护。
【实验环境】
Linux实验台(CentOS)
gcc和gdb工具
【实验步骤】
打开Linux实验台,输入用户名和口令,进入系统。
一、修改变量值
(1)新建stackover1.c文件:touch stackover1.c;
(2)编辑stackover1.c文件:vim stackover1.c,按i键进如编辑模式,输入示例程序1,按Esc键退出编辑模式,进入命令行,输入:wq保存并退出文件编辑,上述过程如图3.5.37和图3.5.38所示;
(3)查看编辑结果:cat stackover1.c,如图3.5.39所示;
(4)生成可执行文件:gcc stackover1.c –o stackover1;
(5)运行执行文件:./ stackover1 ABCDE,执行结果如图3.5.310所示;
从结果中可以看出,x的值为程序中的设定值10;
(6)在内存中x紧邻buf,所以输入11个字符制造栈缓冲区溢出,以修改x所在地址空间的值:./ stackover1 ABCDEFGHIJK,运行结果如图3.5.311所示;
x的值为75,对应K的ASCII码,x的值发生改变;
(7)利用perl命令输入适当的字符串,将x的值修改为任意指定的值,如修改为30,十六进制为0x1e:./ stackover1 `perl –e ‘print “A”x10 . “\\x1e”’`,运行结果如图3.5.312所示;
x的值为30,修改成功。
示例程序1:
#include #include void function(char * str) { } int main(int argc,char * argv[]) { function(argv[1]); return 0; } 二、修改函数返回地址,运行恶意代码 (1)参照上述步骤,新建stackover2.c文件,输入示例程序2,生成可执行文件stackover2; (2)运行可执行文件并查看结果:./ stackover2 AAAAA,结果如图3.5.313所示; 从结果中可以看出程序运行的栈情况,foo函数的返回地址位于第7个位置上,bar的函数入口地址为0x08048421; (3)随意输入多个字符制造栈溢出:./ stackover2 `perl –e ‘print “A”x25’`,运行结果如图3.5.315所示; (4)构造输入字符串,将第7个位置修改为bar函数入口地址,即修改foo函数的返回地址为bar函数的入口地址,以修改程序运行流程: ./ stackover2 `perl –e ‘print “A”x14 . “\\x21\\x84\\x04\\x08”’`,运行结果如图3.5.317所示; bar函数运行,打印出“Hi!You’ve been hacked!”,攻击成功。 通常我们无法获取函数的地址和程序运行时栈的情况,需要通过反编译来查看变量地址或函数的入口地址,通过深入理解程序运行时的内存分配来计算函数返回地址在内存中的位置,以便达到修改程序运行流程、执行恶意代码的目的 示例程序2 #include #include void foo(const char *input) { } void bar(void) { } int main(int argc,char *argv[]) { foo(argv[1]); } 【实验思考】 尝试分析并画出上述程序的栈结构,可借助gdb命令; 尝试在Windows环境下进行上述实验,并分析与Linux环境下的异同; 根据程序运行时的内存分配原理计算函数的返回地址;以示例程序3为例,通过计算快速实现nevercalled函数的运行。 示例程序3 #include #include void nevercalled() { printf("never called\\n"); exit(0); } void functioncalled(char *arg) { char buf[10]; int x,y,z,w; strcpy(buf,arg); } int main(int argc,char * argv[]) { functioncalled(argv[1]); return 0; }