简单分析栈以及栈溢出

简单分析栈以及栈溢出

目标代码

#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的地方。

上一篇:leetcode 151. 翻转字符串里的单词


下一篇:Robust adaptive bamforming using worst-case performance optimization: a solution to the signal misma