日期:2014-05-16  浏览次数:20707 次

《coredump问题原理探究》Linux x86版4.1节函数的逆向之序言

在产品的生命周期中,会遇到各种coredump,如果在调试版本出现coredump,定位它是非常简单的事情,因为从栈就可以知道是哪一行代码出现了问题。如:

(gdb) bt
#0  0x4365b569 in vfprintf () from /lib/libc.so.6
#1  0x436629ff in printf () from /lib/libc.so.6
#2  0x080485b9 in main (argc=3, argv=0xbfe21504) at xuzhina_dump_c4_s4.cpp:20

即使是main函数多次调用printf,从上面栈可以是第20行代码出了问题。

 

但,生活并不是这么容易。在产品生命周期中,发布版本的时间应该是最长,在发布版本的时间里出现的coredump或许不多,但都很难重现,也很难定位,且大多数是在客户环境上出现,解决它的优先级就非常高,影响也很大。但在发布版本出现的coredump,栈往往是这样的。

(gdb) bt
#0  0x4365b569 in vfprintf () from /lib/libc.so.6
#1  0x436629ff in printf () from /lib/libc.so.6
#2  0x080485b9 in main ()

如果main函数多次调用printf,究竟是哪个?如果手头上有对应的调试版本,可能会很好。但如果没有,怎么定位?难道只能一个个地试?每修改一个,就要开发人员修改,提供补丁,测试人员测试,一个来回可能就要几天时间。如果main函数调用10次printf,那么可能要花上一两个月的时间,这种研发成本是无法让人忍受的。虽然有些经验丰富的代码高手,会从代码审核中来猜出哪一行。就本人的工作经历所遇到的,也是不尽人意的,特别是非常难重现的场景。

再考虑一种情况,如果从客户环境返回来的并不是一个dump文件,只是把一些栈和寄存器的,那么又如何定位是哪一行代码出现问题?

 

由于C、C++代码的逻辑,即使编译成了可执行文件,它的逻辑仍然保留,完全可以在汇编里体现出来。也就是说,可执行文件的汇编和源代码是有对应关系。但由于源代码和汇编是一对多的关系,一行代码可以编译成几条甚至十几条指令,出现coredump的函数可能只有十几行代码,但对应的汇编指令却有几百行,怎么从coredump的指令来推断出出错的代码行?

 

每种编程语言都有几种结构:顺序结构,条件结构,循环结构。这些结构就构成了代码的骨架。汇编语言也不例外。如果能够把出错函数的汇编指令的骨架快速找出来,把这些骨架逆向成相关的结构语句,然后看coredump的指令位于骨架的哪一部分,就能够很快推断出出错的代码行了。

 

现在开始探究C,C++语言代码结构在汇编里对应的特征。