经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
记一次 .NET某股票交易软件 灵异崩溃分析
来源:cnblogs  作者:一线码农  时间:2023/12/29 9:19:29  对本文有异议

一:背景

1. 讲故事

在dump分析的旅程中也会碰到一些让我无法解释的灵异现象,追过这个系列的朋友应该知道,上一篇我聊过 宇宙射线 导致的程序崩溃,后来我又发现了一例,而这一例恰恰是高铁的 列控连锁一体化 程序,所以更加让我确定这是由于 电离辐射 干扰了计算机的 数字信号 导致程序的bit翻转,而这一篇也是一个我认为的 灵异现象,拿出来给朋友们分享一下。

前段时间有位朋友找到我,说他的程序会偶发性崩溃,一直找不到原因很纠结,看我在这一块非常有经验让我帮忙看一下怎么回事,既然是有备而来自然dump也准备好了,接下来开始分析之旅吧。

二:WinDbg 分析

1. 为什么会崩溃

要想分析崩溃的原因还得windbg自带的自动化分析命令 !analyze -v ,输出如下:

  1. 0:117> !analyze -v
  2. *******************************************************************************
  3. * *
  4. * Exception Analysis *
  5. * *
  6. *******************************************************************************
  7. CONTEXT: (.ecxr)
  8. rax=0000000000000001 rbx=0000000000000000 rcx=0000000000000002
  9. rdx=000000000005001b rsi=000000000000000e rdi=00000161b1b8c718
  10. rip=00007ffdd0961abd rsp=000000341547b370 rbp=000000341547b250
  11. r8=0000000000000005 r9=000000000000003d r10=0000000000000000
  12. r11=7007f0b8d350316a r12=0000000000000000 r13=0000000000000003
  13. r14=000000341547b5c0 r15=0000000000000001
  14. iopl=0 nv up ei pl nz na pe nc
  15. cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
  16. clr!_report_gsfailure+0x1d:
  17. 00007ffd`d0961abd cd29 int 29h
  18. Resetting default scope
  19. EXCEPTION_RECORD: (.exr -1)
  20. ExceptionAddress: 00007ffdd0961abd (clr!_report_gsfailure+0x000000000000001d)
  21. ExceptionCode: c0000409 (Security check failure or stack buffer overrun)
  22. ExceptionFlags: 00000001
  23. NumberParameters: 1
  24. Parameter[0]: 0000000000000002
  25. Subcode: 0x2 FAST_FAIL_STACK_COOKIE_CHECK_FAILURE
  26. SYMBOL_NAME: clr!_report_gsfailure+1d
  27. ...

卦中有一句话叫 Security check failure or stack buffer overrun,浅层意思就是: 安全检查失败或缓冲区溢出,行话就是:栈上的cookie遭到了破坏。

可能有些朋友对 cookie 不是很了解,这个cookie非web的cookie,而是在方法栈上藏的一个随时值,在方法的退出前会检查这个值有没有被破坏,目的就是防止有人无意或者恶意攻击线程栈,如果遭到破坏,会触发 int 29nt!KiRaiseSecurityCheckFailure 函数让程序快速硬性崩溃。

如果有些朋友不明白,画个图如下:

既然说 cookie 被破坏了,说明有栈溢出的情况,那到底溢出了什么东西呢?这需要分析崩溃处附近的汇编代码才能知道,接下来使用 .ecxr ; k 3 切到崩溃前的上下文。

  1. 0:117> .ecxr ; k 3
  2. rax=0000000000000001 rbx=0000000000000000 rcx=0000000000000002
  3. rdx=000000000005001b rsi=000000000000000e rdi=00000161b1b8c718
  4. rip=00007ffdd0961abd rsp=000000341547b370 rbp=000000341547b250
  5. r8=0000000000000005 r9=000000000000003d r10=0000000000000000
  6. r11=7007f0b8d350316a r12=0000000000000000 r13=0000000000000003
  7. r14=000000341547b5c0 r15=0000000000000001
  8. iopl=0 nv up ei pl nz na pe nc
  9. cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
  10. clr!_report_gsfailure+0x1d:
  11. 00007ffd`d0961abd cd29 int 29h
  12. # Child-SP RetAddr Call Site
  13. 00 00000034`1547b370 00007ffd`d0977900 clr!_report_gsfailure+0x1d
  14. 01 00000034`1547b3b0 00007ffd`d097816d clr!RtlAllocateLUnicodeString+0xe0
  15. 02 00000034`1547b420 00007ffd`d09e1d06 clr!RtlDuplicateLUnicodeString+0x8d
  16. ...

