为什么 CDOSYS 向我发送带有 56 字节响应的类型 3 消息?

Why is CDOSYS sending me a type 3 message with a 56 byte response?

我有一些非常旧的代码,我想使用 CDO.Message 对象。许多年前,我认为这段代码确实有效(Windows 98?),但后来它从 Windows XP 开始不起作用(现在我在 Windows 10)。

我的代码首先检查 sResponse2.cLength,如果是 24,则执行 NTLM 计算。如果做不到这一点,如果 sResponse1.cLength 也是 24,那么它会改为执行 LM 计算。这就是我曾经实施过的所有内容。

今天,通过 CDO.Message 测试,它采用了 LM 代码路径,并且我的代码计算的哈希值与 CDO.Message 提供的不匹配。那是另一个问题。

我现在比较感兴趣的是为什么sResponse2.cLength是56。56的值表示什么?鉴于该值和标志,我是否应该使用不同的算法测试类型 3 消息响应?

当我向客户端发送类型 2 消息时,我只指定了这两个标志:

#define F_NEGOTIATE_OEM             0x00000002
#define F_NEGOTIATE_NTLM            0x00000200

响应大于 24 个字节,因为它是 NTLMv2 响应。这 56 个字节包括 16 字节的 HMAC-MD5 哈希、一个 blob 签名、两个“保留”字段、一个 8 字节的时间戳、一个 8 字节的客户端随机数和可变长度目标数据(在本例中为 12 字节的开销). 56 字节可能是空目标 server/domain 字符串的最小可能大小。

如果它是 24 个字节,它可能是 NTLMv2 会话响应,这是不同的。

可在此处找到详细信息: http://davenport.sourceforge.net/ntlm.html

我创建了以下代码来解析 NTLMv2 响应:

struct TYPE3_BLOCK
{
    const BYTE* pcbHash;        // 16 bytes

    const BYTE* pcbBlockPtr;    // Points to the block after the hash
    DWORD cbBlockPtr;

    DWORD dwSignature;
    FILETIME ftStamp;
    const BYTE* pcbNonce;       // 8 bytes
    DWORD dwReserved1;
    DWORD dwReserved2;
    const BYTE* pcbTarget;      // Remainder, less dwReserved2
    DWORD cbTarget;
};

template <typename T>
BOOL TConsumePtr (const BYTE*& pcbData, DWORD& cbData, T** pptPtr, DWORD cbPtr)
{
    if(cbData >= cbPtr)
    {
        *pptPtr = reinterpret_cast<T*>(pcbData);
        pcbData += cbPtr;
        cbData -= cbPtr;
        return TRUE;
    }

    return FALSE;
}

template <typename T>
BOOL TConsumeData (const BYTE*& pcbData, DWORD& cbData, T* ptPtr)
{
    if(cbData >= sizeof(T))
    {
        CopyMemory(ptPtr, pcbData, sizeof(T));
        pcbData += sizeof(T);
        cbData -= sizeof(T);
        return TRUE;
    }

    return FALSE;
}

BOOL CrackType3Response (const BYTE* pcbResponse, DWORD cbResponse, __out TYPE3_BLOCK* pBlock)
{
    if(TConsumePtr(pcbResponse, cbResponse, &pBlock->pcbHash, 16))
    {
        pBlock->pcbBlockPtr = pcbResponse;
        pBlock->cbBlockPtr = cbResponse;

        if(TConsumeData(pcbResponse, cbResponse, &pBlock->dwSignature) &&
            TConsumeData(pcbResponse, cbResponse, &pBlock->dwReserved1) &&
            TConsumeData(pcbResponse, cbResponse, &pBlock->ftStamp) &&
            TConsumePtr(pcbResponse, cbResponse, &pBlock->pcbNonce, 8))
        {
            pBlock->cbTarget = cbResponse - sizeof(pBlock->dwReserved2);

            return TConsumePtr(pcbResponse, cbResponse, &pBlock->pcbTarget, pBlock->cbTarget) &&
                TConsumeData(pcbResponse, cbResponse, &pBlock->dwReserved2);
        }
    }

    return FALSE;
}