到现在才做这样的尝试,真是不好意思.不过还是写出吧. 先讲讲如何通过改写返回地址来改变程序流程
代码: w32下编译vc6
缓冲区溢出就是:
1函数调用要把返回地址保留放在栈中,当程序返回时,ret指令会把栈中的地址弹出到ip计数器,然后继续执行
2栈中的内容可能被改写,当内容覆盖了返回地址后,程序就会跳转到别的地方执行.
下面举个例子,看不懂没关系
低------------------------buffer2-buffer1-ebp-retAddr-para---------高
当输出的内容超过缓冲区长度后,就会覆盖ebp和返回地址.当然,一般的输入只会造成段错误,精心构建的缓冲区溢出代码才会影响软件
void function( int a, int b, int c) ... {
char buffer1[5];
char buffer2[10];
int *ret;
//栈示意图
ret =(int *) (buffer1 + 12); //加12是因为buffer1占8个字节(由于内存对齐),然后原来的ebp占4个
(*ret) += 10; //为什加10后面会说
}
void main() ... {
int x;
x = 0;
function(1,2,3); //我们的目标是跳过x=1的赋值,直接执行printf
x = 1;
printf("%d ",x);
}
先看看main的反汇编代码.用vc6的debug功能,运行中设置断点,然后在main上面右键选 反汇编
00401060 push ebp
00401061 mov ebp,esp//上面两句是过场,每个函数都要保存ebp,这样建立了一个所谓的帧
00401063 sub esp,44h//sp向下,给局部变量提供空间,装buffer1和buffer2
00401066 push ebx
00401067 push esi
00401068 push edi
00401069 lea edi,[ebp-44h]
0040106C mov ecx,11h
00401071 mov eax,0CCCCCCCCh//把中间的内容全部填成ccccc,防止信息泄露用
00401076 rep stos dword ptr [edi]
13: int x;
14:
15: x = 0;
00401078 mov dword ptr [ebp-4],0
16: function(1,2,3);
0040107F push 3
00401081 push 2
00401083 push 1
00401085 call @ILT+0(function) (00401005)//在这个call了,我们要跳过下面x=1的语句
0040108A add esp,0Ch//在这里我注意到call后面的语句地址是08a,这个值会被压栈,我们要改这个值
17: x = 1;
0040108D mov dword ptr [ebp-4],1//我们要跳过
18: printf("%d ",x);
00401094 mov eax,dword ptr [ebp-4]//这是我们希望执行的语句,94-8a=10,我们返回地址要加10
00401097 push eax
00401098 push offset string "ret %x" (00420f74)
0040109D call printf (004010d0)
004010A2 add esp,8
19: }
004010A5 pop edi
004010A6 pop esi
004010A7 pop ebx
004010A8 add esp,44h
004010AB cmp ebp,esp
004010AD call __chkesp ( 00401150 )
004010B2 mov esp,ebp
004010B4 pop ebp
004010B5 ret
右建在vc里面添加寄存器,和内存查看的窗口,你会用的着的.
这是function里面的两句做的事
7: ret =(int *) (buffer1 + 12);
0040B7D8 lea eax,[ebp+4] //ebp+4是返回地址,放入eax
0040B7DB mov dword ptr [ebp-18h],eax//把这个地址放入ret
8:
9: (*ret) += 10;//修改这个地址
0040B7DE mov ecx,dword ptr [ebp-18h]
0040B7E1 mov edx,dword ptr [ecx]
0040B7E3 add edx,0Ah
0040B7E6 mov eax,dword ptr [ebp-18h]
0040B7E9 mov dword ptr [eax],edx
10: }
执行完这些以后,返回地址就从8a改成了94.
0012FF20 0040108A 改前
0012FF24 00000001
0012FF28 00000002
0012FF2C 00000003
改后
0012FF20 00401094
0012FF24 00000001
0012FF28 00000002
0012FF2C 00000003
最后ret会把94这个地址弹到ip,然后下面就跳过了x=1这句了
不过程序最后会在_chkesp函数里面出错,原因是我们虽然跳过了x=1
的赋值语句,也跳过了汇编中0040108A add esp,0Ch这句,这句的目的是让esp回到调用前的位置.
由于调用事压了1,2,3这三个数,所以要加0c=12个字节.
没有这句esp就和开始调用不一致拉.不过没有关系.缓冲区溢出时我们的代码会用exit()退出,所以根本运行不到后面的内容.
eof