• 记一次 .NET 某企业 ERP网站系统 崩溃分析


    一:背景

    1. 讲故事

    前段时间收到了一个朋友的求助,说他的ERP网站系统会出现偶发性崩溃,找了好久也没找到是什么原因,让我帮忙看下,其实崩溃好说,用 procdump 自动抓一个就好,拿到 dump 之后,接下来就是一顿分析了。

    二:WinDbg 分析

    1. 是什么导致的崩溃

    windbg 有一个自动化的分析命令 !analyze -v 可以帮我们提前预诊一下,就好像进医院先在问询台那里过一下。

    
    0:019> !analyze -v
    CONTEXT:  (.ecxr)
    eax=14c9cd00 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=14c9d664
    eip=682a024a esp=14c9cfd4 ebp=14c9d018 iopl=0         nv up ei pl nz ac po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
    msvcr90!fprintf+0x34:
    682a024a 83c414          add     esp,14h
    Resetting default scope
    
    EXCEPTION_RECORD:  (.exr -1)
    ExceptionAddress: 682a024a (msvcr90!fprintf+0x00000034)
       ExceptionCode: c0000417
      ExceptionFlags: 00000001
    NumberParameters: 0
    
    PROCESS_NAME:  w3wp.exe
    
    ERROR_CODE: (NTSTATUS) 0xc0000417 -    C
    
    EXCEPTION_CODE_STR:  c0000417
    
    STACK_TEXT:  
    14c9d018 1766013b     00000000 176d9c60 17def1a8 msvcr90!fprintf+0x34
    WARNING: Stack unwind information not available. Following frames may be wrong.
    14c9d664 454c5153     75636578 203a6574 5332347b satrda!Writer_Write+0x4bb
    000000c8 75636578     203a6574 5332347b 207d3230 0x454c5153
    000000c8 17673623     17d538e8 17ded730 00000001 crypt32!profapi_NULL_THUNK_DATA_DLA  (crypt32+0x126578)
    00000009 176604b6     14c9d74c 17ded730 17dae9c8 satrda!SATRDA_Proto_UnitTest+0x6c93
    ffffffff 17654012     17dae9c8 17d538e8 17ded730 satrda!Writer_Write+0x836
    17dae9c8 665fe072     14c9d74c 00000001 1765405b satrda!ConfigDSN+0xd0c2
    ...
    160a0000 00000000     00000000 00000000 00000000 0x7071e31
    
    FAULTING_SOURCE_LINE:  f:\dd\vctools\crt_bld\self_x86\crt\src\fprintf.c
    
    FAULTING_SOURCE_FILE:  f:\dd\vctools\crt_bld\self_x86\crt\src\fprintf.c
    
    FAULTING_SOURCE_LINE_NUMBER:  55
    
    FAULTING_SOURCE_CODE:  
    No source found for 'f:\dd\vctools\crt_bld\self_x86\crt\src\fprintf.c'
    
    
    SYMBOL_NAME:  msvcr90!fprintf+34
    
    MODULE_NAME: msvcr90
    
    IMAGE_NAME:  msvcr90.dll
    
    STACK_COMMAND:  ~19s; .ecxr ; kb
    
    FAILURE_BUCKET_ID:  INVALID_CRUNTIME_PARAMETER_c0000417_msvcr90.dll!fprintf
    
    

    从错误信息看,问题是出在 satrda.dll 这个第三方库,赶紧网上搜一下是这是何方神圣。

    看样子是一个连接数据库的商业组件,接下来看下 FAILURE_BUCKET_ID: INVALID_CRUNTIME_PARAMETER_c0000417_msvcr90.dll!fprintf 信息,可以发现因为在调用 fprintf 函数时出现了参数错误,到这里我们将包围圈极大的收缩了。

    2. 为什么会出现参数错误

    熟悉 C 语言 fprintf 函数的朋友都知道,它是用来向 文件 写入数据的,类似 C# 的 WriteFile,既然报了参数异常,那就说明肯定在参数上出了问题,接下来看下它的签名。

    
    int fprintf(
       FILE *stream,
       const char *format [,
       argument ]...
    );
    
    

    有了这些基础之后切到 19 号线程观察下它的调用栈。

    
    0:019> ~19s; .ecxr ; kb 10
    eax=14c9cd00 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=14c9d664
    eip=682a024a esp=14c9cfd4 ebp=14c9d018 iopl=0         nv up ei pl nz ac po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
    msvcr90!fprintf+0x34:
    682a024a 83c414          add     esp,14h
     # ChildEBP RetAddr      Args to Child              
    00 14c9d018 1766013b     00000000 176d9c60 17def1a8 msvcr90!fprintf+0x34 [f:\dd\vctools\crt_bld\self_x86\crt\src\fprintf.c @ 55] 
    WARNING: Stack unwind information not available. Following frames may be wrong.
    01 14c9d664 454c5153     75636578 203a6574 5332347b satrda!Writer_Write+0x4bb
    02 000000c8 75636578     203a6574 5332347b 207d3230 0x454c5153
    03 000000c8 17673623     17d538e8 17ded730 00000001 crypt32!profapi_NULL_THUNK_DATA_DLA  (crypt32+0x126578)
    04 00000009 176604b6     14c9d74c 17ded730 17dae9c8 satrda!SATRDA_Proto_UnitTest+0x6c93
    05 ffffffff 17654012     17dae9c8 17d538e8 17ded730 satrda!Writer_Write+0x836
    06 17dae9c8 665fe072     14c9d74c 00000001 1765405b satrda!ConfigDSN+0xd0c2
    07 17ded730 63207463     2c44492e 6f532e63 632c7472 clr!CompressDebugInfo::CompressBoundariesAndVars+0x2d0
    08 656c6573 2c44492e     6f532e63 632c7472 7261502e 0x63207463
    09 656c6573 6f532e63     632c7472 7261502e 49746e65 0x2c44492e
    0a 656c6573 69482e63     6e656464 4c2e632c 6c657665 Microsoft_Build_Tasks_v4_0_ni+0x2f2e63
    0b 2c687461 6e656464     4c2e632c 6c657665 64646948 System_ServiceModel_Web_ni+0xf2e63
    0c 69482e63 4c2e632c     6c657665 64646948 632c6e65 System_Runtime_Serialization_ni+0x226464
    0d 6e656464 6c657665     64646948 632c6e65 6d6f432e 0x4c2e632c
    0e 6e656464 64646948     632c6e65 6d6f432e 656e6f70 System_ServiceModel_ni+0x537665
    0f 6c657665 632c6e65     6d6f432e 656e6f70 632c746e 0x64646948
    
    

    从线程栈来看 msvcr90!fprintf 函数的第一个参数居然是 00000000 ,也就是说 *stream 这个参数为 NULL,难怪说参数异常!

    3. 为什么 stream 为空

    熟悉 C 的朋友应该知道 *stream 参数是通过 fopen 函数得到的,可能有些朋友有点混,这里就写个简单的模型吧。

    
    int main()
    {
    	FILE* pFile;
    	int n;
    	char name[100];
    
    	pFile = fopen("D:\\dumps\\myfile2.txt", "w");
    
    	gets_s(name, 100);
    
    	fprintf(pFile, "%s", name);
    
    	fclose(pFile);
    
    	return 0;
    }
    
    

    接下来我们到 dump 中寻找一下 fopen 函数,这个在线程栈上是没有了,先提取出 msvcr90!fprintf+0x34 中的 RetAddr=1766013b 返回值地址到汇编窗口查找,截图如下:

    从图中可以看到,esi 是 eax 给的,而 eax 是 call 返回值给的,不出意外 176D727Ch 中存的就是 fopen 函数,输出如下:

    
    0:019> u poi(176D727Ch)
    msvcr90!fopen [f:\dd\vctools\crt_bld\self_x86\crt\src\fopen.c @ 123]:
    682a01a2 8bff            mov     edi,edi
    682a01a4 55              push    ebp
    682a01a5 8bec            mov     ebp,esp
    682a01a7 6a40            push    40h
    682a01a9 ff750c          push    dword ptr [ebp+0Ch]
    682a01ac ff7508          push    dword ptr [ebp+8]
    682a01af e825ffffff      call    msvcr90!_fsopen (682a00d9)
    682a01b4 83c40c          add     esp,0Ch
    
    

    接下来我们需要提取 fopen 中的两个参数,截图如下:

    第二个参数很好获取就是 176D9C60h 的 ascii 表示,第一个参数获取起来就麻烦了,我们需要详细的如图那样推测当时的 esp 指向的位置。

    
    0:019> da poi(14c9d02c+0x5c)
    17def0e0  "log/20220810.txt"
    0:019> da 176D9C64h
    176d9c64  "at++"
    
    

    还原成 C 代码大概就是:

    
    FILE*  pFile = fopen("log/20220810.txt", "at++");
    
    

    代码大概是恢复出来了,那为什么会抛异常呢? windbg 有一个 !gle 命令可以查看当时发生了什么错误。

    
    0:019> !gle
    LastErrorValue: (NTSTATUS) 0 (0) - STATUS_SUCCESS
    LastStatusValue: (NTSTATUS) 0xc000003a - {            }       %hs
    
    

    接下来到微软的官方文档:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 找一下这个 3a 到底表示啥意思,截图如下:

    从图中看,原来是路径不存在的错误,应该就是没找到 20220810.txt 这个文件。

    到这里就基本弄清楚了来龙去脉,应该是朋友的服务器有意或者无意清理了由 satrda 生成的20220810.txt文件,引发 satrda.dll 找不到文件路径导致的程序崩溃,将这些信息提供给朋友之后,让朋友去找 satrda 官网去了解下详情,毕竟官方才是最清楚的。

    三:总结

    这次事故是由于 satrda 层面找不到文件路径导致的程序崩溃,据朋友说在 C# 层面没收到这种C++异常,确实当 C# 和 C++ 产生交互时经常会有各种奇怪的问题,我无意删除你的,你无意干扰我的,大家都好自为之吧😂😂😂

    图片名称
  • 相关阅读:
    leetcode-10-[150]逆波兰表达式求值 [239]滑动窗口最大值[347]前k个高频元素
    Java 虚拟机操作码探秘:常量指令
    AJAX——HttpRequest对象、get/post请求
    无线传感器网络定位综述
    有多条业务线,mysql建多库多表比较好还是一个库多个表比较好呢?
    实现BIO多客户端通讯模式
    汽车电子行业知识:什么是汽车协议栈
    C语言每日一题(20)最大公因数等于 K 的子数组数目
    自动驾驶学习笔记(一)——Apollo平台
    Java入门有多快,看这篇
  • 原文地址:https://www.cnblogs.com/huangxincheng/p/17260332.html