简单分析栈以及栈溢出
目标代码
#include<iostream>
#include<cstring>
bool IsPasswordOkay(void)
{
char Password[12] = {0,1,2,3,4,5,7,8,9,10,11};//直接将数组填满
for(int i=12;i<20;i++)//覆盖EBP和ESP所指向的返回地址
{
printf("NO.%d\t",i);
scanf("%x",&Password[i]);
printf("NO.%d put in hex is %c\n",i,Password[i]);
}
return 0==strcmp(Password,"goodpass");
}
int main()
{
bool PwStatus;
puts("Enter");
PwStatus = IsPasswordOkay();
if(PwStatus == false){
puts("denied");
exit(-1);
}
else puts("Access Granted");
}
这里将输入密码的地方直接用0~11填满,直接进行后面的覆盖。
由于EBP指针和ESP指向的地址都是8位16进制
在OD中可以看到存储Password数组的内容都是MOV BYTE PTR SS:[LOCAL.3]
其翻译过来是MOV BYTE PTR SS:[EBP-0C],0
,表示把0赋值给距离EBP指针12(16进制为0C)的一个byte,那么装满了12个元素的字符数组Password的最后一个元素的地址[EBP-01]和EBP相邻。
CPU Disasm
地址 十六进制转储 命令 注释
00401048 |. C645 F4 00 MOV BYTE PTR SS:[LOCAL.3],0
0040104C |. C645 F5 01 MOV BYTE PTR SS:[LOCAL.3+1],1
00401050 |. C645 F6 02 MOV BYTE PTR SS:[LOCAL.3+2],2
00401054 |. C645 F7 03 MOV BYTE PTR SS:[LOCAL.3+3],3
00401058 |. C645 F8 04 MOV BYTE PTR SS:[LOCAL.2],4
0040105C |. C645 F9 05 MOV BYTE PTR SS:[LOCAL.2+1],5
00401060 |. C645 FA 07 MOV BYTE PTR SS:[LOCAL.2+2],7
00401064 |. C645 FB 08 MOV BYTE PTR SS:[LOCAL.2+3],8
00401068 |. C645 FC 09 MOV BYTE PTR SS:[LOCAL.1],9
0040106C |. C645 FD 0A MOV BYTE PTR SS:[LOCAL.1+1],0A
00401070 |. C645 FE 0B MOV BYTE PTR SS:[LOCAL.1+2],0B
而在堆栈中,可以看到程序中的存储情况。临时变量(指这里的数组Password)存储在栈中,并且最后一个元素的地址与EBP的地址相邻。
程序返回时ESP所指向的返回地址指向值是函数ISpasswordOkay的下一句的地址
那么我们可以通过覆盖EBP指针和返回地址改变程序的返回,从而控制程序下一步的执行。
这里的控制方法是使用循环输入EBP和返回地址的值。
for(int i=12;i<20;i++)//覆盖EBP和ESP所指向的返回地址
{
printf("NO.%d\t",i);
scanf("%x",&Password[i]);
printf("NO.%d put in hex is %c\n",i,Password[i]);
}
EBP和返回地址分别占用4个字节故使用8次输入一个字节的内容。
目标为
CPU Disasm
地址 十六进制转储 命令 注释
0040117D |> \68 50304300 PUSH OFFSET 00433050 ; ASCII "Access Granted"
输入内容为
Enter
NO.12 30
NO.12 put in hex is 0
NO.13 ff
NO.13 put in hex is
NO.14 19
NO.14 put in hex is
NO.15 00
NO.15 put in hex is
NO.16 7d
NO.16 put in hex is }
NO.17 11
NO.17 put in hex is
NO.18 40
NO.18 put in hex is @
NO.19 00
NO.19 put in hex is
Access Granted
这里EBP指针是main函数的EBP,返回地址是0040117D
即将"Access Granted"入栈准备输出的地址
在函数ISpasswordOkay进行返回时
CPU Disasm
地址 十六进制转储 命令 注释
004010F6 |. 8BE5 MOV ESP,EBP
004010F8 |. 5D POP EBP
004010F9 \. C3 RETN
MOV ESP,EBP:EBP的值会给到ESP,然后会将存储在栈中的main的EBP指针的值取出(这里进行的操作的结果就是在程序返回后,ESP的值改成了我们写入的0019FF30
(其实原来就是这个值,只是象征性的写一下,意思就是ISpasswordOkay的EBP存储main的EBP地址,而main的EBP存储的是win32内核的EBP指针的地址)
POP EBP :会把之前存储在栈中的ISpasswordOkay的EBP的值取出,并且将栈顶弹出,栈顶弹出后(ESP指向返回地址
!!!!!!)
EBP的值不对的话会使程序崩溃。(当然是试过了)
RETN 基于ESP现在指向的地址进行返回
结果就是成功的返回到输出Access Granted的地方。