卦中的信息很丰富,说 clr 在 RtlAllocateLUnicodeString 函数退出阶段时检查 cookie 被破坏了,继而程序快速崩溃,接下来需要反编译 RtlAllocateLUnicodeString 函数,简化后如下:

  1. 0:117> uf clr!RtlAllocateLUnicodeString
  2. clr!RtlAllocateLUnicodeString:
  3. 00007ffd`d0977820 48895c2418 mov qword ptr [rsp+18h],rbx
  4. 00007ffd`d0977825 55 push rbp
  5. 00007ffd`d0977826 56 push rsi
  6. 00007ffd`d0977827 57 push rdi
  7. 00007ffd`d0977828 488bec mov rbp,rsp
  8. 00007ffd`d097782b 4883ec50 sub rsp,50h
  9. 00007ffd`d097782f 488b05d2777600 mov rax,qword ptr [clr!_security_cookie (00007ffd`d10df008)]
  10. 00007ffd`d0977836 4833c4 xor rax,rsp
  11. 00007ffd`d0977839 488945f8 mov qword ptr [rbp-8],rax
  12. 00007ffd`d097783d 488bfa mov rdi,rdx
  13. 00007ffd`d0977840 488bf1 mov rsi,rcx
  14. 00007ffd`d0977843 c745f0e50000c0 mov dword ptr [rbp-10h],0C00000E5h
  15. 00007ffd`d097784a 33db xor ebx,ebx
  16. 00007ffd`d097784c 4885d2 test rdx,rdx
  17. 00007ffd`d097784f 745f je clr!RtlAllocateLUnicodeString+0x90 (00007ffd`d09778b0) Branch
  18. ...
  19. 00007ffd`d09778f2 8bc3 mov eax,ebx
  20. 00007ffd`d09778f4 488b4df8 mov rcx,qword ptr [rbp-8]
  21. 00007ffd`d09778f8 4833cc xor rcx,rsp
  22. 00007ffd`d09778fb e820a1feff call clr!_security_check_cookie (00007ffd`d0961a20)
  23. 00007ffd`d0977900 488b9c2480000000 mov rbx,qword ptr [rsp+80h]
  24. 00007ffd`d0977908 4883c450 add rsp,50h
  25. 00007ffd`d097790c 5f pop rdi
  26. 00007ffd`d097790d 5e pop rsi
  27. 00007ffd`d097790e 5d pop rbp
  28. 00007ffd`d097790f c3 ret

卦中的信息量还是非常大的,我们通读下汇编代码理解下 安全检查 中的一些基本元素以及逻辑是什么? 步骤大概如下:

  1. _security_cookie

这个是 cookie 种子,可以用 dp 给捞出来,即下面的 0000d9998c879750

  1. 0:117> dp clr!_security_cookie L1
  2. 00007ffd`d10df008 0000d999`8c879750
  1. xor rax,rsp

将 cookie 种子和当前方法的栈顶指针rsp异或一下,目的就是做一个和栈帧相关的随机值,当前的rsp即k上的000000341547b3b0 ,用 windbg 计算之后为:

  1. 0:117> ? 00000034`1547b3b0 ^ 0000d999`8c879750
  2. Evaluate expression: 239339632076000 = 0000d9ad`99c024e0
  1. qword ptr [rbp-8],rax

将异或后的 安全值 塞到 rbp-8 的栈位置,这里的 rbp 由上面的汇编语句 mov rbp,rsp 赋值的,因为上面有三个push加一个call,所以rbp应该退掉4个0x8,最后计算的结果为栈位置000000341547b3f8 存的就是安全值,下面的输出也可以确认。

  1. 0:117> ? 00000034`1547b420-0x8-0x8-0x8-0x8
  2. Evaluate expression: 223695320064 = 00000034`1547b400
  3. 0:117> dp 00000034`1547b400-8 L1
  4. 00000034`1547b3f8 0000d9ad`99c024e0
  1. clr!_security_check_cookie

