windows - LookupAccountSid() 损坏内存堆
问题描述
我花了几天时间追踪一个表现为结构化异常0xC0000374
(堆损坏)的崩溃......当然,只能在客户环境中重现。
将其缩小到这个(非常简化的)代码:
DWORD cchName = 0, cchDomain = 0;
SID_NAME_USE type;
if (!LookupAccountSidW(NULL, pSid, NULL, &cchName, NULL, &cchDomain, &type))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
<bail via exception or return>;
}
cout << cchName << ":" << cchDomain << " -> ";
DWORD cchName1 = (cchName + 1), cchDomain1 = (cchDomain + 1);
LPWSTR pName = ... allocate cchName1 WCHARs ...;
LPWSTR pDomain = ... allocate cchDomain1 WCHARs ...;
if (!LookupAccountSidW(NULL, pSid, pName, &cchName1, pDomain, &cchDomain1, &type))
<bail via exception or return>;
cout << cchName1 << ":" << cchDomain1 << endl;
... deallocate pDomain; // <- here Application Verifier detects corrupted block
... deallocate pName;
请忽略内存泄漏的可能性(代码已简化)。另外,请注意,根据MSDN,不需要过度分配 1 个符号。但是让我描述一下我在调试器中看到的......
对于遇到的所有(除了一个)SID,它会打印出诸如16:7 -> 15:6
or之类的东西,6:7 -> 5:6
而且一切都很花哨。基本上,第一个调用返回所需的缓冲区大小(包括 terminating NUL
),第二个调用返回写入(不包括 )到提供的缓冲区中的符号数(NUL
顺便说一句,这些缓冲区被过度分配)。
现在,一个特定的 SID 会产生6:3 -> 5:2
输出。但是当我查看pDomain
缓冲区(长度为 4 个符号)时,我看到了截断的域名ABCD
(实际域名为 6 个符号),然后是NUL
(这会破坏堆控制结构)。所以LookupAccountSidW
声称它只写入了 2(+1) 个符号,而实际上它已经将 4(+1) 个符号写入了一个 4 个符号长的缓冲区。
从我的角度来看,这是一个明显的错误LookupAccountSidW
,但我真的很想弄清楚该 SID 与其他 SID 有何不同。也许它是从另一个(较短的)域迁移的?
PS 它是 Windows 10 (10.0.14393.2969)
PPS SID 是S-1-5-21-<3-part domain id>-<user id>
解决方案
我遇到了这个问题,所以我正在为不幸落入这个陷阱的人记录我的发现。
似乎存在 LookupAccountSidA 在某些环境中会导致堆溢出的情况。
可以将用户从一个域迁移到另一个域。由于历史原因,SID 可以保留为域的一部分。LookupAccountSidA 应该支持查找这些 SID,它会这样做,只是当旧域的名称比新域的名称长时,它会在内部超出堆。
在后台,LookupAccountSidA 调用 LsaLookupSids2,它获取一个 PLSA_TRANSLATED_NAME(名称数组)和一个 PLSA_REFERENCED_DOMAIN_LIST。这里的问题是 LookupAccountSidA 使用了错误的数组位置来设置域数据所需的大小。如果有记忆,它会使用位置零,它应该是其他的东西。因此,如果位置 0 的域的名称比与您正在查找的 SID 关联的名称短,LookupAccountSidA 将自行覆盖堆。
唯一的解决方法是传递两个 256 个字符的缓冲区,并将它们填充初始化为 '\0'。将这些传入您的名称和域数组,不要相信为域返回的大小。将这些从您返回的缓冲区中复制出来,并注意不要在读取时超出缓冲区。
当然,更好的选择是使用 LsaLookupSids2 并在必要时进行 Unicode 到多字节的转换。
PS:微软的一位代表告诉我,这是在 Windows 7 和 8 之间引入的错误。我不知道这是否会影响 LookupAccountSidW,但肯定有可能。
推荐阅读
- ruby-on-rails - 模块解析失败:意外字符 '�' (1:0) 添加 jpeg 或 mp3 时,您可能需要适当的加载程序来处理此文件类型
- templates - 将值传递给内部模板时,可观察到的淘汰模板不起作用
- javascript - 使用 React Router 进行路由连接
- javascript - 如何在 javascript 上从数据 json 创建列表?
- c++ - 如何将特定的 QPixmap 拖放到 QGraphicsView 中?
- python - 计算 csv 文件中的元素总数
- node.js - 'npm' 'node' is not recognized as internal or external command, operable program or batch file for Windows
- javascript - Angular9 sanitizer:Sanitizer 返回 null
- c++ - 为什么 rand()%10+1 给我一个 1-10 之间的数字?
- java - 使用 RewardedAd 将用户名传递给另一个活动