一个DLL未正式卸载却从内存消失的案例,第一部分
摘要
本文调查了一个崩溃转储文件:进程关闭期间,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!<lambda_f03950bc5685219e0bcd2087efbe011e>::operator()+0xa5
248 000000ba`9294f0a0 00007ff9`fc7ab84d ucrtbase!__crt_seh_guarded_call<int>::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 团队来说,好消息是他们摆脱了嫌疑;他们是受害者。坏消息是我们不知道罪魁祸首是谁。下次,我们将了解更多关于这些崩溃的信息,这有助于确认关于这个特定崩溃的一些理论,甚至可能否定其他理论。
¹ 内核模式可以处理的事情包括保护页异常(通过扩展堆栈)或已换出内存中的页面错误(通过将其换回)。
相似文章
一个未正式卸载却从内存中消失的DLL案例,第二部分
Ray Chen的一篇技术博文,探究一个内存损坏bug:单个字节0x01破坏HMODULE句柄,导致DLL被错误释放,进而在进程终止时崩溃。
关于线程从已卸载的第三方DLL中执行的问题
微软Raymond Chen的一则技术调试故事,讲述了Windows资源管理器崩溃由从已卸载第三方DLL中执行的线程引起,展示了开发者如何调查此类问题。
我们如何确定 CcNamespace.dll 是一组过早卸载的 DLL 中的主谋?
Raymond Chen 解释了他是如何通过分析调试器的已卸载 DLL 列表,并注意命名模式和顺序,从而确定 CcNamespace.dll 是一串过早卸载的 DLL 中的关键 DLL。
关闭显示控制面板时无效函数指针的案例
这篇来自 The Old New Thing 的文章调查了 Windows 显示控制面板中一个常见的崩溃问题,该问题是由一个被截断为 32 位并进行符号扩展的无效函数指针引起的。作者通过分析崩溃转储来找出根本原因。
Windows DLL加载器锁:Rust线程如何使JVM挂起
QuestDB工程师调试一个偶发的Windows挂起问题,该问题由涉及Windows DLL加载器锁、Rust线程本地存储销毁、JNI分离和JVM垃圾收集安全点机制的死锁引起。