一个DLL未正式卸载却从内存消失的案例,第一部分

The Old New Thing (Raymond Chen) 新闻

摘要

本文调查了一个崩溃转储文件:进程关闭期间,shell32.dll中发生栈溢出,导致重复的异常处理循环。分析追踪了递归异常处理,并确定了涉及DLL卸载的根本原因。

<p>负责shell32.dll的团队收到一个bug报告,称他们在某个第三方程序中导致了大量崩溃。打开崩溃转储后,明显看到了栈溢出的迹象:</p> <pre><span style="border: solid 1px transparent;"> # Child-SP RetAddr Call Site</span> <span style="border: solid 1px transparent;">00 000000ba`92851098 00007ff9`fed521c1 ntdll!_chkstk+0x37</span> <span style="border: solid 1px transparent;">01 000000ba`928510b0 00007ff9`feea5ace ntdll!RtlDispatchException+0x2d1</span> <span style="border: solid 1px transparent;">02 000000ba`92851300 00007ff9`fed4e02d ntdll!KiUserExceptionDispatch+0x2e</span> <span style="border: solid 1px transparent;">03 000000ba`92852060 00007ff9`fed5222f ntdll!RtlLookupFunctionEntry+0x8d</span> <span style="border: solid 1px transparent;">04 000000ba`928520b0 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f</span> <span style="border: solid 1px transparent;">05 000000ba`92852800 00007ff9`fed4e02d ntdll!KiUserExceptionDispatch+0x2e</span> <span style="border: solid 1px transparent;">06 000000ba`92853560 00007ff9`fed5222f ntdll!RtlLookupFunctionEntry+0x8d</span> <span style="border: solid 1px transparent;">07 000000ba`928535b0 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f</span> <span style="border: solid 1px transparent;">08 000000ba`92853d00 00007ff9`fed4e02d ntdll!KiUserExceptionDispatch+0x2e</span> <span style="border: solid 1px currentcolor; border-bottom: none;">09 000000ba`92854a60 00007ff9`fed5222f ntdll!RtlLookupFunctionEntry+0x8d </span> <span style="border: 1px currentcolor; border-style: none solid;">0a 000000ba`92854ab0 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f </span> <span style="border: solid 1px currentcolor; border-top: none;">0b 000000ba`92855200 00007ff9`fed51f29 ntdll!KiUserExceptionDispatch+0x2e</span> <span style="border: solid 1px transparent;">0c 000000ba`92855f70 00007ff9`feea5ace ntdll!RtlLookupFunctionEntry+0x8d</span> <span style="border: solid 1px transparent;">0d 000000ba`928561c0 00007ff9`fed4e02d ntdll!RtlDispatchException+0x33f <span style="border: solid 1px transparent;">...</span> </span></pre> <p>高亮的堆栈帧块(从<code>RtlLookupFunctionEntry</code>到<code>KiUserExceptionDispatch</code>)重复了很长一段时间。</p> <p>很明显,我们陷入了某种递归异常处理的死亡螺旋。发生了一个异常,内核判断它无法在内核模式下处理¹,于是将异常反射回用户模式进行进一步处理(<code>KiUserExceptionDispatch</code>)。在试图找出该调用哪个异常处理程序时(<code>RtlLookupFunctionEntry</code>),我们又触发了异常,从而重启了异常循环。</p> <p>最终,所有这些递归异常耗尽栈空间,我们收到了栈溢出异常,进程终止。</p> <p>这个bug被分配给了shell32,因为看起来shell32是原始异常的来源。如果你一路回溯到栈底,会得到类似这样的信息:</p> <pre>23f 000000ba`9294c620 00007ff9`fed5222f ntdll!RtlLookupFunctionEntry+0x8d 240 000000ba`9294c670 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f 241 000000ba`9294cdc0 00007ff9`fed4e02d ntdll!KiUserExceptionDispatch+0x2e 242 000000ba`9294db20 00007ff9`fed5222f ntdll!RtlLookupFunctionEntry+0x8d 243 000000ba`9294db70 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f 244 000000ba`9294e2c0 00007ff9`fcba0af0 ntdll!KiUserExceptionDispatch+0x2e 245 000000ba`9294f018 00007ff9`fde2ad13 combase!CoTaskMemFree 246 000000ba`9294f020 00007ff9`fc7abc75 shell32!wil::details::string_maker::~string_maker+0x13 247 000000ba`9294f050 00007ff9`fc7ab897 ucrtbase!&lt;lambda_f03950bc5685219e0bcd2087efbe011e&gt;::operator()+0xa5 248 000000ba`9294f0a0 00007ff9`fc7ab84d ucrtbase!__crt_seh_guarded_call&lt;int&gt;::operator()+0x3b 249 000000ba`9294f0d0 00007ff9`fc7d2f0c ucrtbase!execute_onexit_table+0x3d 24a 000000ba`9294f110 00007ff9`fdff4645 ucrtbase!__crt_state_management::wrapped_invoke+0x2c 24b 000000ba`9294f140 00007ff9`fdff476e shell32!dllmain_crt_process_detach+0x45 24c 000000ba`9294f180 00007ff9`fdff476e shell32!dllmain_dispatch+0xe6 24d 000000ba`9294f1e0 00007ff9`fee9f6fe ntdll!LdrpCallInitRoutineInternal+0x22 24e 000000ba`9294f210 00007ff9`fedcd37f ntdll!LdrpCallInitRoutine+0x10e 24f 000000ba`9294f280 00007ff9`fedcc54e ntdll!LdrShutdownProcess+0x17f 250 000000ba`9294f390 00007ff9`fdcb18ab ntdll!RtlExitUserProcess+0x9e 251 000000ba`9294f3c0 00007ff9`e754882e kernel32!ExitProcessImplementation+0xb 252 000000ba`9294f3f0 00007ff9`e754f344 mscoreei!RuntimeDesc::ShutdownAllActiveRuntimes+0x2fa 253 000000ba`9294f6d0 00007ff9`e66f464b mscoreei!CLRRuntimeHostInternalImpl::ShutdownAllRuntimesThenExit+0x14 254 000000ba`9294f700 00007ff9`e66f44c9 clr!EEPolicy::ExitProcessViaShim+0x8b 255 000000ba`9294f760 00007ff9`e66f441e clr!SafeExitProcess+0x9d 256 000000ba`9294f9e0 00007ff9`e66f3f44 clr!HandleExitProcessHelper+0x3e 257 000000ba`9294fa10 00007ff9`e66f3e24 clr!_CorExeMainInternal+0xf8 258 000000ba`9294faa0 00007ff9`e753d6da clr!CorExeMain+0x14 259 000000ba`9294fae0 00007ff9`e75d785b mscoreei!CorExeMain+0xfa 25a 000000ba`9294fb40 00007ff9`fdc9e8d7 mscoree!CorExeMain_Exported+0xb 25b 000000ba`9294fb70 00007ff9`fedcc40c kernel32!BaseThreadInitThunk+0x17 25c 000000ba`9294fba0 00000000`00000000 ntdll!RtlUserThreadStart+0x2c </pre> <p>重复块在第一个异常的源头处停止:<code>combase!CoTaskMemFree</code>。</p> <p>我们可以查找异常记录,看看最初的问题是什么。</p> <p>异常记录和上下文记录很可能被传递给<code>RtlDispatchException</code>,因此我们可以看看<code>KiUserExceptionDispatch</code>传递了什么。</p> <pre> # Child-SP <span style="border: solid 1px transparent;">RetAddr</span> Call Site 243 000000ba`9294db70 <span style="border: solid 1px currentcolor;">00007ff9`feea5ace</span> ntdll!RtlDispatchException+0x33f 244 000000ba`9294e2c0 <span style="border: solid 1px transparent;">00007ff9`fcba0af0</span> ntdll!KiUserExceptionDispatch+0x2e 0:000> u ntdll!KiUserExceptionDispatch 00007ff9`feea5ace ntdll!KiUserExceptionDispatch: 00007ff9`feea5aa0 cld 00007ff9`feea5aa1 mov rax,qword ptr [ntdll!Wow64PrepareForException (00007ff9`fef272f0)] 00007ff9`feea5aa8 test rax,rax 00007ff9`feea5aab je ntdll!KiUserExceptionDispatch+0x1c (00007ff9`feea5abc) 00007ff9`feea5aad mov rcx,rsp 00007ff9`feea5ab0 add rcx,4F0h 00007ff9`feea5ab7 mov rdx,rsp 00007ff9`feea5aba call rax 00007ff9`feea5abc <span style="border: solid 1px currentcolor; border-bottom: none;">mov rcx,rsp </span> 00007ff9`feea5abf <span style="border: 1px currentcolor; border-style: none solid;">add rcx,4F0h</span> 00007ff9`feea5ac6 <span style="border: solid 1px currentcolor; border-top: none;">mov rdx,rsp </span> 00007ff9`feea5ac9 call ntdll!RtlDispatchException (00007ff9`fed51ef0) 00007ff9`feea5ace test al,al </pre> <p>我们看到传递给<code>RtlDispatchException</code>的两个参数分别在<code>rsp+4f0h</code>和<code>rsp</code>处。我猜第一个是异常记录,第二个是上下文记录,因为这是它们在<code>EXCEPTION_POINTERS</code>中出现的顺序。</p> <pre> # <span style="border: solid 1px transparent;">Child-SP</span> RetAddr Call Site 244 <span style="border: solid 1px currentcolor;">000000ba`9294e2c0</span> 00007ff9`fcba0af0 ntdll!KiUserExceptionDispatch+0x2e 00007ff9`feea5ace test al,al 0:000> dps 000000ba`9294e2c0+4f0 000000ba`9294e7b0 00000000`<span style="border: solid 1px currentcolor;">c0000005</span> ← STATUS_ACCESS_VIOLATION 000000ba`9294e7b8 00000000`00000000 000000ba`9294e7c0
查看原文
查看缓存全文

