每日视讯:记一次 .NET 某企业采购平台 崩溃分析
时间:2023-06-27 02:28:36来源:一线码农聊技术
一:背景1. 讲故事

前段时间有个朋友找到我,说他们的程序有偶发崩溃的情况,让我帮忙看下怎么回事,针对这种 crash 的程序,用 AEDebug 的方式抓取一个便知,有了 dump 之后接下来就可以分析了。

二:Windbg 分析1. 为什么会崩溃

既然是程序的崩溃,我们可以像看蓝屏一下看dump文件,使用!analyze -v命令即可。


(资料图片)

0:000> !analyze -v********************************************************************************                                                                             **                        Exception Analysis                                   **                                                                             ********************************************************************************CONTEXT:  (.ecxr)rax=0000000000000000 rbx=0000000000f7ccb0 rcx=00007ffe23af7ab0rdx=00000000013e3b10 rsi=0000000000f7ccb0 rdi=0000000000f7c7c0rip=00007ffe538e7044 rsp=0000000000f7cf60 rbp=0000000000f7d770 r8=0000000000000001  r9=000000f7000006bd r10=0000000000f7d640r11=0000000000f7cf50 r12=0000000000000001 r13=0000000000000001r14=000000005c520126 r15=00000000013e3b10iopl=0         nv up ei pl nz na po nccs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010204clr!ComPreStubWorker+0xf2e54:00007ffe`538e7044 f6403820        test    byte ptr [rax+38h],20h ds:00000000`00000038=??Resetting default scopeEXCEPTION_RECORD:  (.exr -1)ExceptionAddress: 00007ffe538e7044 (clr!ComPreStubWorker+0x00000000000f2e54)   ExceptionCode: c0000005 (Access violation)  ExceptionFlags: 00000001NumberParameters: 2   Parameter[0]: 0000000000000000   Parameter[1]: 0000000000000038Attempt to read from address 0000000000000038PROCESS_NAME:  xxx.exeREAD_ADDRESS:  0000000000000038 ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%p            0x%p                    %sEXCEPTION_CODE_STR:  c0000005EXCEPTION_PARAMETER1:  0000000000000000EXCEPTION_PARAMETER2:  0000000000000038STACK_TEXT:  00000000`00f7cf60 00007ffe`538e7044 clr!ComPreStubWorker+0xf2e5400000000`00f7d590 00007ffe`53712d62 clr!ComCallPreStub+0x6200000000`00f7d660 00007ffe`1de3ba83 wwkrn64+0xba8300000000`00f7d740 00007ffe`638ebc70 ole32!CPrivDragDrop::PrivDragDrop+0x2b000000000`00f7d790 00007ffe`638eb98c ole32!PrivDragDrop+0x19800000000`00f7d830 00007ffe`638a9c1e ole32!CDragOperation::GetDropTarget+0xee00000000`00f7d8b0 00007ffe`638ac239 ole32!CDragOperation::UpdateTarget+0x4cd....

从上面的信息看,这个程序是一个经典的访问违例异常,违例是因为rax=0导致读取了不该读取的地方,接下来我们切到异常上下文看下为什么会是 0 ?

2. eax 为什么会是 0

要想切到异常上下文,先使用.ecxr命令,再使用 ub 反汇编。

