草庐IT

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

一线码农聊技术 2023-03-28 原文

一:背景

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某企业ERP网站系统崩溃分析的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  3. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  4. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  5. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  6. Ruby Readline 在向上箭头上使控制台崩溃 - 2

    当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby​​安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少

  7. ruby - Ping ruby 网站? - 2

    在Ruby中可以使用哪些替代方法来ping一个ip地址?标准库“ping”库的功能似乎非常有限。我对在这里滚动我自己的代码不感兴趣。有没有好的gem?我应该接受它并忍受它吗?(我在Linux上使用Ruby1.8.6编写代码) 最佳答案 net-ping值得一看。它允许TCPping(如标准ruby​​ping),但也允许UDP、HTTP和ICMPping。ICMPping需要root权限,但其他则不需要。 关于ruby-Pingruby网站?,我们在StackOverflow上找到一个类

  8. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  9. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  10. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

随机推荐