栈溢出实验

目标

​ 通过对程序输入的密码的长度、内容等修改,实现缓冲区溢出的发生实验,栈溢出问题涉及到计算机体系结构、内存管理、编程语言和程序执行原理等多个领域。通过实验,可以深入了解计算机系统的底层工作原理,从而更好地理解计算机科学和计算机工程。

测试步骤与结果

淹没相邻变量改变程序流程实验

源文件分析

​ 观察源文件中的判断函数,可以看到strcpy()函数没有限制长度,因此可以确定这其中有栈溢出的漏洞

2

x32dbg中定位子函数

​ 通过x32dbg,我们根据主函数我们找到了verify_password函数所在位置,并进行分析,尝试了解这个函数中的栈结构

1

栈结构分析

​ 我们输入的字符串是12345+6,从图中的栈结构里我们看到,从0019FAC8地址往下的栈中装的是我们的输入值转换为的ASCII码,也就说明这一段是分给strcpy的缓冲区,通过之前的分析,我们知道了在0019FAD0中装的是经过strcmp函数判断之后的返回值,若为0则表示与预设密码相同,而这个变量在buffer缓冲区的下面,说明先进栈,也就是authenticated变量的值,于是我们就可以知道如果将输入的值的长度增加,由于strcpy没有长度限制,所以输入的值可以溢出到下面,来改变authenticated变量的值,以达到最终改变整个函数返回值的目的

3

进行淹没相邻变量

​ 我们可以看到分给strcpy()得到的值的位置只有8字节,其中还有c语言数组自带的一字节空字符,所以只要我们输入超过7字节的内容就可以达到溢出的目的,而我们的最终目的是让0019FAD0地址处的值为0,因此我们需要输入的是8字节的任意内容加3字节的空字符,通过python生成文本进行输入即可,生成的带有空字符的字符串为“UUUUUUUU ”进行输入,再观察栈中内容的变化如下:

4

​ 我们看到本来存储strcmp判断结果的栈中已经被flood,显示为我们想要的0,这样我们就可以越过判断了。

结果展示

5

修改原理图示

0ea8549b83c8db640c164e0dad0585e

python代码附录

1
2
3
4
5
6
inp = '55'*8 + '00'*3
hex_pairs = [inp[i:i+2] for i in range(0, len(inp), 2)]
ascii_string = ''.join([chr(int(pair, 16)) for pair in hex_pairs])

with open(r'C:\Users\Kurumi\Desktop\软件安全\实验2-栈溢出\2-栈溢出\overflow_var\Debug\password.txt', 'w') as f:
f.write(ascii_string)

淹没返回地址改变程序流程实验

栈结构分析

​ 由于这两个实验的子函数相同,所以上一道题中分析过的这里就略过。现在我们分析一下子函数的返回值情况,这次我们在文件中输入的是233122,可以看到和之前的结果大同小异,都是在buffer缓冲区下存储着authenticated的值,而在这个值之后我们可以看到函数结束后返回的地方是00401114地址处,于是我们就可以将buffer长度增加到淹没这个返回地址,并把它改成输出正确的函数的地址处,这样我们就可以直接跳过判断了。

6

进行淹没返回地址

​ 我们可以看到输出”congratulations“的函数所在地址为0040112F,所以我们只需要把前面16个字节占掉并把这个地址写入即可,由于是小端存储,所以我们只需要在文件中写入16字节的随意内容加上’2F1140‘转化为ASCII码的字符即可。用python生成这样一串字符:”/@ “写入password.txt文件中

7

结果展示

​ 写入后重新调试文件,可以看到栈中的内容发生的变化:

8

​ 这样在子函数进行之后就会自动跳转到输出正确的函数了,下面来看一下结果:

9

修改原理的图示

1474234cd660d5f99a1bffacd810410

python代码附录

1
2
3
4
5
6
inp = '11'*16 + '2F114000'
hex_pairs = [inp[i:i+2] for i in range(0, len(inp), 2)]
ascii_string = ''.join([chr(int(pair, 16)) for pair in hex_pairs])

with open(r'C:\Users\Kurumi\Desktop\软件安全\实验2-栈溢出\2-栈溢出\overflow_ret\Debug\password.txt', 'w') as f:
f.write(ascii_string)

实验结论

