ProbeForRead和ProbeForWrite

WRK中这两个函数的实现:


VOID
ProbeForWrite (
    __inout_bcount(Length) PVOID Address,
    __in SIZE_T Length,
    __in ULONG Alignment
    )
{
    ULONG_PTR EndAddress;
    ULONG_PTR StartAddress;

#define PageSize PAGE_SIZE

    if (Length != 0) {
        ASSERT((Alignment == 1) || (Alignment == 2) ||
               (Alignment == 4) || (Alignment == 8) ||
               (Alignment == 16));
        StartAddress = (ULONG_PTR)Address;
        if ((StartAddress & (Alignment - 1)) == 0) {
            EndAddress = StartAddress + Length - 1;
            if ((StartAddress <= EndAddress) &&
                (EndAddress < MM_USER_PROBE_ADDRESS)) {
                //
                // If this is a Wow64 process, then the native page is 4K, which
                // could be smaller than the native page size/
                //
                EndAddress = (EndAddress & ~(PageSize - 1)) + PageSize;
                do {
                    *(volatile CHAR *)StartAddress = *(volatile CHAR *)StartAddress;
                    StartAddress = (StartAddress & ~(PageSize - 1)) + PageSize;
                } while (StartAddress != EndAddress);
                return;
            } else {
                ExRaiseAccessViolation();
            }
        } else {
            ExRaiseDatatypeMisalignment();
        }
    }
    return;
}

VOID
ProbeForRead(
    __in_bcount(Length) VOID *Address,
    __in SIZE_T Length,
    __in ULONG Alignment
    )
{
    PAGED_CODE();
    ASSERT((Alignment == 1) || (Alignment == 2) ||
           (Alignment == 4) || (Alignment == 8) ||
           (Alignment == 16));
    if (Length != 0) {
        if (((ULONG_PTR)Address & (Alignment - 1)) != 0) {
            ExRaiseDatatypeMisalignment();
        } else if ((((ULONG_PTR)Address + Length) > (ULONG_PTR)MM_USER_PROBE_ADDRESS) ||
                   (((ULONG_PTR)Address + Length) < (ULONG_PTR)Address)) {
            *(volatile UCHAR * const)MM_USER_PROBE_ADDRESS = 0;
        }
    }
}

可以看到这两个函数仅仅是检测传入的地址是否对齐、是否在用户地址空间,*Write的还检测是否可写,如果出现意外就抛出异常,所以应该配合__try和__except使用。如果传入内核地址,将会产生异常,try同时会捕获该异常。

这两个函数的使用常常会造成一些危险,因为它们的检测非常简单,而过度依赖或者认为它们很强大,会使恶意软件绕过防护。比如捕获到对齐错误时,即STATUS_DATATYPE_MISALIGNMENT异常,这时不做进一步检查直接放行,就会出问题;对length==0的检查也一样容易出问题。