首页
社区
课程
招聘
求助,关于在verifier.exe开启Force IRQL Checking选项时,会导致MmIsAddressValid返回非预期结果的问题
发表于: 2023-5-10 17:20 6743

求助,关于在verifier.exe开启Force IRQL Checking选项时,会导致MmIsAddressValid返回非预期结果的问题

2023-5-10 17:20
6743

向各位大佬们请教一个问题:在verifier.exe开启Force IRQL Checking选项时,由RtlAnsiStringToUnicodeString返回的UNICODE_STRING在卸载流程中MmIsAddressValid返回失败的结果,而在不使用verifier时返回正确,暂时没有找到确切原因,请大家帮个忙,谢谢。

#include <ntifs.h>

UNICODE_STRING g_ustr = {0};

VOID DriverUnload(PDRIVER_OBJECT pDrvObj) {
    /*
     * 当开启verifier的Force IRQL Checking时"MmIsAddressValid"会返回False,否则返回True
     */
    KIRQL irql = KeGetCurrentIrql();
    DbgPrint("Unload IRQL [%d] \n", irql);

    DbgPrint("Ustr --> [%wZ][%d] \n", &g_ustr, MmIsAddressValid(g_ustr.Buffer));
    RtlFreeUnicodeString(&g_ustr);
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pUstrRegPath) {
    NTSTATUS status = STATUS_SUCCESS;
    ANSI_STRING ansiString;
    RtlInitAnsiString(&ansiString, "abbccddee");
    
    pDrvObj->DriverUnload = DriverUnload;

    status = RtlAnsiStringToUnicodeString(&g_ustr, &ansiString, TRUE);
    DbgPrint("RtlAnsiStringToUnicodeString return [0x%X] \n", status);

    if (!NT_SUCCESS(status)) {
        return STATUS_UNSUCCESSFUL;
    }

    /*
     * 此处不影响,"MmIsAddressValid"的返回值依然是True
     */
    DbgPrint("Ustr --> [%wZ][%d] \n", &g_ustr, MmIsAddressValid(g_ustr.Buffer));

    return STATUS_SUCCESS;
}



