我们如何确定 CcNamespace.dll 是一组过早卸载的 DLL 中的主谋?
摘要
Raymond Chen 解释了他是如何通过分析调试器的已卸载 DLL 列表,并注意命名模式和顺序,从而确定 CcNamespace.dll 是一串过早卸载的 DLL 中的关键 DLL。
<p>当我展示<a title="线程从已卸载的第三方 DLL 执行的案例" href="https://devblogs.microsoft.com/oldnewthing/20260702-00/?p=112500">我对于由线程从已卸载的第三方 DLL 执行导致的崩溃的分析</a>时,有人问我如何得出结论认为 <tt>CcNamespace.dll</tt> 是一系列相关 DLL 的“主谋”。</p>
<p>最近卸载的 DLL 列表被记录在一个循环历史中¹。因此,当你看到一行中列出多个 DLL 时,它们是一个接一个卸载的²。</p>
<pre>00007ff9`6d7c0000 00007ff9`6d80a000 FabrikamContextMenu.dll
00007ff9`115e0000 00007ff9`1172f000 LitWareSync.dll
00007ff9`643d0000 00007ff9`64681000 CcNamespace.dll
00000000`55440000 00000000`5550b000 LibDB_CloudNs_3.dll
00000000`55860000 00000000`55998000 LibNet_CloudNs_3.dll
00000000`557f0000 00000000`5585b000 LibJson_CloudNs_3.dll
00000000`55510000 00000000`557e7000 LibUtils_CloudNs_3.dll
00000000`561a0000 00000000`56238000 MSVCP100.dll
00000000`56240000 00000000`56312000 MSVCR100.dll
00007ff9`85130000 00007ff9`85167000 EhStorShell.dll
00007ff9`3cac0000 00007ff9`3cb61000 wpdshext.dll
00007ff9`78a00000 00007ff9`78a26000 EhStorAPI.dll
00007ff9`686f0000 00007ff9`68754000 PlayToDevice.dll
00007ff9`67110000 00007ff9`6718d000 provsvc.dll
</pre>
<p>从相似的名称可以明显看出,<tt>Lib⟦...⟧.CloudNs.3.dll</tt> 这些 DLL 都密切相关。</p>
<p>名称 <tt>CcNamespace.dll</tt> 某种程度上也对得上,如果你认为 <tt>Cc</tt> 代表“Contoso Cloud”,而其他 DLL 名称中的 <tt>Ns</tt> 是“Namespace”的缩写。</p>
<p>我不确定已卸载 DLL 列表是按时间顺序还是逆时间顺序排列的,但事实证明这对本分析并不重要:<tt>CcNamespace.dll</tt> 要么是第一个卸载的,要么是最后一个卸载的,而且它的名称与其他 DLL 略有不同,这进一步支持了它是整个 DLL 家族核心的猜想。</p>
<p>为什么名称略有不同会成为线索?</p>
<p>其他 DLL 似乎来自这家公司重用的一套辅助 DLL 库。名称略有不同的那个是“前台”,它从其他 DLL 中召集了一组助手。就像你参加婚礼或其他正式活动时,可以通过着装略微不同来识别服务人员的负责人。而且你可以想象,如果所有服务人员需要排成一列行走,负责人很可能站在队伍的最前面或最后面。</p>
<p>在列表的开头或结尾发现名称略有不同的 DLL 并不能证明什么,但这是一个非常有力的证据。我们通过从应用程序兼容性库中安装一个 Contoso Cloud 的副本,并查看哪个 DLL 被注册为命名空间扩展 DLL,验证了这一理论。</p>
<p>¹ 调试器只是将每个卸载的 DLL 添加到数组中,当到达数组末尾时,“下一个 DLL 的存放位置”会循环回到数组顶部。不幸的是,调试器中没有任何指示“下一个 DLL 的存放位置”指针在哪里的信息。</p>
<p>² 即使它们是连续的,你也不知道它们之间经过了多少时间。</p>
<p>本文最初发布在 <a href="https://devblogs.microsoft.com/oldnewthing">《旧日新事》</a> 上,标题为 <a href="https://devblogs.microsoft.com/oldnewthing/20260703-00/?p=112504">我们如何确定 <TT>CcNamespace.dll</TT> 是一组过早卸载的 DLL 中的主谋?</a>。</p>
查看缓存全文
缓存时间: 2026/07/04 06:38
# 我们如何断定 CcNamespace.dll 是一组提前卸载的 DLL 中的“罪魁祸首”?—— 旧事新知
来源:https://devblogs.microsoft.com/oldnewthing/20260703-00?p=112504
在我介绍关于一个由从已卸载的第三方 DLL 中执行的线程引发的崩溃的研究时(https://devblogs.microsoft.com/oldnewthing/20260702-00/?p=112500),有人问我如何断定 `CcNamespace.dll` 是这一系列关联 DLL 的主导者。
最近卸载的 DLL 列表记录在一个循环历史中¹。所以当你看到一连串的 DLL 排成一行时,它们是依次被卸载的²。
```
00007ff9`6d7c0000 00007ff9`6d80a000 FabrikamContextMenu.dll
00007ff9`115e0000 00007ff9`1172f000 LitWareSync.dll
00007ff9`643d0000 00007ff9`64681000 CcNamespace.dll
00000000`55440000 00000000`5550b000 LibDB_CloudNs_3.dll
00000000`55860000 00000000`55998000 LibNet_CloudNs_3.dll
00000000`557f0000 00000000`5585b000 LibJson_CloudNs_3.dll
00000000`55510000 00000000`557e7000 LibUtils_CloudNs_3.dll
00000000`561a0000 00000000`56238000 MSVCP100.dll
00000000`56240000 00000000`56312000 MSVCR100.dll
00007ff9`85130000 00007ff9`85167000 EhStorShell.dll
00007ff9`3cac0000 00007ff9`3cb61000 wpdshext.dll
00007ff9`78a00000 00007ff9`78a26000 EhStorAPI.dll
00007ff9`686f0000 00007ff9`68754000 PlayToDevice.dll
00007ff9`67110000 00007ff9`6718d000 provsvc.dll
```
从相似的名称来看,`Lib[...].CloudNs.3.dll` 这些 DLL 显然是密切相关的。
`CcNamespace.dll` 这个名称也大致对得上,如果你猜想 `Cc` 代表 “Contoso Cloud”,而其它 DLL 名称中的 `Ns` 是 “Namespace” 的缩写。
我不确定已卸载 DLL 列表是按时间正序还是逆序排列的,但事实证明这对于分析并不重要:`CcNamespace.dll` 要么是第一个卸载的,要么是最后一个卸载的。而且它的名字与其他 DLL 略有不同,这进一步支持了它是一整族 DLL 核心枢纽的推断。
为什么名称略有不同会成为重要线索呢?
其它 DLL 似乎来自该公司重复使用的一套辅助 DLL 库。而那个名字略有不同的 DLL 则像是“门面”,它从其它 DLL 中组建了一支辅助团队。就好比参加婚礼或其他正式活动时,你能从服务人员中认出负责人,因为他们的着装略有不同。你还可以想象,如果所有服务人员需要排成单列前进,负责人很可能会站在队伍的最前面或最后面。
在列表的开头或末尾发现名称略有不同的 DLL 并不是确凿证据,但却是非常有力的佐证。我们通过从应用程序兼容库中安装一份 Contoso Cloud,并查看哪个 DLL 被注册为命名空间扩展 DLL,最终验证了这一推断。
¹ 调试器只是将每个已卸载的 DLL 依次添加到数组中,当到达数组末尾时,“下一个 DLL 存放位置”会循环回到数组开头。遗憾的是,调试器中没有指示当前“下一个 DLL 存放位置”指针的位置。
² 即使它们是顺序出现的,你也不知道它们之间间隔了多长时间。
### 分类
### 主题
## 作者
Raymond Chen
Raymond 参与 Windows 的演进已有 30 多年。2003 年,他创建了一个名为“旧事新知”的网站,其受欢迎程度远远超出了他最大胆的想象,这一发展至今仍让他感到不寒而栗。该网站后来衍生出一本书,巧合的是,书名也是《旧事新知》(Addison Wesley,2007 年)。他偶尔会出现在 Windows Dev Docs 的 Twitter 账号上,讲述一些毫无实用信息的故事。
相似文章
关于线程从已卸载的第三方DLL中执行的问题
微软Raymond Chen的一则技术调试故事,讲述了Windows资源管理器崩溃由从已卸载第三方DLL中执行的线程引起,展示了开发者如何调查此类问题。
一个DLL未正式卸载却从内存消失的案例,第一部分
本文调查了一个崩溃转储文件:进程关闭期间,shell32.dll中发生栈溢出,导致重复的异常处理循环。分析追踪了递归异常处理,并确定了涉及DLL卸载的根本原因。
一个未正式卸载却从内存中消失的DLL案例,第二部分
Ray Chen的一篇技术博文,探究一个内存损坏bug:单个字节0x01破坏HMODULE句柄,导致DLL被错误释放,进而在进程终止时崩溃。
Windows DLL加载器锁:Rust线程如何使JVM挂起
QuestDB工程师调试一个偶发的Windows挂起问题,该问题由涉及Windows DLL加载器锁、Rust线程本地存储销毁、JNI分离和JVM垃圾收集安全点机制的死锁引起。
Windows 95 如何判断一个安装程序正在运行?
Raymond Chen 解释了 Windows 95 如何通过检查可执行文件名中是否包含“setup”或“install”等魔法词来判断一个程序是否为安装程序。