关键词:
一:背景
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 <PERF> (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 <PERF> (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 14c9d074
14c9d074 "0810"
0:019> da 176D9C64h
176d9c64 "at++"
还原成 C 代码大概就是:
FILE* pFile = fopen("0810", "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 到底表示啥意思,截图如下:
从图中看,原来是路径不存在的错误,应该就是没找到 0810
这个文件。
到这里就基本弄清楚了来龙去脉,应该是朋友的服务器有意或者无意清理了由 satrda
生成的 0810 文件,引发 satrda.dll
找不到文件路径导致的程序崩溃,将这些信息提供给朋友之后,让朋友去找 satrda
官网去了解下详情,毕竟官方才是最清楚的。
三:总结
这次事故是由于 satrda
层面找不到文件路径导致的程序崩溃,据朋友说在 C# 层面没收到这种C++异常,确实当 C# 和 C++ 产生交互时经常会有各种奇怪的问题,我无意删除你的,你无意干扰我的,大家都好自为之吧
记一次.net某医疗住院系统崩溃分析(代码片段)
一:背景1.讲故事最近收到了两起程序崩溃的dump,查了下都是经典的doublefree造成的,蛮有意思,这里就抽一篇出来分享一下经验供后面的学习者避坑吧。二:WinDbg分析1.崩溃点在哪里windbg带了一个自动化分析... 查看详情
记一次.net某工控mes程序崩溃分析(代码片段)
一:背景1.讲故事前几天有位朋友找到我,说他的程序出现了偶发性崩溃,已经抓到了dump文件,Windows事件日志显示的崩溃点在clr.dll中,让我帮忙看下是怎么回事,那到底怎么回事呢?上WinDbg说话。二:W... 查看详情
记一次.net某家装erp系统内存暴涨分析(代码片段)
一:背景1.讲故事前段时间微信上有一位老朋友找到我,说他的程序跑着跑着内存会突然爆高,有时候会下去,有什么会下不去,怀疑是不是某些情况下存在内存泄露,让我帮忙分析一下,其实内存泄露方面的问题还是比较好解... 查看详情
记一次.net某医疗器械程序崩溃分析(代码片段)
一:背景1.讲故事前段时间有位朋友在微信上找到我,说他的程序偶发性崩溃,让我帮忙看下怎么回事,上面给的压力比较大,对于这种偶发性崩溃,比较好的办法就是利用AEDebug在程序崩溃的时候自动抽一... 查看详情
记一次.net某家装erp系统内存暴涨分析
一:背景1.讲故事前段时间微信上有一位老朋友找到我,说他的程序跑着跑着内存会突然爆高,有时候会下去,有什么会下不去,怀疑是不是某些情况下存在内存泄露,让我帮忙分析一下,其实内存泄露方面的问题还是比较好解... 查看详情
记一次某制造业erp系统cpu打爆事故分析(代码片段)
一:背景1.讲故事前些天有位朋友微信找到我,说他的程序出现了CPU阶段性爆高,过了一会就下去了,咨询下这个爆高阶段程序内部到底发生了什么?画个图大概是下面这样,你懂的。按经验来说,这... 查看详情
记一次.net某外贸erp内存暴涨分析(代码片段)
一:背景1.讲故事上周有位朋友找到我,说他的API被多次调用后出现了内存暴涨,让我帮忙看下是怎么回事?看样子是有些担心,但也不是特别担心,那既然找到我,就给他分析一下吧。二:WinDbg分析1.到底是哪里的泄露这也是... 查看详情
记一次.net某家装erp内存暴涨分析(代码片段)
一:背景1.讲故事前段时间微信上有一位老朋友找到我,说他的程序跑着跑着内存会突然爆高,有时候会下去,有什么会下不去,怀疑是不是某些情况下存在内存泄露,让我帮忙分析一下,其实内存泄... 查看详情
记一次.net某金融企业wpf程序卡死分析(代码片段)
一:背景1.讲故事前段时间遇到了一个难度比较高的dump,经过几个小时的探索,终于给找出来了,在这里做一下整理,希望对大家有所帮助,对自己也是一个总结,好了,老规矩,上WinDBG说话。... 查看详情
记一次.net某供应链web网站cpu爆高事故分析(代码片段)
一:背景1.讲故事年前有位朋友加微信求助,说他的程序出现了偶发性CPU爆高,寻求如何解决,截图如下:我建议朋友用procdump在cpu高的时候连抓两个dump,这样分析起来比较稳健,朋友也如期的成功抓到,接下来就用windbg一起来... 查看详情
记一次.net某智能服装智造系统内存泄漏分析(代码片段)
一:背景1.讲故事上个月有位朋友找到我,说他的程序出现了内存泄漏,不知道如何进一步分析,截图如下:朋友这段话已经说的非常言简意赅了,那就上windbg说话吧。二:Windbg分析1.到底是哪一方面的泄漏根据朋友描述,程序... 查看详情
记一次.net某新能源系统线程疯涨分析(代码片段)
一:背景1.讲故事前段时间收到一个朋友的求助,说他的程序线程数疯涨,寻求如何解决。等我分析完之后,我觉得这个问题很有代表性,所以拿出来和大家分享下,还是上老工具WinDbg。二:WinDbg分析1.... 查看详情
记一次.net某电厂web系统内存泄漏分析
一:背景1.讲故事前段时间有位朋友找到我,说他的程序内存占用比较大,寻求如何解决,截图就不发了,分析下来我感觉除了程序本身的问题之外,.NET5在内存管理方面做的也不够好,所以有必要给大家分享一下。二:WinDbg分... 查看详情
记一次.net某新能源系统线程疯涨分析
一:背景1.讲故事前段时间收到一个朋友的求助,说他的程序线程数疯涨,寻求如何解决。等我分析完之后,我觉得这个问题很有代表性,所以拿出来和大家分享下,还是上老工具WinDbg。二:WinDbg分析1.线程真的在疯涨吗要想查... 查看详情
记一次.net某工控自动化控制系统卡死分析(代码片段)
一:背景1.讲故事前段时间遇到了好几起关于窗体程序的进程加载锁引发的程序卡死和线程暴涨问题,这种dump分析难度较大,主要涉及到Windows操作系统和C++的基础知识,所以有必要简单整理和大家分享一下&... 查看详情
记一次.net某纺织工厂mes系统api挂死分析(代码片段)
一:背景1.讲故事这个月中旬,有位朋友加我wx求助他的程序线程占有率很高,寻求如何解决,截图如下:说实话,和不同行业的程序员聊天还是蛮有意思的,广交朋友,也能扩大自己的圈子,... 查看详情
记一次.net某wms仓储打单系统内存暴涨分析(代码片段)
一:背景1.讲故事七月中旬有一位朋友加wx求助,他的程序在生产上跑着跑着内存就飙起来了,貌似没有回头的趋势,询问如何解决,截图如下:和这位朋友聊下来,感觉像是自己在小县城当了个小老板... 查看详情
记一次腾讯会议的意外崩溃分析(代码片段)
一:背景1.讲故事前段时间在用腾讯会议直播的时候,居然意外崩溃了,还好不是在训练营上课,不然又得重录了,崩完之后发现腾讯会议的bugreport组件会自动生成一个minidump,截图如下:作为一个.NET高级调试的技术博主,非.NET... 查看详情