缓存时间: 2026/06/25 17:09

# 内存中不存在却未被正式卸载的DLL案例,第一部分 - 老调重弹 来源:https://devblogs.microsoft.com/oldnewthing/20260625-00?p=112467 负责 shell32.dll 的团队收到一个 Bug 报告,称某个第三方程序的大量崩溃是由他们造成的。打开崩溃转储文件后,明显可见堆栈溢出的迹象: `` # Child-SP RetAddr Call Site 00 000000ba`92851098 00007ff9`fed521c1 ntdll!_chkstk+0x37 01 000000ba`928510b0 00007ff9`feea5ace ntdll!RtlDispatchException+0x2d1 02 000000ba`92851300 00007ff9`fed4e02d ntdll!KiUserExceptionDispatch+0x2e 03 000000ba`92852060 00007ff9`fed5222f ntdll!RtlLookupFunctionEntry+0x8d 04 000000ba`928520b0 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f 05 000000ba`92852800 00007ff9`fed4e02d ntdll!KiUserExceptionDispatch+0x2e 06 000000ba`92853560 00007ff9`fed5222f ntdll!RtlLookupFunctionEntry+0x8d 07 000000ba`928535b0 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f 08 000000ba`92853d00 00007ff9`fed4e02d ntdll!KiUserExceptionDispatch+0x2e 09 000000ba`92854a60 00007ff9`fed5222f ntdll!RtlLookupFunctionEntry+0x8d 0a 000000ba`92854ab0 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f 0b 000000ba`92855200 00007ff9`fed51f29 ntdll!KiUserExceptionDispatch+0x2e 0c 000000ba`92855f70 00007ff9`feea5ace ntdll!RtlLookupFunctionEntry+0x8d 0d 000000ba`928561c0 00007ff9`fed4e02d ntdll!RtlDispatchException+0x33f ... `` 高亮显示的堆栈帧块(从 `RtlLookupFunctionEntry` 到 `KiUserExceptionDispatch`)重复了很长一段时间。很明显,我们陷入了某种递归异常处理的死亡螺旋。发生了一个异常,内核判定该异常无法在内核模式下处理¹,因此它将异常反射回用户模式进行进一步处理(`KiUserExceptionDispatch`)。在尝试确定要调用哪个异常处理程序时(`RtlLookupFunctionEntry`),我们又触发了异常,从而重新启动了异常循环。最终,所有这些递归异常耗尽堆栈,导致堆栈溢出异常终止进程。 该 Bug 被分配给了 shell32,因为看起来 shell32 是原始异常的来源。如果一直回溯到堆栈底部,会得到类似这样的结果: `` 23f 000000ba`9294c620 00007ff9`fed5222f ntdll!RtlLookupFunctionEntry+0x8d 240 000000ba`9294c670 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f 241 000000ba`9294cdc0 00007ff9`fed4e02d ntdll!KiUserExceptionDispatch+0x2e 242 000000ba`9294db20 00007ff9`fed5222f ntdll!RtlLookupFunctionEntry+0x8d 243 000000ba`9294db70 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f 244 000000ba`9294e2c0 00007ff9`fcba0af0 ntdll!KiUserExceptionDispatch+0x2e 245 000000ba`9294f018 00007ff9`fde2ad13 combase!CoTaskMemFree 246 000000ba`9294f020 00007ff9`fc7abc75 shell32!wil::details::string_maker::~string_maker+0x13 247 000000ba`9294f050 00007ff9`fc7ab897 ucrtbase!::operator()+0xa5 248 000000ba`9294f0a0 00007ff9`fc7ab84d ucrtbase!__crt_seh_guarded_call::operator()+0x3b 249 000000ba`9294f0d0 00007ff9`fc7d2f0c ucrtbase!execute_onexit_table+0x3d 24a 000000ba`9294f110 00007ff9`fdff4645 ucrtbase!__crt_state_management::wrapped_invoke+0x2c 24b 000000ba`9294f140 00007ff9`fdff476e shell32!dllmain_crt_process_detach+0x45 24c 000000ba`9294f180 00007ff9`fee9f6fe shell32!dllmain_dispatch+0xe6 24d 000000ba`9294f1e0 00007ff9`fed4bcae ntdll!LdrpCallInitRoutineInternal+0x22 24e 000000ba`9294f210 00007ff9`fedcd37f ntdll!LdrpCallInitRoutine+0x10e 24f 000000ba`9294f280 00007ff9`fedcc54e ntdll!LdrShutdownProcess+0x17f 250 000000ba`9294f390 00007ff9`fdcb18ab ntdll!RtlExitUserProcess+0x9e 251 000000ba`9294f3c0 00007ff9`e754882e kernel32!ExitProcessImplementation+0xb 252 000000ba`9294f3f0 00007ff9`e754f344 mscoreei!RuntimeDesc::ShutdownAllActiveRuntimes+0x2fa 253 000000ba`9294f6d0 00007ff9`e66f464b mscoreei!CLRRuntimeHostInternalImpl::ShutdownAllRuntimesThenExit+0x14 254 000000ba`9294f700 00007ff9`e66f44c9 clr!EEPolicy::ExitProcessViaShim+0x8b 255 000000ba`9294f760 00007ff9`e66f441e clr!SafeExitProcess+0x9d 256 000000ba`9294f9e0 00007ff9`e66f3f44 clr!HandleExitProcessHelper+0x3e 257 000000ba`9294fa10 00007ff9`e66f3e24 clr!_CorExeMainInternal+0xf8 258 000000ba`9294faa0 00007ff9`e753d6da clr!CorExeMain+0x14 259 000000ba`9294fae0 00007ff9`e75d785b mscoreei!CorExeMain+0xfa 25a 000000ba`9294fb40 00007ff9`fdc9e8d7 mscoree!CorExeMain_Exported+0xb 25b 000000ba`9294fb70 00007ff9`fedcc40c kernel32!BaseThreadInitThunk+0x17 25c 000000ba`9294fba0 00000000`00000000 ntdll!RtlUserThreadStart+0x2c `` 重复块在原始异常源处停止了:`combase!CoTaskMemFree`。我们可以查找异常记录,看看最初的问题是什么。异常记录和上下文记录很可能被传递给 `RtlDispatchException`,因此我们可以查看 `KiUserExceptionDispatch` 传递了什么。 `` # Child-SP RetAddr Call Site 243 000000ba`9294db70 00007ff9`feea5ace ntdll!RtlDispatchException+0x33f 244 000000ba`9294e2c0 00007ff9`fcba0af0 ntdll!KiUserExceptionDispatch+0x2e 0:000> u ntdll!KiUserExceptionDispatch 00007ff9`feea5ace ntdll!KiUserExceptionDispatch: 00007ff9`feea5aa0 cld 00007ff9`feea5aa1 mov rax,qword ptr [ntdll!Wow64PrepareForException (00007ff9`fef272f0)] 00007ff9`feea5aa8 test rax,rax 00007ff9`feea5aab je ntdll!KiUserExceptionDispatch+0x1c (00007ff9`feea5abc) 00007ff9`feea5aad mov rcx,rsp 00007ff9`feea5ab0 add rcx,4F0h 00007ff9`feea5ab7 mov rdx,rsp 00007ff9`feea5aba call rax 00007ff9`feea5abc mov rcx,rsp 00007ff9`feea5abf add rcx,4F0h 00007ff9`feea5ac6 mov rdx,rsp 00007ff9`feea5ac9 call ntdll!RtlDispatchException (00007ff9`fed51ef0) 00007ff9`feea5ace test al,al `` 我们看到传递给 `RtlDispatchException` 的两个参数位于 `rsp+4f0h` 和 `rsp`。我猜测异常记录在前,上下文记录在后,因为这是它们在 `EXCEPTION_POINTERS` 中出现的顺序。 `` # Child-SP RetAddr Call Site 244 000000ba`9294e2c0 00007ff9`fcba0af0 ntdll!KiUserExceptionDispatch+0x2e 00007ff9`feea5ace test al,al 0:000> dps 000000ba`9294e2c0+4f0 000000ba`9294e7b0 00000000`c0000005 ← STATUS_ACCESS_VIOLATION 000000ba`9294e7b8 00000000`00000000 000000ba`9294e7c0 00007ff9`fcba0af0 combase!CoTaskMemFree 000000ba`9294e7c8 00000000`00000002 000000ba`9294e7d0 00000000`00000008 `` 没错,看起来像一个异常记录。它以异常代码开头,随后紧跟着异常发生的代码地址。 `` 0:000> .exr 000000ba`9294e2c0+4f0 ExceptionAddress: 00007ff9fcba0af0 (combase!CoTaskMemFree) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 0000000000000008 Parameter[1]: 00007ff9fcba0af0 Attempt to execute non-executable address 00007ff9fcba0af0 `` 好的,我们试图执行一个不可执行的地址,该地址是 `combase!CoTaskMemFree`。为趣味起见,让我们确认第二个参数确实是一个上下文记录: `` 0:000> .cxr 000000ba`9294e2c0 rax=00007ff9fe3a9850 rbx=000001bbebd12388 rcx=000001bbebd63140 rdx=00007ff9fe4e99e0 rsi=000001bbebd12828 rdi=000001bbebd12310 rip=00007ff9fcba0af0 rsp=000000ba9294f018 rbp=0000df1c60b20569 r8=000001bbebd12310 r9=0000df1c60b20569 r10=d94b3944a87271f0 r11=000000000000000b r12=0000000000000001 r13=00007ff9fdff47c0 r14=000000ba9294f128 r15=000001bbebd12310 iopl=0 nv up ei pl nz na pe nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202 combase!CoTaskMemFree: 00007ff9`fcba0af0 sub rsp,28h `` 是的,看起来像一个上下文记录。但等等,异常声称 `combase!CoTaskMemFree` 不可执行。首先,让我们看看调试器是否同意这个评估。 `` 0:000> !address 00007ff9`fcba0af0 Usage: Image Base Address: 00007ff9`fcb20000 End Address: 00007ff9`fcea6000 Region Size: 00000000`00386000 ( 3.523 MB) State: 00010000 MEM_FREE Protect: 00000001 PAGE_NOACCESS Type: Image Path: C:\Windows\System32\combase.dll Module Name: combase Loaded Image Name: combase.dll Mapped Image Name: C:\symbols\combase.dll More info: lmv m combase More info: !lmi combase More info: ln 0x7ff9fcba0af0 More info: !dh 0x7ff9fcb20000 Content source: 2 (mapped), length: 1eb510 `` 包含 `CoTaskMemFree` 函数的内存已被释放!事实上,如果你查看基地址和区域大小,你会发现整个 combase.dll 已从内存中卸载。另一方面,如果你询问加载器关于那个地址的看法,它会说:“哦,那是 combase.dll 内部的代码。” `` 0:000> !dlls -c 00007ff9`fcba0af0 0x1bbeb111020: C:\WINDOWS\System32\combase.dll Base 0x7ff9fcb20000 EntryPoint 0x7ff9fcc9a9d0 Size 0x00386000 DdagNode 0x1bbeb114380 Flags 0x0028a2cc TlsIndex 0x00000000 LoadCount 0xffffffff NodeRefCount 0x00000000 LDRP_LOAD_NOTIFICATIONS_SENT LDRP_IMAGE_DLL LDRP_PROCESS_ATTACH_CALLED `` 好的,现在我们已经收集了证据,让我们看看能提出什么理论。combase.dll 仍然存在于加载器的记账中,我们看到其加载计数为 0xFFFFFFFF,这意味着该 DLL 已被“固定”,即加载器永远不会卸载它。这两条信息表明,DLL 不是通过 `FreeLibrary` 从内存中移除的,而是由某人显式释放的,比如通过 `VirtualFree` 操作内存。我的猜测是,某处的内存损坏 bug 导致某些代码清理了错误的内存块,并且它无意中释放了 combase.dll 占用的内存,例如因为有人将其“别忘了释放这个”变量覆盖为 combase.dll 的地址,或者存在未初始化变量 bug,未初始化的值恰好是 combase.dll 基址的残留副本。 但无论如何,问题不在于 shell32。shell32 只是另一个受害者,它是第一个在 combase 被某个未知组件强制从内存中移除后调用 combase 的 DLL。如果这个理论正确,那么我应该能够找到类似类型的崩溃,其中其他 DLL 成为被强制从内存中移除的 DLL 的受害者。我要求提供该第三方程序最近 100 次崩溃,并将它们放入数据透视表中,以便查看分布情况。 崩溃类型 | 数量 --- | --- bugcheck_0x124_0_... | 1 bugcheck_0x139_a_... | 1 bugcheck_0x7f_8_... | 1 bugcheck_0xe6_26_... | 7 access_violation_c0000005_contoso!unknown_error_in_application | 23 access_violation_c0000005_gdi32full.dll!__dyn_tls_init | 1 access_violation_c0000005_shell32.dll!invokeshellexecutehook | 1 clr_exception_80004005_contoso!contoso.program.main | 3 clr_exception_80004005_contoso!unknown_function | 1 clr_exception_80070002_contoso!contoso.program.main | 6 clr_exception_80070002_contoso!unknown_function | 3 clr_exception_80070005_contoso!contoso.program.main | 1 clr_exception_8007000b_contoso!contoso.program.main | 21 clr_exception_8007000e_contoso!contoso.program.main | 1 clr_exception_80070422_windows.management.winmd!unknown | 1 clr_exception_800705af_contoso!contoso.program.main | 1 clr_exception_800705af_contoso!unknown_function | 2 clr_exception_8013152d_contoso!contoso.program.main | 1 illegal_instruction_c000001d_contoso!unknown_error_in_application | 1 stack_overflow_c0000005_contoso!unknown_error_in_application | 9 stack_overflow_c0000005_ctxapclient64.dll!unknown | 2 stack_overflow_c0000005_shell32.dll!wil::details::string_maker::~string_maker | 11 stack_overflow_c00000fd_contoso!unknown_error_in_process | 1 shell32 bug 是倒数第二个,占崩溃的 11%。但还有 13 个其他堆栈溢出 bug。还有一堆“未知”的访问冲突。我抽查了那些堆栈溢出和“未知访问冲突”的崩溃,发现它们与 shell32 bug 的形式相同,但 DLL 不同:在发送 `DLL_PROCESS_DETACH` 通知时,发现某个 DLL 已被强制从内存中移除,而下一个调用该强制卸载 DLL 的 DLL 就被责怪,尽管它是受害者。(其中一些以“未知访问冲突”的形式出现,因为系统在异常分发代码内部看到崩溃,并且由于某种原因无法一直回溯到递归崩溃循环的起始点。)因此,总共 46% 的崩溃是由于这种流氓强制卸载 DLL 导致的。这是一个桶喷溅案例 (https://devblogs.microsoft.com/oldnewthing/20200121-00/?p=103351),其中单个根本原因会产生大量不同类型的崩溃。 对于 shell32 团队来说,好消息是他们摆脱了嫌疑;他们是受害者。坏消息是我们不知道罪魁祸首是谁。下次,我们将了解更多关于这些崩溃的信息,这有助于确认关于这个特定崩溃的一些理论,甚至可能否定其他理论。 ¹ 内核模式可以处理的事情包括保护页异常(通过扩展堆栈)或已换出内存中的页面错误(通过将其换回)。

相似文章

关闭显示控制面板时无效函数指针的案例

The Old New Thing (Raymond Chen)

这篇来自 The Old New Thing 的文章调查了 Windows 显示控制面板中一个常见的崩溃问题,该问题是由一个被截断为 32 位并进行符号扩展的无效函数指针引起的。作者通过分析崩溃转储来找出根本原因。