您的位置:首页 > 编程语言 > C语言/C++

C语言实现缓冲区溢出实例

2015-06-05 15:58 585 查看
参考书目:0day安全:软件漏洞分析技术

相关工具使用:OD,IDA Pro,VC++6.0,UltraEdit

最近需要做课堂演习,就选了缓冲区溢出的实践。主要参考0day安全这本书,一面一句话很经典:

To be the apostrophe which changed Impossible into I’m possible!

直接步入正题:

1. 反汇编修改程序

在实现缓冲区溢出之前,简单熟悉一下反汇编重用的两个软件,OD和IDA,用OD修改程序代码实例。

[code]#include <stdio.h>
#define PASSWORD "1234567"
int verify(char *password)
{
    int auth;
    auth = strcmp(password, PASSWORD);
    return auth;
}

int main(void){
    int flag = 0;
    char pass[1024];
    while(1){
        printf("enter the password:\t");
        scanf("%s", pass);
        flag = verify(pass);
        if(flag)
            printf("password incorrect!\n");
        else{
            printf("congratulation!\n");
            break;
        }
    }
}


代码就是简单验证输入密码是否正确,首先用IDA打开编译生成的应用程序。看到这样一个图(好像每次打开生成的东西都不太一样~)




如果看不到地址可以在Options->General在Line prefixes前打钩以及将Number of opcode bytes设置为6就可以了。这样就可以找到代码中相应的跳转部分,然后用OD打开对应的EXE文件,看到下图:




找到004010D5这条命令就是对应的判断地方了,对应的汇编命令为:

[code]JE X1.004010E6


于是将JE改成JNE,再运行程序,这时发现原来正确的密码不正确,原来错误密码都可以通过!用OD还可以对程序保存,一个简单的破解就完成了。(OD保存软件不会的话可以网上搜一下,我当时没搞懂还是请教的大神)

2. 缓冲区溢出漏洞修改邻接变量

还是刚刚的类似程序,假如程序员一不小心或者因为要在其他地方使用字符串,加了个strcpy函数,验证函数如下所示:

[code]int verify(char *password)
{
    int auth;
    char buffer[8];
    auth = strcmp(password, PASSWORD);
    strcpy(buffer, password);
    return auth;
}


[code]看似还是正常的一个程序却会发生不可思议的事情,不信运行程序输入qqqqqqqq试试,发现没,通过验证了!这是为什么呢?来看看栈里面数据的存放:





啊,原来是这样啊,输入qqqqqqqq,或者输入其他8位也行(11111111不行,自己探索吧)在strcmp的时候auth确实是1,但是在进行strcpy之后,buffer里面的数据太长了,占了auth的位置,将auth从1改成了0。

看来下次写程序要注意点了!

3. 修改程序执行流程

是看了上面的原理图,是不是懂了点什么!EBP和返回地址是啥?不禁心生恶意,我要是再长点,是不是把返回地址给换了???还有那个EBP啥东西???

事实上,EBP是PE文件执行时候在函数调用时函数调用前栈底指针,在函数调用时会重新生成一个比较小的栈,此新栈为调用的函数所用,因此需要将之前栈的栈底入栈。在调用新的子程序的时候,也需要将子函数的返回地址入栈,不然子函数执行完了,计算机就不知道下一步执行什么了~

搞懂了这点知识,我们就可以利用这个来修改程序的执行流程了,具体实验可以自己做。

4 代码植入

之前例子的缓冲区较小,正常的函数中如果使用的话,缓冲区可能是比较大的,下面用新的例子来测试缓冲区溢出中的代码植入,完整的代码见上传的文件。

[code]int verify(char *password)
{
    int auth;
    char buffer[44];
    auth = strcmp(password, PASSWORD);
    strcpy(buffer, password);
    return auth;
}


此次的实验中由于控制台输入的限制性,将输入流改为文本。实验的目的是利用缓冲区溢出漏洞植入代码,代码内容为弹出一个新的窗口。

[code]int MessageBox(
  HWND hWnd,          // handle of owner window
  LPCTSTR lpText,     // address of text in message box
  LPCTSTR lpCaption,  // address of title of message box
  UINT uType          // style of message box
);


上面是MessageBox的四个参数,如果弹出标题和内容都是hellobug的串,四个参数分别为:

NULL, “hellobug”, “hellobug”, NULL

根据MessageBox得到应该执行的汇编代码

[code]Xor ebx, ebx
Push    ebx
Push    68656C6Ch
Push    6F627567h
Mov eax, esp
Push    ebx
Push    eax
Push    eax
Push    ebx
MOV EAX, ADDRESS
CALL EAX


最后的Address是MessageBoxA地址,对应的机器码如下:

[code]33 DB
53
68 6C 6C 65 68        //hell
68 67 75 62 6F        //obug
8B C4
53
50
50
53
B8 ADDRESS
FF D0


因为缓冲区有44个字节,而四个字节一组,因此有11组,加上原来的auth,EBP和返回地址,一共14组,每组4个字节。新建一个这样的文件。




我在执行的过程中最后部分0018FD44就是Buff的起始地址。

接下来的问题就是buff的起始地址以及MessageBox的入口地址怎么找到呢?

首先是buff的起始地址,这个比较简单,将该文件写为14组1234,然后在OD里面跑一遍之后可以看到内存里面的数据,这个数据区的起始地址就是就是buff的起始地址。

然后是MessageBox的入口地址,在0Day安全这本书中有计算过程,我按照这个过程没有实现,因此我自己找了一个方法,有兴趣的同学可以按照书上的计算一下~

我的试验工程就是新建一个工程,只允许Messagebox然后通过OD查看其入口地址

[code]#include <stdio.h>
#include <windows.h>

int main(void)
{

    LoadLibrary("user32.dll");
    MessageBoxA(NULL, "abc", "def", NULL);
    return 0;
}


写这样一个简单的程序用OD打开以下就好啦




找到MessageBox回车之后就进入了,从这个图可以看出入口地址是768EFD1E

然后整个文件就***完成了,运行一下看看吧



以上就是一个简单的缓冲区溢出的实例了,不过在植入代码这个过程中电机确定之后~~就挂了,因为一些参数没有设置好,慢慢再深入研究吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: