摘要:
原来的版本:http://blog.csdn.net/bigbug_zju/article/details/39892129
原版本中的问题主要在于调试过程中,蛮力的痕迹太重,没有很好地体现常用的调试准则;本文在原版本的基础上,融入参考文献中提及的调试原则,重新审视和操练该问题,希望尽量体现出调试中常用的思维法则。
测试的平台:
1. ubuntu 9; gcc 4.4.1; Gdb 7.0-ubuntu
2. ubuntu系统安装在virtual box 3.2.8虚拟机上;
问题重述:
此处简要地描述下原来的问题,具体细节查看原文,我们希望采用stackoverflow aaaabbbbccccddddeeee中的eeee的值覆盖stackoverflow中overflow到main函数的返回地址,调用overflow返回到main函数时,控制eip,使其中的值为0x63636363;但是实际过程中问题在于eip的值并不是设想的0x63636363,而是0x61616161;
原因何在?
调试过程:
调试原则1:要去看,不是去想;
一开始,我们不能假设性地认为问题就出在overflow返回到main的过程中(既然出问题了,就说明系统的行为跟你想象的差别很大,问题发生在何处都不奇怪)而应该实际地单步运行下看看,看看问题到底在什么地方?通过设置断点,以及单步运行,我们发现eip的控制并不出现在overflow返回到main的过程,而是main函数退出的
过程;
调试原则2:理解系统
根据函数的调用过程,在call前会将当前主函数的下一句地址压栈;在进入函数后,首先执行:push ebp; mov ebp, esp;
退出函数时,mov esp, ebp; pop ebp。最后调用ret指令时,将call指令压入的下一句地址弹出到eip中。根据上述系统模型,大概需要查看的分支为:
1. main函数返回的地址被修改(也即call main时函数压入的地址),也即esp所指向的返回地址的内容被改变;
2. 还有,返回地址中的内容没有改变,esp所指向的返回地址被改变;
调试原则3:先排除易于验证的分支;
调试原则4:非正常的情况与正常的情况进行比较;
上述的两个调试分支,其中2的验证难度小,因为只需要看看esp的值是否改变,所以我们从分支2入手。采用对比的手法,将刚进入main函数后的ebp和esp记录下来,然后再将main中的ret之前的esp和ebp记录下来。对比两者的值是否相同,理论上而言,两者的值应该相同。以下是我们记录的数据,在刚进入main函数时,ebp的值为468(省略前面部分数字),而esp的值为43c;以下是我们记录的正常版本和溢出版本在执行main中的ret指令之前的ebp和esp值:
图1 正常的版本
图2 非正常的版本
由此,我们找到问题所在:esp指向的地址被修改了,原来应该指向43c,当此处却指向404;此时如果我们查看下404处的值,可以发现其值正好为0x61616161,这就解释了为什么执行完ret指令后,eip的值变为0x61616161。在执行完overflow后,返回到main函数时,overflow之所以能返回到main就说明其esp的设置是正确的;否则overflow中执行完ret指令后,还不知道跳到哪去了。那么此处只可能在leave指令执行完后,esp的值不正常了。考虑leave指令的等价:
mov esp, ebp
pop ebp
使得esp不正常,也即意味着ebp的值不正常;经过验证,的确发现ebp的值从overflow返回后变得不正常。在进入overflow之前的ebp的值为0x420,但是从overflow返回到main函数后,ebp的值变为400,根据猜测,esp的确是正常的(要不怎么返回到main呢?)。那此处的ebp为什么会不正常?
利用调试原则1:要去看,而不是去想。我们跟入overflow中去查看的话,进入overflow时,ebp的值为438,随后变为418;执行完strcpy函数后,ebp的值恢复为418,esp的值此处
肯定正常(否则不能从strcpy返回到overflow),那我们接下来要看执行完leave后,ebp是否能够418恢复会438了,遗憾的是并没有恢复为438,而是变为400;那么问题只能出在leave指令部分,又因为此处的leave指令等效为:
mov esp, ebp
pop ebp
我们又知道此处的esp恢复的是成功的,那么问题只能出在pop ebp时;pop ebp时出现问题,也只能是418这个栈里的值被修改了,原来应该是438被修改为400。经过验证,的确如此。根据后面的验证,调用strcpy后,将418处的值被修改了,至于怎么会修改这个位置的,那么得深入strcpy了。
结论:
本文希望在调试过程中,尽量展现调试原则的作用。由于在已有工作上的重新审视,难免陷入从后视镜看问题的陷阱,读者大不用深究,关键还是原则的运用。
参考文献:
调试九法:软硬件错误的排查之道,David J. Agans著,赵俐译,人民邮电出版社。