【问题标题】:Access violation when reading buffer of UNICODE_STRING读取 UNICODE_STRING 的缓冲区时访问冲突
【发布时间】:2018-09-07 08:25:07
【问题描述】:

我编写了一个使用 KERB_CERTIFICATE_LOGON 结构的自定义凭据提供程序。 现在一切正常,我尝试编写一些单元测试(我知道应该在编码之前编写测试,但在这种情况下,我必须先弄清楚一切是如何工作的;-))。

当我尝试使用this SO-question 的解决方案和 wcsncmp 来断言 KERB_CERTIFICATE_LOGON 结构的内容时,如下所示:

KERB_CERTIFICATE_LOGON* kerbCertificateLogon = reinterpret_cast<KERB_CERTIFICATE_LOGON*>(serializedCredentials->rgbSerialization);
wcsncmp(domainName, kerbCertificateLogon->DomainName.Buffer, kerbCertificateLogon->DomainName.Length / sizeof(wchar_t));

此时我在 wcsncmp 中遇到访问冲突:

    extern "C" int __cdecl wcsncmp(
    wchar_t const* a,
    wchar_t const* b,
    size_t         count
    )
{
    if (count == 0)
        return 0;

    while (--count != 0 && *a && *a == *b) // <== Here comes "read access violation b was 0x48"
    {
        ++a;
        ++b;
    }

    return static_cast<int>(*a - *b);
}

此外,在调试填充缓冲区的函数时,我可以在使用以下代码填充缓冲区后直接在 Visual Studio 的“Locals”视图中看到:

kerbCertificateLogon->DomainName.Buffer = reinterpret_cast<PWSTR>(domainBuffer - authInfo);

这个结果:

0x0000000000000048

WTF??? LSA 使用相同的代码时工作正常,所以我认为一切都很好,但为什么我不能在简单的单元测试中读取值?

【问题讨论】:

  • 什么是domainBufferauthInfo 是什么?它们有什么关系?您可以尝试创建一个Minimal, Complete, and Verifiable Example 向我们展示吗?虽然崩溃可能发生在库函数中,但根本原因不在函数中。
  • 我知道 mcve。在这种情况下,这可能会很困难,因为凭据提供程序不能归结为某些完整的代码行。可以找到 KERB_CERTIFICATE_LOGN 的完整构造here on SO
  • 你需要使用RtlEqualUnicodeString(如果只需要==和!=)或RtlCompareUnicodeString(如果需要>UNICODE_STRING
  • 我已经阅读过相关内容,但我不想安装驱动程序套件只是为了进行单元测试,并且在其他地方找不到 wdm.h。
  • 构造minimal reproducible example 非常简单。您不需要任何证书结构。您可以通过制作 2 个类似于您对 wcsncmp 的输入的 UNICODE_STRING 对象来重现该问题。

标签: c++ winapi credential-providers


【解决方案1】:

好的,像往常一样,我在询问后不久就找到了解决方案。 与上面的错误之一相反,错误必须与证书结构有关!因此,仅使用两个 UNICODE_STRING 无法重现该问题。

在这些结构内部,相对指针用于存储信息,从旧问题的链接中可以看出。

我只是只见树木不见森林。

所以解决方案非常简单:我必须重新计算绝对指针,而不是直接使用指向 UNICODE_STRING 中缓冲区的指针。我将它提取到一个方法中,因为我在测试中经常需要这种比较:

static void AssertRelativeUnicodeStringEquals(const wchar_t* expected, UNICODE_STRING actual, LPBYTE basePointer)
{
    const int result = wcscmp(expected, reinterpret_cast<PWSTR>(basePointer + reinterpret_cast<int>(actual.Buffer)));
    EXPECT_EQ(0, result);
}

我现在可以调用:

AssertRelativeUnicodeStringEquals(domainName, kerbCertificateLogon->DomainName, (LPBYTE)kerbCertificateLogon);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-16
    • 2015-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多