破一桩疑案——硬件错误


破一桩疑案——硬件错误
本文插图
0、引言
这次分析的问题, 与熊力书中遇到的一个案例特别类似 , 分析之余 , 发现还是有很多思路以及很多新奇的知识可以记录下来 , 与君分享 。
涉及到以下知识点:
1、硬件断点的使用;2、硬件cpuid指令与dmp中相关信息的查询;3、dmp分析的一般思想方法;4、虚拟内存的VAD树和页表;
1、背景
这次的dmp次数不多 , 目前根据后台聚类之后的结果 , 同类的只有一次 。 本来可以不用操心之 , 但谁让咸吃萝卜淡操心的我就喜欢干这个事呢 。 分析之后 , 确实发现了一个很奇怪的事情 , 安推论这种情况出现的概率是极其小的 , 基本不该发生 , 可偏偏就发生了 , 与我之前在熊力书中所看过的一个案例极其相似——硬件故障 。 敢下这个结论 , 也是极其小心 , 做了很多事情的 。 具体分析 , 且看下文 。
2、分析过程
2.1 step1:看一下异常记录 , 如下 , 为了规避具体哪款软件触发的crash , 这里隐藏掉相关信息 , 包括进程的各个模块名 , 代之以AppModule,AppExe这样的名字;
0:030> .exr -1 ExceptionAddress: 51212dae (VCRUNTIME140!memmove+0x0000004e) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 00000001 Parameter[1]: 04c86000 Attempt to write to address 04c86000简单看下 , 是个常规的访问异常 , 往0x04c86000这个内存地址中写入数据了;但奇怪的是触发问题的模块居然是VCRUNTIME140 , 这个模块可是OS的 , 或者说是IDE自带的库 。 这就奇怪了 , 且先看看这个内存地址是Free状态 , 还是只读状态或者什么其他诡异的情况:
0:030> !address 04c86000 Usage:Free Base Address:04c86000 End Address:04ca0000 Region Size:0001a000 Type:00000000 State:00010000MEM_FREE Protect:00000001PAGE_NOACCESS由上可知 , 是Free状态 , 且保护属性是NOACCESS , 这里多说一句 , 很多做Windows开发的人在这里可能会迷糊 , State和Protect这两个有啥区别 , 微软为啥搞两个?其实对内核熟悉 , 或者多CPU的页机制熟悉的话 , 这个就很好理解了 , 前者是Windows操作系统为虚拟地址空间维护的一个树节点中 , 该虚拟内存的状态属性;后者是Windows内核为每个进程维护的页表中该虚拟内存对应的页表的属性;正常情况下这两个属性是完全一致的或者说是接近的;当然 , 前者是底层无关的 , 换一个架构比如PowerPC , Mips , Arm等等 , 也都是没问题 , OK的;后者则是架构相关的 , 虽然其他的架构也有类似于x86的设计 , 但底层的实现逻辑肯定是有区别的;好了 , 暂且打住 , 回归正题;那既然这块内存不该写 , 为啥会往里边写?看看栈回溯吧 , 看看是否能够发现点蛛丝马迹;2.2 step2:寻找程序执行的路径——栈回溯
0:030> .ecxr eax=073d56c0 ebx=00000d90 ecx=ffeff6c4 edx=00000d90 esi=074d5ffc edi=04c86000 eip=51212dae esp=09edfbb8 ebp=09edfbdc iopl=0nv up ei pl nz na po cy cs=0023ss=002bds=002bes=002bfs=0053gs=002befl=00010203 VCRUNTIME140!memmove+0x4e: 51212dae f3a4rep movs byte ptr es:[edi],byte ptr [esi] 0:030> kb *** Stack trace for last set context - .thread/.cxr resets it ChildEBP RetAddrArgs to Child 09edfbbc 5470cb95 04b84934 073d4930 00000d90 VCRUNTIME140!memmove+0x4e [d:agent_work2ssrcvctoolscrtvcruntimesrcstringi386MEMCPY.ASM @ 194] 09edfbdc 54751fbd 073d4930 00000d90 073a4058 AppExe+0xcb95 09edfc00 54751ae3 073d4930 000048f4 5487fd84 AppExe+0x51fbd 09edfc38 55d925d5 073d4930 00000d90 d3e7c270 AppExe+0x51ae3 09edfc70 55d922ea d3e7c290 05fbc528 55d92220 AppModule+0x25d5 09edfc90 55d9222a 09edfcd4 5115d5ef 09b9fa44 AppModule+0x22ea 09edfc98 5115d5ef 09b9fa44 d3e7f4e2 00000000 AppModule+0x222a 09edfcd4 75c7344d 05fbc528 09edfd20 77e59802 ucrtbase+0x3d5ef 09edfce0 77e59802 05fbc528 7e0d54e4 00000000 kernel32+0x1344d 09edfd20 77e597d5 5115d5b0 05fbc528 00000000 ntdll+0x39802 09edfd38 00000000 5115d5b0 05fbc528 00000000 ntdll+0x397d5 void *memmove(void *dst, const void *src, size_t n)很清晰 , 代码里在拷贝数据 , 从VCRUNTIME140!memmove这一帧可知 , 代码执行时 , 是想从0x073d4930拷贝数据到0x04b84934 , 拷贝的长度是0x00000d90;根据对汇编中 , 这种拷贝指令的了解 , 一般情况下 , 编译器在生成汇编指令时 , 都是用的ecx/rcx作为的重复计算的次数 , CPU内部每次执行完带有循环结构的指令 , 如loop或者rep之类的前缀时 , 都会检测rcx/ecx是否为0;那我们暂且先按照这个假设来看下出问题的寄存器上下文;ecx居然是0xffeff6c4;这不正常 , 这大概率是异常了 , 即ecx不停的减1 , 减到0时没有停止而是继续减下去了;这不合理啊 。 好了 , 不猜测了 , 我们看下memmove的汇编代码;根据Windbg提供的信息可知 , memmove这个函数的实现在 MEMCPY.ASM这个文件中 , 用EveryThing搜索的了下 , 结果如下:


推荐阅读