0:000> .ecxrrax=0000000000000000 rbx=0000000000f7ccb0 rcx=00007ffe23af7ab0rdx=00000000013e3b10 rsi=0000000000f7ccb0 rdi=0000000000f7c7c0rip=00007ffe538e7044 rsp=0000000000f7cf60 rbp=0000000000f7d770 r8=0000000000000001  r9=000000f7000006bd r10=0000000000f7d640r11=0000000000f7cf50 r12=0000000000000001 r13=0000000000000001r14=000000005c520126 r15=00000000013e3b10iopl=0         nv up ei pl nz na po nccs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010204clr!ComPreStubWorker+0xf2e54:00007ffe`538e7044 f6403820        test    byte ptr [rax+38h],20h ds:00000000`00000038=??0:000> ub 00007ffe`538e7044clr!ComPreStubWorker+0xf2e20:00007ffe`538e7010 0f8591d3f0ff    jne     clr!ComPreStubWorker+0x1b4 (00007ffe`537f43a7)00007ffe`538e7016 488b0e          mov     rcx,qword ptr [rsi]00007ffe`538e7019 488d81b8ffffff  lea     rax,[rcx-48h]00007ffe`538e7020 48898424c0050000 mov     qword ptr [rsp+5C0h],rax00007ffe`538e7028 48898424b8050000 mov     qword ptr [rsp+5B8h],rax00007ffe`538e7030 488b89c0ffffff  mov     rcx,qword ptr [rcx-40h]00007ffe`538e7037 e8fcfcebff      call    clr!MethodTable::GetComCallWrapperTemplate (00007ffe`537a6d38)00007ffe`538e703c 48898424b0050000 mov     qword ptr [rsp+5B0h],rax

从汇编代码看,rax 是clr!MethodTable::GetComCallWrapperTemplate方法的返回值,从方法名字看是一个经典的 COM 和 .NET 互操作,接下来继续用uf反汇编看下这个方法,精简后的代码如下:

0:000> uf clr!MethodTable::GetComCallWrapperTemplate00007ffe`537a6d38 48895c2408      mov     qword ptr [rsp+8],rbx00007ffe`537a6d3d 57              push    rdi...00007ffe`537a6d57 488bcb          mov     rcx,rbx00007ffe`537a6d5a e8c943f7ff      call    clr!MethodTable::GetClass (00007ffe`5371b128)00007ffe`537a6d5f 488b4030        mov     rax,qword ptr [rax+30h]...

再结合 coreclr 源码:

inline ComCallWrapperTemplate *MethodTable::GetComCallWrapperTemplate(){    LIMITED_METHOD_CONTRACT;    return GetClass()->GetComCallWrapperTemplate();}class EEClass // DO NOT CREATE A NEW EEClass USING NEW!{    ComCallWrapperTemplate* m_pccwTemplate;   // points to interop data structures used when this type is exposed to COM    inline ComCallWrapperTemplate *GetComCallWrapperTemplate()    {        LIMITED_METHOD_CONTRACT;        return m_pccwTemplate;    }}

到这里大概能推测到是因为EEClass.m_pccwTemplate字段为 null 所致,从注释看,他是 CLR 用来暴露给 COM 使用的数据结构,那为什么暴露给 COM 使用的数据结构为 NULL 呢? 这个分析起来就复杂了。

但有一点可以确定,像这种逻辑必然是坚如磐石,受过日月精华,经历过500年的风吹雨打,不可能无缘无故的出篓子。

3. 出路在哪里

要寻找突破口还得从调用栈入手,我们用k命令洞察一下。

0:000> k  *** Stack trace for last set context - .thread/.cxr resets it # Child-SP          RetAddr               Call Site00 00000000`00f7cf60 00007ffe`53712d62     clr!ComPreStubWorker+0xf2e5401 00000000`00f7d590 00007ffe`1de3ba83     clr!ComCallPreStub+0x6202 00000000`00f7d660 00007ffe`638ebc70     wwkrn64+0xba8303 00000000`00f7d740 00007ffe`638eb98c     ole32!CPrivDragDrop::PrivDragDrop+0x2b0 [com\ole32\com\rot\getif.cxx @ 659] 04 00000000`00f7d790 00007ffe`638a9c1e     ole32!PrivDragDrop+0x198 [com\ole32\com\rot\getif.cxx @ 920] 05 00000000`00f7d830 00007ffe`638ac239     ole32!CDragOperation::GetDropTarget+0xee [com\ole32\ole232\drag\drag.cpp @ 1128] 06 00000000`00f7d8b0 00007ffe`638ac91c     ole32!CDragOperation::UpdateTarget+0x4cd [com\ole32\ole232\drag\drag.cpp @ 2026] 07 00000000`00f7d9a0 00007ffe`2443f664     ole32!DoDragDrop+0x10c [com\ole32\ole232\drag\drag.cpp @ 3007] 08 00000000`00f7dc80 00007ffe`244ccd8d     System_Windows_Forms_ni+0x9cf664...

仔细观察线程栈信息,不难发现用户是在用DoDragDrop方法实现控件的拖拽,不过在执行流中有一个陌生的动态链接库wwkrn64,它到底是何方神圣呢?我们用 lmvm 观察下。

0:000> lmvm wwkrn64Browse full module liststart             end                 module name00007ffe`1de30000 00007ffe`1df1e000   wwkrn64  C (export symbols)       wwkrn64.dll    Loaded symbol image file: wwkrn64.dll    Image path: D:\xxx\wwall\wwkrn64.dll    Image name: wwkrn64.dll    Browse all global symbols  functions  data    Timestamp:        Wed Apr 26 10:18:26 2023 (644889F2)    CheckSum:         00000000    ImageSize:        000EE000    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4    Information from resource tables:

从输出信息看,果然是一个外来物种,经过网上一顿搜索,发现是一款信息安全软件,哪家公司就模糊了哈,截图如下:

到这里就真相大白了,让朋友把这款软件卸载掉再试试看,问题就解决了。

4. 安全软件为什么要介入

我这里只能简单推测一下,ComCallPreStub和ComPreStubWorker方法是 JIT 在编译某一个方法时的前缀路径,也是很多加壳软件以及永恒之蓝这样的蠕虫病毒重点关注的方法,所以这些高危方法自然也是安全软件重点监视的,如果安全软件没处理好,自然就会误杀。。。

当然真正的原因只能问系铃人。

可能有些朋友要说了,怎么验证这两个方法就是 JIT 编译的前缀,这里我们用 普通方法+windbg 的方法简单验证下吧,参考代码如下:

internal class Program    {        static void Main(string[] args)        {            Debugger.Break();            Test();            Console.ReadLine();        }        static void Test()        {            Console.WriteLine("Test1");        }    }

接下来我们重点观察下Test方法的编译过程,看过程之前先上一张架构图:

从架构图看 Test() 方法的编译最终是由clrjit!jitNativeCode来处理的,要想验证很简单用bp clrjit!jitNativeCode下一个断点即可。

0:000> bp clrjit!jitNativeCode0:000> gBreakpoint 0 hitclrjit!jitNativeCode:00007ffb`590cc040 4c894c2420      mov     qword ptr [rsp+20h],r9 ss:0000009c`5efed218=00000000000000000:000> k # Child-SP          RetAddr               Call Site00 0000009c`5efed1f8 00007ffb`5917d683     clrjit!jitNativeCode [D:\a\_work\1\s\src\coreclr\jit\compiler.cpp @ 6941] 01 0000009c`5efed200 00007ffb`594d3091     clrjit!CILJit::compileMethod+0x83 [D:\a\_work\1\s\src\coreclr\jit\ee_il_dll.cpp @ 279] 02 (Inline Function) --------`--------     coreclr!invokeCompileMethodHelper+0x86 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 12774] 03 (Inline Function) --------`--------     coreclr!invokeCompileMethod+0xc5 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 12839] 04 0000009c`5efed270 00007ffb`594d274d     coreclr!UnsafeJitFunction+0x7f1 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 13355] 05 0000009c`5efed760 00007ffb`594d22ce     coreclr!MethodDesc::JitCompileCodeLocked+0x1f1 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 1051] 06 0000009c`5efed930 00007ffb`59472009     coreclr!MethodDesc::JitCompileCodeLockedEventWrapper+0x466 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 920] 07 0000009c`5efeda90 00007ffb`59473f58     coreclr!MethodDesc::JitCompileCode+0x2a9 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 860] 08 (Inline Function) --------`--------     coreclr!MethodDesc::PrepareILBasedCode+0x5ae [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 439] 09 (Inline Function) --------`--------     coreclr!MethodDesc::PrepareCode+0x5ae [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 332] 0a 0000009c`5efedb40 00007ffb`5947340c     coreclr!CodeVersionManager::PublishVersionableCodeIfNecessary+0x7f8 [D:\a\_work\1\s\src\coreclr\vm\codeversion.cpp @ 1701] 0b 0000009c`5efee070 00007ffb`5947316b     coreclr!MethodDesc::DoPrestub+0x16c [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 2215] 0c 0000009c`5efee190 00007ffb`595abec5     coreclr!PreStubWorker+0x21b [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 2039] 0d 0000009c`5efee320 00007ffa`f9a0296e     coreclr!ThePreStub+0x550e 0000009c`5efee3d0 00007ffb`595aae93     Example_19_1_1!Example_19_1_1.Program.Main+0x2e [D:\skyfly\19.20230624\src\Example\Example_19_1_1\Program.cs @ 10] ...

如果想在jitNativeCode方法中把md提取出来的话,可以取 r9 参数。

0:000> !dumpmd poi(r9)Method Name:          Example_19_1_1.Program.Test()Class:                00007ffaf9abd520MethodTable:          00007ffaf9ac8880mdToken:              0000000006000006Module:               00007ffaf9ac6908IsJitted:             noCurrent CodeAddr:     ffffffffffffffffVersion History:  ILCodeVersion:      0000000000000000  ReJIT ID:           0  IL Addr:            000001f865be20a7     CodeAddr:           0000000000000000  (MinOptJitted)     NativeCodeVersion:  0000000000000000
三:总结

这次崩溃事故的直接原因是由于第三方安全软件的介入导致的,因ComPreStubWorker是加壳程序和蠕虫病毒注入的突破口,不管怎样还是希望安全软件对高危函数ComPreStubWorker的照护逻辑再优化下吧,减少误杀的发生。

标签:

最新
  • 每日视讯:记一次 .NET 某企业采购平台 崩溃分析

    一:背景1 讲故事前段时间有个朋友找到我,说他们的程序有偶发崩溃的情

  • 上海男篮目前还有两个顶薪名额 王哲林的D类合同还剩两年|当前通讯

    上海男篮目前还有两个顶薪名额王哲林的D类合同还剩两年,周琦,王哲林,cb

  • 环球最新:笔记本电脑怎么强制开机?笔记本wifi功能消失了怎么办

    笔记本电脑怎么强制开机通常情况下,笔记本电脑只要按下电源键即可

  • 花生是糖尿病的克星吗?糖尿病到底是什么原因造成的呢?

    花生是糖尿病的克星吗?糖尿病患者适量吃花生可以起到美容效果,但不

  • 浑水摸鱼 抓泥鳅……济南这个高山景区成网红避暑打卡地!

    浑水摸鱼抓泥鳅……济南这个高山景区成网红避暑打卡地!,森林,打卡,沙

  • 看完《上甘岭》新预告!我难掩激动,终于有部像样的国产大剧了|天天滚动

    打得一拳开,免得百拳来。这句话一出来,不用我多说,大家应该知道今天

  • 麦收时节丨小麦主产区大规模机收基本结束 夏播粮食超七成

    据中央广播电视总台中国之声《新闻和报纸摘要》报道,根据农业农村部小

  • 实时:意媒:加拉塔萨雷想签约维奇代替伊卡尔迪,已与经纪人接触

    直播吧6月24日讯据Firenzeviola报道,加拉塔萨雷有意引进约维奇代替伊

  • 乡村追梦人丨刘逸:一步步扮靓家乡,让她变成“诗和远方” 全球独家

    编者按:党的二十大报告明确提出,“加快建设农业强国,扎实推动乡村产

  • 我苏漫评丨双“11”牵手!从苏州坐地铁去上海,长三角一体化再加速_全球最新

    来源|小苏“画”事工作室统筹|周安琪文案|程梦青手绘|姜向慧开发|

  • 环球精选!全球钻石价格较峰值暴跌18%:人造钻石市场规模不断扩大

    据报道,根据最新公布的全球毛坯钻石价格指数,今年迄今,钻石价格已下

  • 7000MB/S读速!致态TiPlus7100 NVMe M.2固态硬盘1TB低至439元_天天讯息

    ZHITAI致态TiPlus7100NVMeM 2固态硬盘1TB当前促销价低至439元,性价比

  • 绿康生化:拟定增募资不超13亿元 用于光伏胶膜等项目_热点聚焦

    绿康生化6月21日晚间公告,公司拟向特定对象发行股票募资不超过13亿元

  • @高校毕业生 办理离校手续时别忘记这一环节→

    日前,教育部发布《关于建立高校毕业生毕业去向登记制度的通知》,要求

  • 李想,在微博“造车”

    尽管李想的理想是跑赢BBA,比如他在最新一周新势力销量公布之后,便在

  • 坚持久久为功,培植青年人才成长沃土

    近期,第四届上海创新创业青年50人论坛在上海中心召开。上海历来是投资

  • 旅游
    • 家常红烧鲳鱼,味道鲜美,简单的做法就有家的味道-天天快看

    • 曲沃县气象台发布大风蓝色预警【Ⅳ级/一般】【2023-06-16】

    • 买不到瑞宁得要断药吗?内附解决办法-当前热议

    • 事关孩子的安全!上海首个,正式成立!