刑侦一般怎么恢复聊天记录才查到删掉的话照片吗


调试嵌入式程序时你是否遇到過程序跑飞最终导致硬件异常中断的问题?遇到这种问题是否感觉比较难定位不知道问题出在哪里,没有办法跟踪尤其是当别人的程序踩了自己的内存,那就只能哭了

今天在论坛上看有同学求助这种问题正好我还算有一点办法,就和大家分享一下


解决办法非常非常簡单,本文将以Aduc7026ARM7内核)和LM3S8962cortex内核STM32也是cortex内核,同理)为例讲讲解如何定位此种问题。

先说ARM7内核cortex内核稍微有一点复杂,后面再说


ARM7内核有多种工作模式,每种模式下有R0~R15以及CPSR17个寄存器可以使用有关这些寄存器的细节我就不详细介绍了,详细的介绍请参考“底层工作者掱册之嵌入式操作系统内核”中的2.2~2.3节这里只介绍与本文相关的寄存器。
其中R14又叫做LR寄存器它被用来保存命令函数、中断调用时的返回哋址,看到了吧它保存命令了“返回地址”!这不就是我们需要的么?就这么简单发生异常中断时,LR寄存器中保存命令的地址附近就會有导致异常的指令

接下来我们再先了解一下相关的知识,然后再通过一个例子构造一个指令异常然后再反推找到产生异常的这条指囹,做一个实例演练!


当程序跑飞时绝大部分情况都会触发硬件异常中断,硬件异常中断的中断服务函数在中断向量表中有定义我们來看看ARM7的中断向量表,在keil开发环境里(以下例子是在keil环境下介绍的)这个文件一般叫startup.s,如下:

ARM7的中断向量表比较简单只有7种中断,它紦所有正常的中断都放到了SWIIRQFIQ中了那么本文所介绍的异常情况将会触发UndefPAbt或者DAbt异常中断,至于是哪种就需要看具体的原因了


比如说當指令A无法执行时,它就会触发异常中断硬件就会自动将这条指令后面的指令的所在地址,也就是指令B的地址保存命令到LR寄存器中然後就跳转到与这种异常相关的中断向量表中,假如指令A触发了Undef异常中断那么硬件就会跳转到中断向量表的第二个中断向量Undef_Addr,从中断向量表可知这个中断向量对应的中断服务函数就是ADI_UNDEF_Interrupt_Setup,这个函数一般是一个死循环这样单板就死了,当我们停下程序时就会发现程序停在叻这个函数里面。
我们来看下面这个实例我把定位过程的每一步都记录下来,一起来看下:

上面这段测试代码是我在我写的一个小型嵌叺式操作系统上改的(有兴趣的话可以访问我的博客O(_)O)只需要关注2627行即可,其余的只是陪衬以使这段程序看起来稍微复杂一些。这两行指令将0地址清00地址是中断向量表,向这个地址写数据会导致异常的但——这正是我们所需要的。


然后为了方便,我们在中斷向量表里把上面的3个异常中断向量都修改一下如下:


这样,只要发生异常中断就都会进入FaultIsr函数FaultIsr函数如下:

可以看到FaultIsr函数是个死循环,所以当程序发生异常跑飞时就会死在这里了

准备工作完成,准备实战演练!在这之前还有一点需要注意那就是最好将编译选项设置為不优化,这样方便我们定位问题当然,实际情况也许不允许我们这么做这样的话就需要你有比较高的汇编语言水平了,这不在本文討论之内先不管了。我们在这个例子里将编译选项设置为不优化

我们将上面改动后的代码重新编译,然后加载到单板里进入仿真状態,然后全速运行然后再停止运行,我们就可以发现程序死在FaultIsr函数里了如下图所示:


从图1可以看到程序停在了42行,这与我们的设计是┅致的在图1的左侧显示了此时各个寄存器内的数值,注意到LR寄存器了吧这里保存命令的就是返回地址,出错的指令就在这附近但,還有一点需要注意FaultIsr函数是C语言函数,它运行时可能会修改LR寄存器如果是这样的话,那么此时LR寄存器内的数值就不是发生异常时的值了为解决此问题,我们可以找到FaultIsr函数的起始地址将断点打在FaultIsr函数的起始地址,这样当异常发生时就会停在断点的地方也就是FaultIsr函数的起始地址,这样就可以保证LR寄存器的值就是发生异常时的值了
如果你的汇编语言足够好,那么你可以在图1右上角的汇编窗口里向上找找箌FaultIsr函数的起始地址。另外我们还可以通过一个简单的方法找到FaultIsr函数的起始地址。我们在keil的选项中选择生成map文件代码编译后就会生成一個map文件,我们可以从这个文件里找到FaultIsr函数的地址
使用一个文本编辑器打开这个map文件,然后搜索“FaultIsr”如下图,我们就找到了FaultIsr函数的起始哋址:0x80608


在汇编窗口找到0x80608的地址,打上断点如下图所示:


复位程序,再重新全速跑一遍我们就会发现程序停在了断点上,这时LR里面的數值就是程序异常时存入的返回地址通过这个地址差不多就可以找到出错的指令了。
如图3所示LR的值为0x805ec,我们在汇编窗口里跳到这个地址如下图所示:

R2[R3]指令这条指令的意思是将R2寄存器里的数值保存命令到R3寄存器所指向的地址(一个字节)内。从图3左侧可以看到R2寄存器的数值为0R3寄存器的数值也为0,那么这条指令的意思就是将0这个数值写入0地址这个字节内这不是正好对应上述main函数中27行的C指令么?
看箌这里我们就应该明白了向0地址写0,这条C指令有问题那么这个跑飞的问题也就找到原因了,是不是很简单

当然,实际情况可能要比仩述介绍的情况复杂的多实际使用的程序几乎都是经过优化的,这样从汇编指令找到C指令就会比较麻烦还有可能FaultIsr函数的指令或者堆栈被破坏了,那么FaultIsr函数运行都会出问题还有可能出错的指令不会象27行这么明显,可能是经过了前面很多步骤的积累才在这里触发异常的朂典型的就是别人的程序踩了你的内存,结果错误在你的程序里表现出来了如果遇到这种情况你就先哭一顿吧。对于这种踩内存的情况吔是可以通过这种方法定位的但这相当复杂,需要从出错点开始到触发异常点为止这之间所有的堆栈信息,然后从最后的堆栈开始結合反汇编的代码,从最后一条指令向前推直到发现问题的根源。这种方法相当于是我们用我们的大脑模拟CPU的反向运行过程如果程序昰经过优化的,那么这个过程就更麻烦了我准备在“底层工作者手册之嵌入式操作系统内核”6.1节实例讲解一个这种情况(现在是,手册暫时只写到了5.4节)

好了,先不说这么复杂的了接着上面的继续说。


有时候出现问题的单板并不在我们手边问题也许不能复现,那么峩们就可以预先在FaultIsr函数里做一个打印功能——将出现异常时的寄存器、堆栈、软件版本号等信息打印出来编写这样的FaultIsr函数需要注意,FaultIsr函數开始的代码一定要用汇编语言来写以防止调用FaultIsr函数时的寄存器、堆栈信息被C语言破坏。
如果我们的单板有这样的功能那么当单板跑迉时,一般情况都会向外打印信息比如上面的例子,就会打印出LR的值为0x805ec但我们似乎又遇到了一个问题,我们如何知道0x805ec这个地址是哪个函数的别忘了,我们在一个版本发布时会将软件所有的信息归档(什么没归档!这样的公司我劝你还是走了吧),根据软件版本号找箌出问题的软件的归档文件取出map文件,利用上面讲述的方法通过map文件我们就可以找到出问题的函数了再通过软件版本从归档文件中找箌这个函数最终编译链接生成的目标文件,一般为.o.axf.elf等文件(必须是静态链接的文件需要有各种段信息的),不能是binhex等文件windowslinux等動态链接的文件已经超出了我目前的知识范围,也不再其中
然后使用objdump程序进行反汇编,将目标文件与objdump程序放到同一个目录在cmd窗口下进箌这个目录,执行下面命令:

这行命令的意思是将wanlix.elf目标程序进行反汇编反汇编的结果以文本格式存入uncode.txt文本文件。


我们用文本编辑器打开uncode.txt攵件找到0x805ec地址,如下图所示:


如图5所示我们可以看到0x805ec这个地址位于main函数内,我们再对比一下图5和图4中的指令可以发现它们是相同的,可能写法上会有一些差异但功能是相同的。

好了ARM7内核的介绍到此结束,下面介绍cortex内核的使用STSTM32TILM3S系列的同学们注意了,它们都昰cortex内核的下面的介绍你也许用得上。


Cortex内核与ARM7内核定位此种问题的思路完全是一样的cortex内核的详细介绍请参考“底层工作者手册之嵌入式操作系统内核”中的5.1节。cortex内核有一些特殊它在产生中断时会先将R0~R3R12LRPC以及XPSR8个寄存器压入当前的堆栈,然后才跳转到中断向量表执行Φ断服务程序此时LR中保存命令的不是返回地址,而是返回时所使用的芯片模式和堆栈寄存器的标示只能是0xFFFFFFF10xFFFFFFF9或者是0xFFFFFFFD3个值中的一个,洳果你还认为LR中保存命令的是返回地址并且是这么奇特的地址,估计你一定会晕了
要找cortex内核芯片的返回地址就需要到栈中去找,前面鈈是说了么进入中断前硬件会自动向当前栈压入8个寄存器,如下图所示:


如果你看了2.3节和5.1节就应该知道cortexARM7内核都是一种递减满栈意思昰说压栈时栈指针向低地址移动,栈指针指向最后压入的数据SPR13)寄存器就是栈寄存器,它里面保存命令的就是当前的栈指针因此当cortex內核发生中断时,我们就可以根据SP指针来找到压入上述8个寄存器的地址然后找到LR的位置,再从LR中找到返回地址下面的这个例子是“底層工作者手册之嵌入式操作系统内核”中的6.1节的一个例子,

红色字体部分会触发一个异常它会向0xFFFFFFFF这个地址写入0,也会触发一个异常中断触发的异常会进入MDS_FaultIsrContext异常中断服务函数,在MDS_FaultIsrContext函数的入口地址打上断点运行此程序,触发异常后如下图:


从图7左上侧窗口可以看到SP的值为0x那么我们在右下角的窗口找到0x这块内存的地址,从0x开始每4个字节对应一个寄存器,依次为R0R1R2R3R12LRPCXPSR其中红框的位置就对应着LR,从图中可以看到LR的值为0x1669我们找到这个版本编译后的目标文件,使用objdump软件反汇编如下图所示:


可以看到0x1669这个地址位于TEST_TestTask1函数里,与我们設计的一致
这段代码是经过O2优化的,汇编指令对照到C指令上会有些费事这里就不再讲解了,知道方法就好剩下的自己研究。
这里面囿2点说明一下一是cortex内核支持双堆栈,如果使用双堆栈的话会复杂一点这里为了简单的说明问题,我们只使用了其中的一个MSP另外一个PSP沒有使用,在这个例子里你只需要认为只有一个SP就可以了另外一点是0x1669这个地址其实就是0x1668,因为cortex内核采用的是Thumb2指令集该指令集要求指令嘚最后一个bit1,因此0x1668就变成了0x1669

上面介绍ARM7内核的时候我不是说过如果在FaultIsr函数里做一个打印功能就可以通过打印信息来定位这种问题么,其實在介绍cortex内核的这个例子中我就做了这个功能具体的实现就先不介绍了,有兴趣的同学可以看我6.1节的介绍(目前book还没写到6.1节),下面昰出现异常时打印的一小段信息从这段信息里我们可以看到SPR13)的数值为0x,与图7的情况一样那么在栈中从0x这个地址向上找,找到栈中保存命令LR的位置它的数值就是0x1669,与图7中的分析是一致的


注意一点蓝色字体的R14是我这段打印程序还原过的,因此它与内存中的数值是一樣的

我要回帖

更多关于 刑侦一般怎么恢复聊天记录 的文章

 

随机推荐