TONT 102300 (38583) 不要把你的DLL命名为Security.dll

题外话:

鉴于 The old new thing 博客迁移至 Microsoft Developer Blogs 之后,部分文章 ID 发生了彻底变化(毫不相关),故自本篇起,存在该问题的对应博文,如在本人手中具有旧版备份,则会在标题中以括号的形式标出旧 ID,新 ID 则在其之前,以不带括号的形式提供。如遇文章的新、旧 ID 相同,或可以通过旧 ID 直接在新版 The old new thing 上进行访问,则只会撰写一个 ID。

原文链接:https://devblogs.microsoft.com/oldnewthing/20040702-00/?p=102300

Reaching back into the history bucket…

让我们回到历史的故纸堆中。

Some people have discovered that strange things happen if you name your DLL “security.dll”.

有人发现,如果将你(开发)的 DLL 命名为 security.dll,就会发生一些奇怪的事情。

The reason is that there is already a system DLL called “security.dll”; it’s the Security Support Provider Interface DLL, and it used to go by the name “security.dll”, though nowadays the name “secur32.dll” is preferred. If you look into your system32 directory, you’ll see both “security.dll” and “secur32.dll” in there. And if you’re handy with an export dumper, you’ll see that “security.dll” is just a bunch of forwarders to “secur32.dll”. If you browse through the MSDN documentation, you’ll see that everybody talks about “secur32.dll” and hardly any mention is made of its doppelgänger “security.dll”.

这是由于已经有一个系统 DLL 的名字叫 security.dll 了,它是(系统的)安全支持提供器接口(SSPI)的 DLL,并且曾经用过  security.dll 的名字,虽然如今比较推荐使用 secur32.dll 就是了。打开你的 system32 目录的话,你会同时看到 secutiry.dll 和 secur32.dll,如果手头上正好有DLL导出函数浏览器的话,你会发现 security.dll 的导出函数不过是一大堆到 secur32.dll 的转发而已。另外,浏览一下 MSDN 文档,也会发现大多数文章里提及的都是 secur32.dll,而极少提及它的影子弟兄 security.dll。

Okay, here’s where the history comes in. Wind back to Windows 95.

好了,现在让我们来谈谈历史,首先回到 Windows 95 的年代。

Back in those days, the Security Support Provider Interface was implemented in two different DLLs. The one you wanted depended on whether you are running Windows NT or Windows 95. On Windows 95, it was called “secur32.dll”, but on Windows NT, it was called “security.dll”.

从前,SSPI 是通过两个不同的 DLL 实现的,需要使用哪一个取决于是在运行 Windows NT 还是 Windows 95。在 Windows 95 里,SSPI 的 DLL 叫 secur32.dll,但在 Windows NT 中,则叫 security.dll。

This was obviously a messed-up state of affairs, so the Windows NT folks decided to “go with the flow” and rename their security DLL to “secur32.dll”. This was probably for application compatibility reasons: Applications that were written to run on Windows 95 and were never tested on Windows NT just went straight for “secur32.dll” instead of loading the correct DLL based on the operating system.

很明显,这样的安排简直是一团糟,所以 Windows NT 开发组的老哥们决定『随波逐流』,将他们的安全 DLL 改名成了 secur32.dll。这大概是出于对应用程序兼容性的考量:面向 Windows 95 开发、并且从来没有在 Windows NT 上测试过的的应用程序会直奔 secur32.dll 而去,而不会根据操作系统的情况去选择加载正确的 DLL。

Okay, so now pop back to the present day. When you put a DLL called “Security.dll” in your application directory, what happens?

好了,回到现在。当你将一个叫 security.dll 的 DLL 放在你的应用程序目录中,会发生什么呢?

Recall that the rules for the order in which DLLs are searched for checks the application directory before it checks the system directory. As a result, anybody in your application who wants “Security.dll” will get your version instead of the system version.

回忆一下有关 DLL 的搜索顺序的设定(译注:原链接已失效,中文链接为新地址),Windows 会在去系统目录下查找之前,先在应用程序的工作目录下进行搜索。因此,由你的应用程序发起的调用,如果要查找名叫 security.dll 的 DLL,都会拿到你(放在应用程序目录下)的版本,而不是系统(目录)中的版本。

Even if the system version is the one they really wanted.

即便系统目录中的那个版本是它们真正需要的。

That’s why overriding the system’s Security.dll with your own results in a bunch of SSPI errors. Components you are using in your program are trying to talk to SPPI by loading “security.dll” and instead of getting the system one, they get yours. But yours was never meant to be a replacement for “security.dll”; it’s just some random DLL that happens to have the same name.