[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2023-5-11 12:28 被PeterZheng编辑 ,原因: 添加图片
收藏
免费 0
支持
分享
最新回复 (12)
雪    币: 12848
活跃值: (9142)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
2

msdn:如果调用方将 AllocateDestinationString 设置为 TRUE,则例程会将 DestinationString 的 Buffer 成员替换为它分配的缓冲区的指针。 即使例程返回错误状态代码,也可以覆盖旧值。

可能RtlAnsiStringToUnicodeString内部调ExAlloc分配内存的时候由于verifier设定,随机分配失败导致Buffer的值被覆盖成不知道什么东西了(可能是栈上的随机数据?),导致unload的时候显示Buffer是无效内存


你最好检查一下RtlAnsiStringToUnicodeString的返回值是不是STATUS_SUCCESS

最后于 2023-5-10 22:22 被hzqst编辑 ,原因:
2023-5-10 22:21
0
雪    币: 1110
活跃值: (1305)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

已测试,但看起来RtlAnsiStringToUnicodeString返回值是正常的,而且就算返回失败,紧接在它后面的MmIsAddressValid也应该失败。


(正文代码已更新)


2023-5-11 10:41
0
雪    币: 6527
活跃值: (4321)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
4
加载和卸载在同一线程里面弄的吗?
2023-5-11 11:17
0
雪    币: 12848
活跃值: (9142)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
5
yimingqpa 加载和卸载在同一线程里面弄的吗?
正常途径加载卸载的话都是插workitem到workerthread里弄的,但是题目这个问题我觉得跟线程没什么关系,他这块内存又不是给某个特定进程分配的
2023-5-11 11:29
0
雪    币: 341
活跃值: (1005)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
https://blog.csdn.net/r250414958/article/details/108561713
MmIsValidAddress分析
2023-5-11 11:47
0
雪    币: 6527
活跃值: (4321)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
7
chatgpt:
在您的代码中,当开启了Verifier.exe的 "Force IRQL Checking" 选项时,您在驱动程序卸载函数DriverUnload中调用了MmIsAddressValid函数来检查g_ustr.Buffer的内存是否可访问,但返回值却为FALSE。而在未开启 "Force IRQL Checking" 选项时,MmIsAddressValid函数返回值为TRUE。这可能是由于 "Force IRQL Checking" 选项引起的内核地址空间访问权限的变化导致的。

在驱动程序的加载函数DriverEntry中,您成功地使用RtlAnsiStringToUnicodeString函数将一个ANSI_STRING类型的字符串转换为一个UNICODE_STRING类型的字符串,并为其分配了足够的内存空间。但在驱动程序卸载函数DriverUnload中,您使用MmIsAddressValid函数来检查g_ustr.Buffer的内存是否可访问,这可能是不必要的,因为您已经确定该内存空间是可用的。

根据我的测试,当我在Verifer.exe中启用了 "Force IRQL Checking" 选项时,MmIsAddressValid函数返回FALSE,因为此时驱动程序在卸载过程中尝试访问了非法的内存地址空间。这是因为,Windows内核在卸载驱动程序时会释放驱动程序所占用的所有内存空间,包括驱动程序中使用的所有内存空间。因此,当您在卸载过程中访问驱动程序中已释放的内存空间时,MmIsAddressValid函数将会返回FALSE。

因此,我建议您在卸载驱动程序时不要再访问g_ustr.Buffer指针,因为该内存空间已经被释放,并且您已经确定它在加载驱动程序时是可用的。如果您需要访问该内存空间中的数据,请在驱动程序退出之前将数据保存到另一个内存空间中。

希望这可以帮助您解决问题。如果您需要更多的帮助,请随时提供更多的信息和代码片段。
2023-5-11 13:33
0
雪    币: 12848
活跃值: (9142)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
8
PeterZheng 已测试,但看起来RtlAnsiStringToUnicodeString返回值是正常的,而且就算返回失败,紧接在它后面的MmIsAddressValid也应该失败。(正文代码已更新)
PVOID __fastcall ExpAllocateStringRoutine(SIZE_T NumberOfBytes)
{
  return ExAllocatePoolWithTag(PagedPool, NumberOfBytes, 0x67727453u);
}

看了下6楼又去看了下IDA,这下应该破案了
2023-5-12 08:43
0
雪    币: 405
活跃值: (2260)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
因为是分配得分页内存。你自己使用非分页内存分配内存,然后用第三个FALSE参数取调用RtlAnsiStringToUnicodeString试试。
2023-5-12 10:43
0
雪    币: 3738
活跃值: (3872)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10

MmIsValidAddress对分页内存判断不正确的。。。应该尽量避免调用这个函数!

最后于 2023-5-12 10:49 被fengyunabc编辑 ,原因:
2023-5-12 10:49
0
雪    币: 1110
活跃值: (1305)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
所以原因是由于verifier导致分页内存被换出,而MmIsAddressValid只检查内存页面的P位,导致不识别分页内存被换出的情况?
2023-5-12 11:37
0
雪    币: 1110
活跃值: (1305)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12

参考WRK复原了MmIsAddressValid函数,但是发现在UnloadRoutine的时候,PTE页的P位和A2位都被置0,貌似不是页面被换出的情形?


  • DriverEntry() 中的MyMmIsAddressValid->MiGetPteAddress (Address)->PointerPte:

          

  • DriverUnload() 中的MyMmIsAddressValid->MiGetPteAddress (Address)->PointerPte:

          


#include <ntifs.h>

UNICODE_STRING g_ustr = {0};

typedef struct _MMPTE_HARDWARE {
    unsigned long long P         :  1; // present (1 = present)
    unsigned long long RW        :  1; // read/write
    unsigned long long US        :  1; // user/supervisor
    unsigned long long PWT       :  1; // page-level write-through
    unsigned long long PCD       :  1; // page-level cache disabled
    unsigned long long A         :  1; // accessed
    unsigned long long Reserved  :  1; // dirty
    unsigned long long PS        :  1; // page size (0 = 4-KB page)
    unsigned long long G         :  1; // global page
    unsigned long long A1		 :  1; // available 1 aka copy-on-write
    unsigned long long A2		 :  1; // available 2/ is 1 when paged to disk
    unsigned long long A3		 :  1; // available 3
    unsigned long long PFN       : 28; // page-frame number
    unsigned long long reserved1 : 12; // reserved
    unsigned long long WS        : 11; // SoftwareWsIndex
    unsigned long long NE        :  1; // NoExecute
} MMPTE_HARDWARE, *PMMPTE_HARDWARE;

typedef struct _MMPTE {
    union  {
        ULONG_PTR Long;
        MMPTE_HARDWARE Hard;
    } u;
} MMPTE, *PMMPTE;

#define PAGE_SHIFT 12L

#define PXE_BASE          0xFFFFF6FB7DBED000UI64
#define PXE_SELFMAP       0xFFFFF6FB7DBEDF68UI64
#define PPE_BASE          0xFFFFF6FB7DA00000UI64
#define PDE_BASE          0xFFFFF6FB40000000UI64
#define PTE_BASE          0xFFFFF68000000000UI64

#define PTE_PER_PAGE 512
#define PDE_PER_PAGE 512
#define PPE_PER_PAGE 512
#define PXE_PER_PAGE 512

#define PTI_MASK_AMD64 (PTE_PER_PAGE - 1)
#define PDI_MASK_AMD64 (PDE_PER_PAGE - 1)
#define PPI_MASK (PPE_PER_PAGE - 1)
#define PXI_MASK (PXE_PER_PAGE - 1)

#define VIRTUAL_ADDRESS_BITS 48
#define VIRTUAL_ADDRESS_MASK ((((ULONG_PTR)1) << VIRTUAL_ADDRESS_BITS) - 1)

#define PTI_SHIFT 12
#define PDI_SHIFT 21
#define PPI_SHIFT 30
#define PXI_SHIFT 39
#define PTE_SHIFT 3

#define MiGetPxeOffset(va) ((ULONG)(((ULONG_PTR)(va) >> PXI_SHIFT) & PXI_MASK))

#define MiGetPxeAddress(va)   ((PMMPTE)PXE_BASE + MiGetPxeOffset(va))

#define MiGetPpeAddress(va)   \
    ((PMMPTE)(((((ULONG_PTR)(va) & VIRTUAL_ADDRESS_MASK) >> PPI_SHIFT) << PTE_SHIFT) + PPE_BASE))

#define MiGetPdeAddress(va)  \
    ((PMMPTE)(((((ULONG_PTR)(va) & VIRTUAL_ADDRESS_MASK) >> PDI_SHIFT) << PTE_SHIFT) + PDE_BASE))

#define MiGetPteAddress(va) \
    ((PMMPTE)(((((ULONG_PTR)(va) & VIRTUAL_ADDRESS_MASK) >> PTI_SHIFT) << PTE_SHIFT) + PTE_BASE))

#define MI_PDE_MAPS_LARGE_PAGE(PDE) ((PDE)->u.Hard.PS == 1)


BOOLEAN MyMmIsAddressValid(PVOID Address) {
    ULONG_PTR StartAddress = (ULONG_PTR)Address;
    PMMPTE PointerPte = NULL;

    UINT_PTR kernelbase = 0x7fffffffffffffffULL;
    UINT_PTR toppart = 0;
    if (!StartAddress) {
        return FALSE;
    }

    // cannonical check. Bits 48 to 63 must match bit 47
    toppart = (StartAddress >> 47);
    if (toppart & 1) {
        // toppart must be 0x1ffff
        if (toppart != 0x1ffff)
            return FALSE;
    } else {
        // toppart must be 0
        if (toppart != 0)
            return FALSE;
    }

    PointerPte = MiGetPxeAddress (Address);
    if (PointerPte->u.Hard.P == 0) {
        return FALSE;
    }

    PointerPte = MiGetPpeAddress (Address);
    if (PointerPte->u.Hard.P == 0) {
        return FALSE;
    }

    PointerPte = MiGetPdeAddress (Address);
    if (PointerPte->u.Hard.P == 0) {
        return FALSE;
    }

    if (MI_PDE_MAPS_LARGE_PAGE (PointerPte)) {
        return TRUE;
    }

    PointerPte = MiGetPteAddress (Address);
    if (PointerPte->u.Hard.P == 0) {
        return FALSE;
    }

    if (MI_PDE_MAPS_LARGE_PAGE (PointerPte)) {
        return FALSE;
    }

    return TRUE;
}

VOID DriverUnload(PDRIVER_OBJECT pDrvObj) {
    /*
     * 当开启verifier的Force IRQL Checking时"MmIsAddressValid"会返回False,否则返回True
     */
    KIRQL irql = KeGetCurrentIrql();
    DbgPrint("Unload IRQL [%d] \n", irql);
    
    DbgBreakPoint();
    DbgPrint("Ustr --> [%wZ][%d] \n", &g_ustr, MyMmIsAddressValid(g_ustr.Buffer));
    RtlFreeUnicodeString(&g_ustr);
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pUstrRegPath) {
    NTSTATUS status = STATUS_SUCCESS;
    ANSI_STRING ansiString;
    RtlInitAnsiString(&ansiString, "abbccddee");
    
    pDrvObj->DriverUnload = DriverUnload;

    status = RtlAnsiStringToUnicodeString(&g_ustr, &ansiString, TRUE);
    DbgPrint("RtlAnsiStringToUnicodeString return [0x%X] \n", status);

    if (!NT_SUCCESS(status)) {
        return STATUS_UNSUCCESSFUL;
    }

    /*
     * 此处不影响,"MmIsAddressValid"的返回值依然是True
     */
    DbgBreakPoint();
    DbgPrint("Ustr --> [%wZ][%d] \n", &g_ustr, MyMmIsAddressValid(g_ustr.Buffer));

    return STATUS_SUCCESS;
}


2023-5-12 12:14
0
雪    币: 1641
活跃值: (3601)
能力值: (RANK:15 )
在线值:
发帖
回帖
粉丝
13
有问题的应该不是MmIsAddressValid,而是RtlAnsiStringToUnicodeString,Verifier会把他用IAT HOOK替换成VerifierRtlAnsiStringToUnicodeString
2023-5-12 13:45
0
游客
登录 | 注册 方可回帖
返回
//