​ 栈溢出实验是一项重要的计算机科学实践,通过这个实验,我们更加深入了解了计算机底层原理、内存管理和程序执行过程。在实验中我学到了如何发现和利用栈溢出漏洞。通过这次实验,不仅加深了我对计算机系统的理解,还提高了我的调试技能。并认识了如何使用调试工具来定位和修复程序中的错误,这将对我们今后的软件开发和故障排除工作非常有帮助。

​ 此外,栈溢出实验也向我们展示了软件安全的重要性,栈溢出是一种常见的计算机安全漏洞,而解决这类漏洞却需要严密的代码审查和漏洞修复,这个经验对于将来从事信息安全相关工作的我们来说,是非常宝贵的。

附加题

分析PE格式加载到内存中的地址变化

​ 刚进入PE文件时,装载地址为77891000,从77941A93开始进行指令的装入,而程序指令在装入内存之后基地址为00401000,偏移量为77850000字节。

分析函数跳转时sp,bp,ip的变化

1、接下来分析一下主函数在调用子函数时候的情况,首先看一下主函数的结构与此时的指针:

主函数

0

2、在执行输出‘Address of foo = %p\n’的函数之前,先Push入了foo()函数的地址401000,之后push入保存着‘Address of foo = %p\n’字符串的地址4060DC,指针变化如下,可以看到ESP改变了8字节,而EBP没有变化,EIP变化了10字节

1

内存如下,可以看到栈中从底到顶依次装着返回地址、foo()函数位置还有待输出的字符串:

1.1

3、在执行输出‘Address of bar = %p\n’的函数之前经历的过程与上面相似,这里不再多介绍,再执行完输出‘Address of bar = %p\n’的函数之后,各个指针如下,可以看到EBP还是没有变,ESP又减少了8字节,说明就是刚刚进栈的两个元素占了8字节,EIP指向401092

2

内存如下,可以看到‘Address of bar = %p\n’字符串和bar()函数的地址位置依次进入了栈:

2.1

4、在进入foo函数之前,push了ecx进入栈中,内存和指针分别如下,可以看到EBP仍然没有变,EIP指向401096,而ESP又减少了4字节,因为PUSH了ecx进入栈中

3.1

3.2

5、进入foo函数,栈指针和内存如下,EBP仍然没有变化,而ESP向上移动了4字节,foo函数的返回值入栈,EIP指向401000。

4.1

4.2

6、进入foo函数后,esp进行的减12字节的操作,中间三段地址装了传入的参数,内存和指针如下

5.1

5.2

7、总的说来就是,进入函数时,先push入需要调用的外面的参数,之后Push入函数的返回地址,最后再push入局部变量的地址

淹没返回地址进行bar函数的输出

程序逻辑分析

主函数

​ 可以看到先调用了两个输出子函数地址的函数,之后把我们往程序中输入的内容的地址放在ecx中并压入栈,最后进入到foo函数中

1

foo函数

​ 可以看到有一条命令repne movsd,这条命令就是起到复制的功能,因此可以考虑在这里进行栈溢出攻击

2

bar函数

3

进行栈溢出攻击

​ 我们输入987654,在执行foo函数过程中观察栈的内存变化,可以看到在做完repne movsd操作之后,我们输入的987654放入了之前进入函数时候开辟出的一块12字节的空白区域中,而这段区域下紧接着就是返回的地址,因此我们可以尝试输入一个11字节的数据来对返回地址进行淹没(后面还有一字节自带的空白符),前8字节的数据可以为任意数据,只需要让后三字节数据指向bar地址00401060即可,通过python我们生成了符合要求的字符串:‘`@’

4

​ 我们尝试把这段字符串输入之后观察内存变化,可以看到之前存放着返回地址的位置已经被替换为bar函数的地址5

​ 在执行之后我们可以看到指针顺利跳到bar函数处

6

结果展示

7

修改原理展示

1b12003ddb23e91c2da021ee71ddf76

修改程序

把foo函数返回地址改为bar函数进入地址即可实现破解

8

运行破解后的文件,成功

9

python代码附录

1
2
3
4
5
6
inp = '11'*12 + '601040'
hex_pairs = [inp[i:i+2] for i in range(0, len(inp), 2)]
ascii_string = ''.join([chr(int(pair, 16)) for pair in hex_pairs])

with open(r'C:\Users\Kurumi\Desktop\软件安全\实验2-栈溢出\2-栈溢出\overflow_ret\Debug\password.txt', 'w') as f:
f.write(ascii_string)