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

时间:2021-10-28 05:15:49

参考书目:0day安全:软件漏洞分析技术 
相关工具使用:OD,IDA Pro,VC++6.0,UltraEdit

最近需要做课堂演习,就选了缓冲区溢出的实践。主要参考0day安全这本书,一面一句话很经典: 
To be the apostrophe which changed Impossible into I’m possible!

直接步入正题:

1. 反汇编修改程序

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

#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;
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

代码就是简单验证输入密码是否正确,首先用IDA打开编译生成的应用程序。看到这样一个图(好像每次打开生成的东西都不太一样~) 
C语言实现缓冲区溢出实例
如果看不到地址可以在Options->General在Line prefixes前打钩以及将Number of opcode bytes设置为6就可以了。这样就可以找到代码中相应的跳转部分,然后用OD打开对应的EXE文件,看到下图: 
C语言实现缓冲区溢出实例 
找到004010D5这条命令就是对应的判断地方了,对应的汇编命令为:

JE X1.004010E6
 
 
  • 1
  • 1

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

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

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

int verify(char *password)
{
int auth;
char buffer[8];
auth = strcmp(password, PASSWORD);
strcpy(buffer, password);
return auth;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
看似还是正常的一个程序却会发生不可思议的事情,不信运行程序输入qqqqqqqq试试,发现没,通过验证了!这是为什么呢?来看看栈里面数据的存放:

C语言实现缓冲区溢出实例 
啊,原来是这样啊,输入qqqqqqqq,或者输入其他8位也行(11111111不行,自己探索吧)在strcmp的时候auth确实是1,但是在进行strcpy之后,buffer里面的数据太长了,占了auth的位置,将auth从1改成了0。 
看来下次写程序要注意点了!

3. 修改程序执行流程

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

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

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

4 代码植入

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

int verify(char *password)
{
int auth;
char buffer[44];
auth = strcmp(password, PASSWORD);
strcpy(buffer, password);
return auth;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

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
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上面是MessageBox的四个参数,如果弹出标题和内容都是hellobug的串,四个参数分别为: 
NULL, “hellobug”, “hellobug”, NULL 
根据MessageBox得到应该执行的汇编代码

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

因为缓冲区有44个字节,而四个字节一组,因此有11组,加上原来的auth,EBP和返回地址,一共14组,每组4个字节。新建一个这样的文件。 
C语言实现缓冲区溢出实例 
我在执行的过程中最后部分0018FD44就是Buff的起始地址。

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

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

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

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

#include <stdio.h>
#include <windows.h>

int main(void)
{

LoadLibrary("user32.dll");
MessageBoxA(NULL, "abc", "def", NULL);
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

写这样一个简单的程序用OD打开以下就好啦 
C语言实现缓冲区溢出实例 
找到MessageBox回车之后就进入了,从这个图可以看出入口地址是768EFD1E 
然后整个文件就制作完成了,运行一下看看吧 
C语言实现缓冲区溢出实例

以上就是一个简单的缓冲区溢出的实例了,不过在植入代码这个过程中电机确定之后~~就挂了,因为一些参数没有设置好,慢慢再深入研究吧。