在方法退出时需要通过 _security_check_cookie 方法来检查cookie是否损坏,核心代码为:

  1. clr!RtlAllocateLUnicodeString+0xd2:
  2. 00007ffd`d09778f4 488b4df8 mov rcx,qword ptr [rbp-8]
  3. 00007ffd`d09778f8 4833cc xor rcx,rsp
  4. 00007ffd`d09778fb e820a1feff call clr!_security_check_cookie (00007ffd`d0961a20)

经过 windbg 计算 rcx=0000d9998c879750 ,即 _security_cookie 值。

  1. 0:117> dp 00000034`1547b400-8 L1
  2. 00000034`1547b3f8 0000d9ad`99c024e0
  3. 0:117> ? 0000d9ad`99c024e0 ^ 00000034`1547b3b0
  4. Evaluate expression: 239253510920016 = 0000d999`8c879750

接下来拿着 rcx= 0000d9998c879750 去反汇编下 _security_check_cookie 函数,简化后如下:

  1. 0:117> uf clr!_security_check_cookie
  2. 00007ffd`d0961a20 483b0de1d57700 cmp rcx,qword ptr [clr!_security_cookie (00007ffd`d10df008)]
  3. 00007ffd`d0961a27 7510 jne clr!_security_check_cookie+0x19 (00007ffd`d0961a39)
  4. 00007ffd`d0961a29 48c1c110 rol rcx,10h
  5. 00007ffd`d0961a2d 66f7c1ffff test cx,0FFFFh
  6. 00007ffd`d0961a32 7501 jne clr!_security_check_cookie+0x15 (00007ffd`d0961a35)
  7. 00007ffd`d0961a34 c3 ret
  8. 00007ffd`d0961a35 48c1c910 ror rcx,10h
  9. 00007ffd`d0961a39 e962000000 jmp clr!_report_gsfailure (00007ffd`d0961aa0)
  10. 00007ffd`d0961aa0 48894c2408 mov qword ptr [rsp+8],rcx
  11. 00007ffd`d0961aa5 4883ec38 sub rsp,38h
  12. 00007ffd`d0961aa9 b917000000 mov ecx,17h
  13. 00007ffd`d0961aae ff15e4fa5a00 call qword ptr [clr!_imp_IsProcessorFeaturePresent (00007ffd`d0f11598)]
  14. 00007ffd`d0961ab4 85c0 test eax,eax
  15. 00007ffd`d0961ab6 7407 je clr!_report_gsfailure+0x1f (00007ffd`d0961abf)
  16. 00007ffd`d0961ab8 b902000000 mov ecx,2
  17. 00007ffd`d0961abd cd29 int 29h

代码逻辑非常简单,还原成 C 大概如下:

  1. void __fastcall _security_check_cookie(uintptr_t stackcookie)
  2. {
  3. if ((stackcookie == __security_cookie) && (stackcookie高四位 == "0000")) {
  4. return;
  5. }
  6. else {
  7. _report_gsfailure()
  8. }
  9. }

从C的逻辑看我们的 stackcookie=0000d9998c879750 完全满足 if 条件,但不知道为什么会走到这个 else 里面去,无法想象。。。所以定性为 灵异事件!!!

4. 故事后续

把所有的值都推算完了之后,在不可能走到 else 的情况下还是走到了 else,这个真的很让人无语+费解,过了几天找朋友确认的时候,朋友又反馈了一个信息,说电脑上的其他程序也会遇到这种情况,让客户重装操作系统,目前还没遇到问题。

所以我觉得这个问题可能是 操作系统层面 的问题,或者是 硬件层面 的问题,而且程序的异常是在 clr 层面,用户代码是无法干涉的,程序中也没有做 Pinvoke。

三:总结

一个是辐射导致的bit位翻转,一个是不可能走到else的地方走了else,各个奇奇怪怪的事情,让我的高级调试之旅丰富多彩,大家觉得这个崩溃还有其他的可能性吗?期待大家的留言。

图片名称

原文链接:https://www.cnblogs.com/huangxincheng/p/17932438.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号