对“仅通过崩溃地址找出源代码的出错行”一文的补充与改进
2010-07-20 20:46:12 来源:WEB开发网其中
?Crash1@@YAXXZ PROC NEAR ; Crash1, COMDAT
为Crash1汇编代码的起始行。产生崩溃的代码便在其后的某个位置。接下去的一行为:
; 15 : {
冒号后的"{"表示源文件中的语句,冒号前的"15"表示该语句在源文件中的行数。 这之后显示该语句汇编后的偏移地址,二进制码,汇编代码。如
00000 55 push ebp
其中"0000"表示相对于函数开始地址后的偏移,"55"为编译后的机器代码," push ebp"为汇编代码。从"cod"文件中我们可以看出,一条(c/c++)语句通常需要编译成数条汇编语句 。此外有些汇编语句太长则会分两行显示如:
00018 c7 45 fc 64 00
00 00 mov DWORD PTR _p$[ebp], 100 ; 00000064H
其中"0018"表示相对偏移,在debug版本中,这个数据为相对于函数起始地址的偏移(此时每个函数第一条语句相对偏移为0000);release版本中为相对于代码段第一条语句的偏移(即代码段第一条语句相对偏移为0000,而以后的每个函数第一条语句相对偏移就不为0000了)。"c7 45 fc 64 00 00 00 "为编译后的机器代码 ,"mov DWORD PTR _p$[ebp], 100"为汇编代码, 汇编语言中";"后的内容为注释,所以";00000064H",是个注释这里用来说明100转换成16进制时为"00000064H"。
接下去,我们开始来定位产生崩溃的语句。
第一步,计算崩溃地址相对于崩溃函数的偏移,在本例中已经知道了崩溃语句的地址(0x00401082),和对应函数的起始地址(0x00401060),所以崩溃地址相对函数起始地址的偏移就很容易计算了:
崩溃偏移地址 = 崩溃语句地址 - 崩溃函数的起始地址 = 0x00401082 - 0x00401060 = 0x22。
第二步,计算出错的汇编语句在cod文件中的相对偏移。我们可以看到函数Crash1()在cod文件中的相对偏移地址为0000,则
崩溃语句在cod文件中的相对偏移 = 崩溃函数在cod文件中相对偏移 + 崩溃偏移地址 = 0x0000 + 0x22 = 0x22
第三步,我们看Crash1函数偏移0x22除的代码是什么?结果如下
00022 c6 00 64 mov BYTE PTR [eax], 100 ; 00000064H
这句汇编语句表示将100这个数保存到寄存器eax所指的内存单元中去,保存空间大小为1个字节(byte)。程序正是执行这条命令时产生了崩溃,显然这里eax中的为一个非法地址 ,所以程序崩溃了!
第四步,再查看该汇编语句在其前面几行的其对应的源代码,结果如下:
; 17 : *p=100;
其中17表示该语句位于源文件中第17行,而“*p=100;”这正是源文件中产生崩溃的语句。
至此我们仅从崩溃地址就查找出了造成崩溃的源代码语句和该语句所在源文件中的确切位置,甚至查找到了造成崩溃的编译后的确切汇编代码!
怎么样,是不是感觉更爽啊?
五、小节
1、新方法同样要注意可以适用的范围,即程序由一条语句当即引起的崩溃。另外我不知道除了VC6外,是否还有其他的编译器能够产生类似的"cod"文件。
2、我们可以通过比较 新方法产生的debug和releae版本的"cod"文件,查找那些仅release版本(或debug版本)有另一个版本没有的bug(或其他性状)。例如"罗文"中所举的那个用例 ,只要打开release版本的"cod"文件,就明白了为啥debug版本会产生崩溃而release版本却没有:原来release版本中产生崩溃的语句其实根本都没有编译 。同样本例中的release版本要看到崩溃的效果,需要将编译选项改为为不优化的配置。
本文配套源码
更多精彩
赞助商链接