LookupAccountSid() 损坏内存堆

LookupAccountSid() corrupts memory heap

我花了几天时间追查表现为结构化异常的崩溃 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:66:7 -> 5:6 之类的内容,并且一切正常。基本上,第一次调用 returns 所需的缓冲区大小(包括终止 NUL),第二次调用 returns 写入的符号数量(不包括 NUL)到提供的缓冲区(过度分配) 1,顺便说一句)。

现在,一个特定的 SID 导致 6:3 -> 5:2 输出。但是当我查看 pDomain 缓冲区(长度为 4 个符号)时,我看到截断的域名 ABCD (实际域名为 6 个符号)后跟 NUL (这会破坏堆控制结构) .所以 LookupAccountSidW 声称它只写入了 2(+1) 个符号,而实际上是将 4(+1) 个符号写入了 4 个符号长的缓冲区。

从我的角度来看,这是 LookupAccountSidW 中的一个明显错误,但我真的很想弄清楚该 SID 与其他 SID 有何不同。也许它是从另一个(更短的)域迁移过来的?

P.S。它是 Windows 10 (10.0.14393.2969)

P.P.S。 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 使用错误的数组位置来设置域数据所需的大小。如果有记忆,它会使用位置零,而它应该不是那个位置。因此,如果位置为零的域的名称短于与您正在查找的 SID 关联的名称,LookupAccountSidA 将自行覆盖堆。

唯一的解决方法是传递两个 256 个字符的缓冲区,并将它们填充初始化为“\0”。将这些传递给您的名称和域数组,不要相信为域返回的大小。将这些从您返回的缓冲区中复制出来,注意不要在读取时溢出缓冲区。

当然,更好的选择是使用 LsaLookupSids2,并在必要时进行 Unicode 到多字节的转换。

PS:Microsoft 的一位代表告诉我,这是 Windows 7 和 8 之间引入的错误。我不知道这是否会影响 LookupAccountSidW,但它最当然可以。