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

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

2023-5-10 17:20
6973

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#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;
}



[注意]看雪招聘,专注安全领域的专业人才平台!

最后于 2023-5-11 12:28 被PeterZheng编辑 ,原因: 添加图片
收藏
免费
支持
分享
最新回复 (12)
雪    币: 12876
活跃值: (9332)
能力值: ( 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
活跃值: (1898)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

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


(正文代码已更新)


2023-5-11 10:41
0
雪    币: 7080
活跃值: (4936)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
4
加载和卸载在同一线程里面弄的吗?
2023-5-11 11:17
0
雪    币: 12876
活跃值: (9332)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
5
yimingqpa 加载和卸载在同一线程里面弄的吗?
正常途径加载卸载的话都是插workitem到workerthread里弄的,但是题目这个问题我觉得跟线程没什么关系,他这块内存又不是给某个特定进程分配的
2023-5-11 11:29
0
雪    币: 341
活跃值: (1020)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
https://blog.csdn.net/r250414958/article/details/108561713
MmIsValidAddress分析
2023-5-11 11:47
0
雪    币: 7080
活跃值: (4936)
能力值: ( 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
雪    币: 12876
活跃值: (9332)
能力值: ( 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
活跃值: (2700)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
因为是分配得分页内存。你自己使用非分页内存分配内存,然后用第三个FALSE参数取调用RtlAnsiStringToUnicodeString试试。
2023-5-12 10:43
0
雪    币: 4038
活跃值: (4312)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10

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

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

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


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

          

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

          


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#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
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册