这就是为什么以你自己版本的 security.dll 替代系统版本会引发一大堆 SSPI 相关的错误。你的应用程序中的组件尝试进行 SSPI 调用时会去尝试加载 security.dll,而基于以上规则,组件会拿到你的程序目录下的版本,而不是系统目录中的版本。然而你的 security.dll 并不是系统中那个 security.dll 的替代品,只是一个恰好同名的、其它用途的 DLL 而已。

You would have had the same problem if you happened to name your DLL something like “DDRAW.DLL” and some component in your program tried to create a DirectDraw surface. “Security.dll” has the disadvantage that it has a simple name (which people are likely to want to name their own DLL), and its importance to proper system functionality is not well-known. (Whereas it would be more obvious that creating a DLL called “kernel32.dll” and putting it in your application directory is going to cause nothing but trouble.)

同样地,当你的 DLL 碰巧起了个名字叫 ddraw.dll 而你的程序中某个组件尝试创建 DirectDraw 绘图平面时,也会发生类似的问题。Security.dll 的劣势在于其名称太简明了(因而人们可能会把他们的 DLL 起一个相同的名字),并且其对系统运行的重要性并非广为人知。(然而更明显的是创建一个 DLL 起名叫 kernel32.dll 然后放进你的应用程序目录中,那么除了麻烦之外什么也不会产生。)

(译注:事实上 kernel32.dll 由于属于 KnownDLLs(注册表 HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs),Windows 会仅在系统目录中查找在该列表中的DLL 而不遵守前述的规则,所以并不会发生这个问题,而 security.dll 之所以至今有这个问题,个人认为更多的还是由于首先微软在历史上搞砸了这个文件的命名,然后又替水平参差不齐的软件开发商背了一部分的黑锅)

Day 11314 关于本博客『Win故知新』分类恢复更新的说明

对 Raymond Chen 的博客《The old new thing》(下文简称TONT)的随缘翻译停了几个月,起因是大约在今年(2019年)5月份时(相关博文原文链接),TONT 整体迁移到了 Microsoft Developer Blog平台,该平台较之旧版对移动端友好,但由于需要遵守欧盟缺德数据保护条例(GDPR),所有的评论都被丢弃了。由于某些文中未完整解明的问题,评论区有人提问后,Raymond 可能在回复中给予进一步解释,故对评论的丢弃实为一大损失。

虽然微软在推特账号 MSDN Services Status(@MSDNService)上声明,为便于用户通过搜索(引擎?)查找已经不再活跃更新的MSDN Blog、TechNet Blog上的数据,将对这些博客进行静态化处理,然而对于一直都在更新的 TONT 来说显然不在此列,而后面几个月以来的实际情况也证实了这一点。

发现该情况后,虽感觉希望渺茫,本人仍然尝试了许多方法去访问旧版的 TONT 内容。在经历了不下数十次碰壁后,发现可能是由于 CDN 缓存的原因,每20-30次访问之间偶尔可以进入旧版页面,同时,该现象还具有如果访问结果是跳转新版页面,则在 HTTP 状态码为 301 的同时内容为空的特点。由此,本人编写了一个简单的下载程序,通过导入 URL 列表的方式,对每个页面最多访问一万次(两次访问间有2-5秒的随机间隔),试图拉取到旧版内容。

程序自2019年5月31日上线,经过一个多月的7×24连续运行,至7月3日起,连续一个星期没有再拉取到新的内容,基本判定该 bug 已被修复,于是停止了下载程序的运行。期间,共成功下载到 TONT 的 1804 篇旧版博客内容,包含对应文章的完整评论。该部分内容不会放出下载,以避免不必要的麻烦。

即日起,『Win故知新』栏目恢复更新,更新频率仍为随缘。

Day 11175 自行组装RAID10磁盘阵列

以前的时候,由于家里的网速一直不是很快,加上由于环境受限,一直没有外网环境,从来没有做过资源党,也就对硬盘空间没有多高的需求。事实上,从第一台电脑开始,家中的台式机几乎一一直是单(机械)硬盘配置,依次经历了2G、20G、120G、600G、1T和2T的容量台阶,并且在600G以前一直是IDE接口。

然而对存储空间的追求是永无止境的,为了在一段时间内暂时不再受台式机硬盘的限制,又忌惮于群晖的价格,一狠心,实践了一次用阵列卡自行组装磁盘阵列的想法。

这中间也算学到了点东西。

(更多…)