打开慢腾腾的MSN邮箱,本来没有抱什么希望的我突然眼前一亮,”A New Message”,不会又是垃圾邮件来浪费我的表情吧?4、5seconds之后,蜗牛般的浏览器终于给出了答案。From: Randy Bryant Randy.Bryant@cs.cmu.edu。
3、4天前在《深入理解计算机系统》上遇到的一个家庭作业问题令我揪心了很久。我分析了各种情况,得出的结论是只有一种办法可以解决这个问题。但是在我实行的时候却意外地收到一个系统给出的”Segmentation fault”。反复检查了输入的字符串,确实没有问题。用gdb调试时发现错误发生在子函数返回的ret语句上。设置断点,检查各相关寄存器的值,一切都符合预期。为什么一到ret就挂呢?猛然间我想起了几年前看过的一片介绍性的文章,讲的是“缓冲区溢出的保护措施”。我又把返回地址改为程序段任意一个地址,这次ret通过了。原来这个“先进”的操作系统从底层就禁止在数据段/程序堆栈的地方运行代码,怪不得我ret到自己预设的地址(位于程序堆栈内)时会收到错误。
这个问题看来是弄清楚了,但是怎么解决那个家庭作业呢?想了很久还是没有答案。旁边又没有一个钻研这个东西的同志。能不能问问作者啊?我想。
以前从来没有给国外的陌生人发过邮件。屈指可数的几次经历是给Konami美国分部的网站写了一封信询问某游戏、Sony的网站询问能否下载sonicstage。好在对方都有回复,现在胆子也大了点。
问就问吧,也没什么大不了的。
To: randy.bryant@cs.cmu.edu
Sent: Saturday, June 25, 2005 9:24 AM
Subject: A question about one of CS:APP's homework
Hello Prof. Bryant,
I got a problem when I was reading Chapter 3 of CS:APP. That is homework 3.38. The description is as followed(copied from the book).
Homework Problem 3.38 [Category 3]:
In this problem, you will mount a buffer overflow attack on your own program. As stated earlier, we do not condone using this or any other form of attack to gain unauthorized access to a system, but by doing this exercise, you will learn a lot about machine-level programming...
To solve this problem, I think the only way is to overwrite the return address of function getbuf(), make it point to a specified address where some executive codes is set from the input. I run the program under GDB. When I do this, I received a segmentation fault. Maybe the operation system prevents program running from the data segment or program stack. I wonder is there any other way to change the process of a program? What is the exact inputing string to make getbuf() return 559038737(0xdeadbeef) to test()?
If you're too busy to reply, just give me the answer.
Linux Kernel is version 2.6.9-1.667, gcc version 3.4.2, gdb version 6.1
Thanks a lot. Your help is very essential to my understanding of this book.
Sonny
Jun 26, 2005
国外的
教授就是负责任啊。两天后我收到了一封长长的reply。
From : Randy Bryant <Randy.Bryant@cs.cmu.edu>
Sent : Monday, June 27, 2005 10:08 PM
Subject : RE: A question about one of CS:APP's homework
You'll notice that this problem is rated as a difficult problem. That's partly because it's hard to determine why a particular solution doesn't work. To solve this problem, you need to have an exploit string that:
1. Overwrites the return address to be that of your exploit code
2. Write exploit code that alters the return value, corrects the return address, and re-executes the return
3. Sets the saved value of %ebp to its original value.
If you do any of these steps wrong, you'll get a segmentation fault. You can used GDB to help you step through the code, but even this is difficult.
That said, it is possible that your Linux system has mechanisms that make the assignment much more difficult. Some versions of Linux 2.6 contain a technology called "ExecShield". This does the following:
* Make it so that code on the stack is not executable
* Use randomization, so that the stack locations change with each execution.
You can test whether your version is using randomization by running a program under GDB multiple times. Set a breakpoint in main, and each time check the value of %ebp. If it changes every time, then your OS is using randomization.
You can disable Execshield at the time you boot your system.
文中提到的ExecShield就是一直困扰我的保护机制。首先它使程序堆栈内的代码不可执行;其次,每次执行的时候都会随机地改变程序堆栈的地址,这样就使攻击者无法得知每次的堆栈地址而无法算出返回地址(当然可以用调试器gdb得到,但这样就没有使用价值,仅仅适合研究)。
Bryant教授提到的3个必须点也都是我想到的:1. 覆盖函数的返回地址以使之指向你自己设置的代码;2. 在自己设置的代码中改写程序的行为(此处为改变返回值),更正返回地址,重新执行ret操作;3. 恢复原先的ebp寄存器的值。
虽然由于底层系统的高安全性没能完成这个作业,但是我学到的很多东西。更清晰地了解了汇编级代码的工作过程,尤其是函数的调用与返回;对缓冲区溢出这个概念有了一些深入的体会,当今一些网络攻击程序的基本攻击原理算是初步理解(尽管经过矛与盾的较量,攻击的手段变得越来越高超,但是其本质的东西是不会变的);第一次给国外的
教授写信求教并且得到了非常详尽的解答,心中还是非常开心的,呵呵呵呵。