首页
社区
课程
招聘
[原创]Windows内存篇Ⅱ x64内核内存布局的迁移演变
2020-10-25 05:52 15484

[原创]Windows内存篇Ⅱ x64内核内存布局的迁移演变

2020-10-25 05:52
15484

前言


 

内存扫描一直都是比较根本的威胁检测手法,对于顽固型木马外挂而言,依赖于操作系统API相关的传统检测方式,模块扫描,窗口扫描,进程扫描以及文件扫描等都显得较为吃力,应用层而言如此,到内核层亦是如此。对于x86内核而言,0x80000000~0xFFFFFFFF 2GB内存扫描检测不是什么难事,因为整个区域就这么大。但是对于x64内核空间而言,就没有这么简单,尽管64位地址高16位一直都是保留字节0xffff,48位地址虚拟内存总量也大的有些离谱。或许有人会说物理内存就这么大,现在见的比较大的是16GB、32GB内存条,直接扫描物理内存不就好了。起初我也这么想,但是最主要的是要访问物理内存本质上需要通过虚拟内存经过页表转换到MMU单元才能完成对物理页的访问,而且抛开这个层面,尽管可以直接访问物理内存最终我们也是需要转为虚拟内存地址。那么本篇就来探索下从Win7开始到Win10 x64内核的虚拟地址空间布局以及经历了哪些更新变化。


项目传送门

黑暗之门


一. WIN7/WIN8 x64固定布局


 

WIN7相关资料也比较多,WIN8总体上与WIN7很像,就放在一起介绍了。推荐下面两篇文章

 

原文版本Kernel Virtual Address Layout
翻译版本内核虚拟地址空间布局

 

引用下这张图

  • PTE Space 顾名思义页表所在区域
  • Hyper Space 姑且称为超区域,主要是操作系统做中转用的,不同页表场景下需要用这块区域临时映射;
  • Shared System Page 共享内存空间,会映射到每一个进程空间,应用层地址即可访问内核层数据;
  • System Cache Working Set 系统缓存工作集,没怎么研究过,还有个SystemCache区域,老容易搞混...
  • Initial Loader Mappings 最初的加载器映射区域, 由加载器初始化。在系统引导阶段,winload.exe 将 NTOSKRNL.EXE,HAL.DLL,以及内核调试器 DLL(KDCOM, KD1394, KDUSB)加载到此区域。该区域还包含了空闲线程的栈,DPC(延迟过程调用)的栈,以及 KPCR(内核处理器控制区,Kernel Processor Control Region)和 Idle 线程的数据结构。
  • Sys PTEs 系统页表条目区域 重点哦,MDL映射的虚拟内存,驱动映像都在这里
  • Paged Pool Area 分页内存区域 注意区分SpecialPagedPool。
  • Dynamic Kernel VA Spacce 动态内存区域 MiVaSystemCache(注意前面还有一个缓存工作集), MiVaSpecialPoolPaged(特殊池,特殊池还会细分PagedPool与NonpagedPool)
  • PFN Database PFN 数据库 别的我不知道,MmGetVirtualForPhysical找虚拟地址的时候访问PFN数据库容易蓝屏
  • Non-paged Pool 非分页内存区域 不会被倒换到磁盘空间,只要有物理内存映射,只要不被申请者释放一般都很稳定,注意这个地址需要获取!

那我们看看怎么获取MmNonPagedPoolStart与MmNonpagedPoolEnd吧:
固定思路先从MiInitializeNonPagedPool函数找到未导出变量MmNonPagedPoolStart,发现他在MiCreatePfnDatabase里初始化
图片描述

 

自己算不太现实,有几个因子也不好确定。查看MmNonpagedPool的引用发现数据段上有个引用:
图片描述

 

最上面有个KdDebuggerDataBlock好像跟调试有关,发现导出函数KeCapturePersistentThreadState有用到这个,记得Blackbone有用到这个函数,可以获取一堆未导出变量地址

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
#ifndef _WIN64
#define DUMP_BLOCK_SIZE        0x20000
#else
#define DUMP_BLOCK_SIZE        0x40000
#endif
 
#ifndef _WIN64
#define KDDEBUGGER_DATA_OFFSET 0x1068
#else
#define KDDEBUGGER_DATA_OFFSET 0x2080
#endif
 
 
VOID InitializeDebuggerBlock()
{
    CONTEXT context = { 0 };
    context.ContextFlags = CONTEXT_FULL;
    RtlCaptureContext(&context);
 
    PDUMP_HEADER dumpHeader = (PDUMP_HEADER)ExAllocatePoolWithTag(NonPagedPool, DUMP_BLOCK_SIZE, MMS_POOL_TAG);
    if (dumpHeader)
    {
        KeCapturePersistentThreadState(&context, NULL, 0, 0, 0, 0, 0, dumpHeader);
 
        g_KdDebuggerDataBlock = dumpHeader->KdDebuggerDataBlock;
        RtlCopyMemory(&g_KdBlock, (PUCHAR)dumpHeader + KDDEBUGGER_DATA_OFFSET, sizeof(g_KdBlock));
 
        ExFreePool(dumpHeader);
    }
}

dumpHeader->KdDebuggerDataBlock就是KdDebuggerDataBlock地址,另外在0x2080偏移处还有另外一份Copy

 

那么拿到内存区域,就可以扫描一些自己想要的东西了,我最想扫的就是PG代码了...... 也可以扫隐藏驱动,隐藏进程
这里实际的应用我就不再做阐述了,简单的做个验证吧,找了几种可以在内核申请内存的方式:

1
2
3
4
5
6
7
8
VOID MmsTestAllocateMemory()
{
    MmsTestAllocatePagedPoolMemory();
    MmsTestAllocateNonPagedPoolMemory();
    MmsTestMapViewInSystemSpace();
    MmsTestAllocateMDLMemory();
    MmsTestAllocateContiguousMemory();
}

图片描述

 

可以得出
MmMapViewInSystemSpace是直接映射到System Ptes区域,与驱动映像属于同一块区域;
MDL申请出来的虚拟内存也是在System Ptes区域;
MmAllocateContiguousMemory申请的连续非分页物理内存 对应的虚拟内存空间 也是在SystemPtes区域。


二. WIN8.1~WIN10 TH2(10586) x64 内核内存布局


 

说起这个阶段,印象最为深刻的就是这两个函数了
MiInitializeDynamicRegion 与 MiInitializeDynamicBitmap

 

MiInitializeDynamicRegion看着就以为好多区域都动态了,不再静态了,实际上呢?
图片描述
图片描述
图片描述

 

就拿这三个区域说事,唬人还是巨硬强。
不过 引入的这两个结构管理内存区域,算是很值得称赞的,内存块全部用Bitmap管理,优化了性能(_MI_DYNAMIC_BITMAP Win8.1没有符号)

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
//0x68 bytes (sizeof)
struct _MI_SYSTEM_PTE_TYPE
{
    struct _RTL_BITMAP_EX Bitmap;                                           //0x0
    struct _MMPTE* BasePte;                                                 //0x10
    ULONG Flags;                                                            //0x18
    enum _MI_SYSTEM_VA_TYPE VaType;                                         //0x1c
    ULONG* FailureCount;                                                    //0x20
    ULONG PteFailures;                                                      //0x28
    union
    {
        ULONGLONG SpinLock;                                                 //0x30
        struct _FAST_MUTEX* GlobalMutex;                                    //0x30
    };
    struct _MMSUPPORT* Vm;                                                  //0x38
    volatile ULONGLONG TotalSystemPtes;                                     //0x40
    ULONGLONG Hint;                                                         //0x48
    struct _MI_CACHED_PTE* CachedPtes;                                      //0x50
    volatile ULONGLONG TotalFreeSystemPtes;                                 //0x58
    volatile LONG CachedPteCount;                                           //0x60
};
 
//0x50 bytes (sizeof)
struct _MI_DYNAMIC_BITMAP
{
    struct _RTL_BITMAP_EX Bitmap;                                           //0x0
    ULONGLONG MaximumSize;                                                  //0x10
    ULONGLONG Hint;                                                         //0x18
    VOID* BaseVa;                                                           //0x20
    ULONGLONG SizeTopDown;                                                  //0x28
    ULONGLONG HintTopDown;                                                  //0x30
    VOID* BaseVaTopDown;                                                    //0x38
    ULONGLONG SpinLock;                                                     //0x40
    struct _MMSUPPORT* Vm;                                                  //0x48
};

其实比较难找的还是NonPagePool区域,因为从Win8.1开始,KdDebuggerDataBlock就不再导出MmNonPagedPoolStart内核变量了,再来Win8.1开始就直接去掉了MmNonPagedPoolStart变量...真令人发指!

 

跟进去MiInitializeNonpagedPool函数看看,由于Win8.1结构体没符号,拿10240作为样例分析
图片描述

 

这个qword_140340930原名叫做MiNodeInformation,Win10把这个变量符号拿掉了。
这个函数其实是对 0x100000000000 大小的NonpagedPool内存进行等分(份数就是KeNumberNodes的值,这个值是干什么的呢?目前我还没搞懂)
那么NonpagedPool基地址是多少呢? 是 ((0x8000000 / KeNumberNodes) << 9 * Index - 0x2000000000) << 12 (Release版本优化才会这么难看)
其实地址就是 Index 为0的时候,正好是 0xFFFFE000`00000000

 

整理之后,得到

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
NTSTATUS MmsInitMemoryLayoutForWin8_1ToWin10TH2(IN OUT PDYNAMIC_DATA pData)
{
    if (!pData)
    {
        return STATUS_INVALID_ADDRESS;
    }
 
    pData->MmPteSpaceStart = (PVOID)0xFFFFF68000000000;
    pData->MmPteSpacecEnd  = (PVOID)0xFFFFF6FFFFFFFFFF;
 
    pData->MmHyperSpaceStart = (PVOID)0xFFFFF70000000000;
    pData->MmHyperSpaceEnd   = (PVOID)0xFFFFF77FFFFFFFFF;
 
    pData->MmSharedSystemPageStart = (PVOID)0xFFFFF78000000000;
    pData->MmSharedSystemPageEnd   = (PVOID)0xFFFFF78000000FFF;
 
    pData->MmSystemCacheStart = (PVOID)0xFFFFB00000000000;
    pData->MmSystemCacheEnd   = (PVOID)0xFFFFBFFFFFFFFFFF;
 
    pData->MmPagedPoolStart = (PVOID)0xFFFFC00000000000;
    pData->MmPagedPoolEnd   = (PVOID)0xFFFFCF7FFFFFFFFF;
 
    pData->MmSpecialPoolStart = (PVOID)0xFFFFCF8000000000;
    pData->MmSpecialPoolEnd   = (PVOID)0xFFFFCFFFFFFFFFFF;
 
    pData->MmSystemPtesStart = (PVOID)0xFFFFD00000000000;
    pData->MmSystemPtesEnd   = (PVOID)0xFFFFDFFFFFFFFFFF;
 
    pData->MmNonpagedPoolStart = (PVOID)0xFFFFE00000000000;
    pData->MmNonpagedPoolEnd   = (PVOID)0xFFFFF00000000000;//等分成KeNumberNodes块
 
    pData->MmDriverImageStart = (PVOID)0xFFFFF80000000000;
    pData->MmDriverImageEnd   = (PVOID)0xFFFFF87FFFFFFFFF;
 
    pData->MmSessionSpaceStart = (PVOID)0xFFFFF90000000000;
    pData->MmSessionSpaceEnd   = (PVOID)0xFFFFF97FFFFFFFFF;
 
    pData->MmDynamicVASpaceStart = (PVOID)0xFFFFF98000000000;
    pData->MmDynamicVASpaceEnd   = (PVOID)0xFFFFFA70FFFFFFFF;
 
    pData->MmPfnDatabaseStart = (PVOID)0xFFFFFA8000000000;
    pData->MmPfnDatabaseEnd   = (PVOID)((ULONG_PTR)pData->MmNonpagedPoolStart - 1);
 
    return STATUS_SUCCESS;
}

看看Win8.1 测试结果
图片描述

 

结果有些不一样,MmMapViewInSystemSpace映射的地址空间在DriverImage,而MDL与连续的物理页面映射的虚拟地址都在SystemPtes
其实从Win8.1开始,DriverImage不再纳入System Ptes空间,而是归入Initial Loader Mapping区域,统一改名为DriverImage,MapViewOfSection这种方式就会在DriverImage中申请虚拟内存空间


三. WIN10 RS1~WIN10 20H1(19041) x64 内核内存布局


 

要是说Win8.1与早期的Win10版本没有真正意义上的内存布局动态化的话,那么Win10RS1以后的就是真正的动态化了,这个动态化从调试来看不是nt内部做的初始化,是更上一层,推测应该是Hal或者是Hyper-V进行的初始化布局。

 

之前研究过这篇文章 Win10 1909 反向计算windows内核内存布局及代码实现 知道了内核有个0x100大小的Mark标记数组,可以通过数组计算每个区域的具体位置,可惜的是到Win10 2004就已经找不到Mark的踪迹了。

 

观前顾后,我找到了MiQuerySystembase函数用到的一个未导出变量
图片描述

 

我是怎么找到他的呢?很简单,还是看的MiInitializeNonpagedPool,发现用了这个数组的0号成员,很明显这个数组就是存放各种区域的地址范围的,并且Nt初始化时这个数组也早已被初始化了
图片描述

 

通过一些猜测与符号的查找,发现这个数组结构体以及宏定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
enum _MI_ASSIGNED_REGION_TYPES
{
    AssignedRegionNonPagedPool = 0,
    AssignedRegionPagedPool = 1,
    AssignedRegionSystemCache = 2,
    AssignedRegionSystemPtes = 3,
    AssignedRegionUltraZero = 4,
    AssignedRegionPfnDatabase = 5,
    AssignedRegionCfg = 6,
    AssignedRegionHyperSpace = 7,
    AssignedRegionKernelStacks = 8,
    AssignedRegionPageTables = 9,
    AssignedRegionSession = 10,
    AssignedRegionSecureNonPagedPool = 11,
    AssignedRegionSystemImages = 12,
    AssignedRegionMaximum = 13
};
 
typedef struct _MI_SYSTEM_VA_ASSIGNMENT
{
    VOID* BaseAddress;                                                      //0x0
    ULONGLONG NumberOfBytes;                                                //0x8
} MI_SYSTEM_VA_ASSIGNMENT, * PMI_SYSTEM_VA_ASSIGNMENT;

在NT内核初始化时,会将Assignment数组里的地址范围全部应用到系统中。
那么找这个数组就用MiQuerySytemBase的特征码就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
MmsScanSection(".text",
    (PCUCHAR)"\x48\x63\xC1\x48\x8D\x0D\xCC\xCC\xCC\xCC\x48\x03\xC0\x48\x8B\x04\xC1\xC3",
    0xCC,
    18,
    (PVOID)&lpTargetAddr);
 
if (!lpTargetAddr)
{
    DbgPrint("MemScanner: %s: MmsScanSection Failed\n", __FUNCTION__);
    return STATUS_NOT_FOUND;
}
 
lpMiSystemVaAssignment = (PMI_SYSTEM_VA_ASSIGNMENT)((PUCHAR)lpTargetAddr + *(PULONG)((PUCHAR)lpTargetAddr + 6) + 10);
for (ulIndex = 0; ulIndex < AssignedRegionMaximum; ulIndex++)
{
    DbgPrint("MemScanner: %s: Names:%s, BaseAddr:%I64x, Size:%I64x\n",
        __FUNCTION__,
        g_szAssignedRegionNames[ulIndex],
        lpMiSystemVaAssignment[ulIndex].BaseAddress,
        lpMiSystemVaAssignment[ulIndex].NumberOfBytes);
}

不过...
最让人头疼的事情 是...

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
Win10 RS1
enum _MI_ASSIGNED_REGION_TYPES
{
    AssignedRegionNonPagedPool = 0,
    AssignedRegionPagedPool = 1,
    AssignedRegionSystemCache = 2,
    AssignedRegionSystemPtes = 3,
    AssignedRegionUltraZero = 4,
    AssignedRegionPfnDatabase = 5,
    AssignedRegionCfg = 6,
    AssignedRegionHyperSpace = 7,
    AssignedRegionPageTables = 8,
    AssignedRegionSpecialPool = 9,
    AssignedRegionSession = 10,
    AssignedRegionWorkingSetPagedPool = 11,
    AssignedRegionWorkingSetSystemCache = 12,
    AssignedRegionSystemImages = 13,
    AssignedRegionMaximum = 14
};
 
Win10 RS2
enum _MI_ASSIGNED_REGION_TYPES
{
    AssignedRegionNonPagedPool = 0,
    AssignedRegionPagedPool = 1,
    AssignedRegionSystemCache = 2,
    AssignedRegionSystemPtes = 3,
    AssignedRegionUltraZero = 4,
    AssignedRegionPfnDatabase = 5,
    AssignedRegionCfg = 6,
    AssignedRegionHyperSpace = 7,
    AssignedRegionPageTables = 8,
    AssignedRegionSpecialPoolPaged = 9,
    AssignedRegionSpecialPoolNonPaged = 10,
    AssignedRegionSession = 11,
    AssignedRegionSystemImages = 12,
    AssignedRegionMaximum = 13
};
 
Win10 RS3
enum _MI_ASSIGNED_REGION_TYPES
{
    AssignedRegionNonPagedPool = 0,
    AssignedRegionPagedPool = 1,
    AssignedRegionSystemCache = 2,
    AssignedRegionSystemPtes = 3,
    AssignedRegionUltraZero = 4,
    AssignedRegionPfnDatabase = 5,
    AssignedRegionCfg = 6,
    AssignedRegionHyperSpace = 7,
    AssignedRegionKernelStacks = 8,
    AssignedRegionPageTables = 9,
    AssignedRegionSpecialPoolPaged = 10,
    AssignedRegionSpecialPoolNonPaged = 11,
    AssignedRegionSession = 12,
    AssignedRegionSystemImages = 13,
    AssignedRegionMaximum = 14
};
 
Win10 RS4
enum _MI_ASSIGNED_REGION_TYPES
{
    AssignedRegionNonPagedPool = 0,
    AssignedRegionPagedPool = 1,
    AssignedRegionSystemCache = 2,
    AssignedRegionSystemPtes = 3,
    AssignedRegionUltraZero = 4,
    AssignedRegionPfnDatabase = 5,
    AssignedRegionCfg = 6,
    AssignedRegionHyperSpace = 7,
    AssignedRegionKernelStacks = 8,
    AssignedRegionPageTables = 9,
    AssignedRegionSpecialPoolPaged = 10,
    AssignedRegionSpecialPoolNonPaged = 11,
    AssignedRegionSession = 12,
    AssignedRegionSystemImages = 13,
    AssignedRegionMaximum = 14
};
 
Win10 RS5
enum _MI_ASSIGNED_REGION_TYPES
{
    AssignedRegionNonPagedPool = 0,
    AssignedRegionPagedPool = 1,
    AssignedRegionSystemCache = 2,
    AssignedRegionSystemPtes = 3,
    AssignedRegionUltraZero = 4,
    AssignedRegionPfnDatabase = 5,
    AssignedRegionCfg = 6,
    AssignedRegionHyperSpace = 7,
    AssignedRegionKernelStacks = 8,
    AssignedRegionPageTables = 9,
    AssignedRegionSpecialPoolPaged = 10,
    AssignedRegionSpecialPoolNonPaged = 11,
    AssignedRegionSession = 12,
    AssignedRegionSystemImages = 13,
    AssignedRegionMaximum = 14
};
 
Win10 19H1
enum _MI_ASSIGNED_REGION_TYPES
{
    AssignedRegionNonPagedPool = 0,
    AssignedRegionPagedPool = 1,
    AssignedRegionSystemCache = 2,
    AssignedRegionSystemPtes = 3,
    AssignedRegionUltraZero = 4,
    AssignedRegionPfnDatabase = 5,
    AssignedRegionCfg = 6,
    AssignedRegionHyperSpace = 7,
    AssignedRegionKernelStacks = 8,
    AssignedRegionPageTables = 9,
    AssignedRegionSession = 10,
    AssignedRegionSystemImages = 11,
    AssignedRegionMaximum = 12
};
 
Win10 19H2
enum _MI_ASSIGNED_REGION_TYPES
{
    AssignedRegionNonPagedPool = 0,
    AssignedRegionPagedPool = 1,
    AssignedRegionSystemCache = 2,
    AssignedRegionSystemPtes = 3,
    AssignedRegionUltraZero = 4,
    AssignedRegionPfnDatabase = 5,
    AssignedRegionCfg = 6,
    AssignedRegionHyperSpace = 7,
    AssignedRegionKernelStacks = 8,
    AssignedRegionPageTables = 9,
    AssignedRegionSession = 10,
    AssignedRegionSystemImages = 11,
    AssignedRegionMaximum = 12
};
 
Win10 20H1
enum _MI_ASSIGNED_REGION_TYPES
{
    AssignedRegionNonPagedPool = 0,
    AssignedRegionPagedPool = 1,
    AssignedRegionSystemCache = 2,
    AssignedRegionSystemPtes = 3,
    AssignedRegionUltraZero = 4,
    AssignedRegionPfnDatabase = 5,
    AssignedRegionCfg = 6,
    AssignedRegionHyperSpace = 7,
    AssignedRegionKernelStacks = 8,
    AssignedRegionPageTables = 9,
    AssignedRegionSession = 10,
    AssignedRegionSecureNonPagedPool = 11,
    AssignedRegionSystemImages = 12,
    AssignedRegionMaximum = 13
};

尽管8号之前没什么变化,但是后面序号一直在变,也不知道微软程序员抽了什么风...
最后看下Win10的测试结果吧,由于序号变得快,调试信息有不准的地方我也没做修改了...

 

图片描述
结论还是与Win8.1一致,MmMapViewInSystemSpace映射在DriverImage区域,MDL与连续物理页的虚拟页映射在SystemPtes区域。

 

五点了,睡觉睡觉!


[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

最后于 2020-10-25 10:54 被FaEry编辑 ,原因:
收藏
点赞13
打赏
分享
最新回复 (18)
雪    币: 5406
活跃值: (11830)
能力值: ( LV12,RANK:312 )
在线值:
发帖
回帖
粉丝
一半人生 5 2020-10-25 07:59
2
0
雪    币: 11356
活跃值: (4939)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 1 2020-10-25 10:51
3
0

win10的nonpagedpool大小应该不是巨硬pdb所说的0x100000000000,实测在这个区域搜DriverObject只能搜出零星的几个,巨硬大概率在内存管理函数里面多做了一些手脚。
另外对于其他内存区域,想暴力搜必须考虑Transition Prototype PageFile这三种情况,Win8.1+有MmCopyMemory,但是实现复杂,很难移植到Win8及以下

最后于 2020-10-25 10:53 被hhkqqs编辑 ,原因:
雪    币: 5023
活跃值: (2521)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
FaEry 6 2020-10-25 10:53
4
0
hhkqqs win10的nonpagedpool大小应该不是巨硬pdb所说的0x100000000000,实测在这个区域搜DriverObject只能搜出零星的几个,巨硬大概率在内存管理函数里面多做了一些手脚。另 ...
为啥我搜出来好多DriverObject
雪    币: 11356
活跃值: (4939)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 1 2020-10-25 10:57
5
0
FaEry 为啥我搜出来好多DriverObject
我在windbg用!poolfind Driv 只搜到零星的几个,非常奇怪,我看了眼IoDriverObjectType上面登记的个数可是一百多个
雪    币: 3428
活跃值: (3482)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 1 2020-10-25 11:03
6
0
666,感谢分享!
雪    币: 5023
活跃值: (2521)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
FaEry 6 2020-10-25 11:04
7
0
hhkqqs 我在windbg用!poolfind Driv 只搜到零星的几个,非常奇怪,我看了眼IoDriverObjectType上面登记的个数可是一百多个

Show you my code,I don't believe windbg in some situation..


VOID EnumDriverByDriverObjectMemory()
{
    PVOID                    lpStartAddr = NULL;
    SIZE_T                   ulSize = 0;
    UNICODE_STRING           uniFuncName = { 0 };
    ULONG_PTR                lpSearchAddr = 0;
    ULONG_PTR                lpEndAddr = 0;
    ULONG                    ulEntrySize = 0;
    ULONG                    ulRemainSize = 0;
    ULONG_PTR                pDriverObject = 0;
    NTSTATUS                 status = STATUS_SUCCESS;
    PLDR_DATA_TABLE_ENTRY    pLdr = NULL;

    if (!g_ObGetObjectType)
    {
        RtlInitUnicodeString(&uniFuncName, L"ObGetObjectType");
        g_ObGetObjectType = (pfnObGetObjectType)MmGetSystemRoutineAddress(&uniFuncName);
    }

    if (g_BuildNumber >= 7600)
    {
        lpStartAddr = g_DriverObject; // 用自己的DriverObject就好,没必要全盘搜NonpagedPool
        lpStartAddr = (PVOID)((ULONG_PTR)lpStartAddr & 0xFFFFFFFF00000000);
        ulSize = 0x300000000;
    }
    else
    {
        return;
    }

    lpSearchAddr = (ULONG_PTR)lpStartAddr;
    lpEndAddr = lpSearchAddr + ulSize - sizeof(DRIVER_OBJECT);

    KdPrint(("[%s] lpSearchAddr:%p lpEndAddr:%p\n", __FUNCTION__, lpSearchAddr, lpEndAddr));

    while (TRUE)
    {
        if (lpSearchAddr + PAGE_SIZE > lpEndAddr)
        {
            ulEntrySize = (ULONG)(lpEndAddr - lpSearchAddr);
        }
        else
        {
            ulEntrySize = PAGE_SIZE;
        }

        if (!IsAddressValidLength((PVOID)lpSearchAddr, ulEntrySize))
        {
            goto NextLoop;
        }

        pDriverObject = lpSearchAddr + 0x30;
        ulRemainSize = 0x30;

        while (ulRemainSize < ulEntrySize)
        {
            POBJECT_TYPE ObjectType = KeGetObjectType((PVOID)pDriverObject);
            if (ObjectType == *IoDriverObjectType)
            {
                if (IsRealDriverObject((PDRIVER_OBJECT)pDriverObject)) // 一堆校验
                {
                    pLdr = (PLDR_DATA_TABLE_ENTRY)(((PDRIVER_OBJECT)pDriverObject)->DriverSection);
                    KdPrint(("[%s] pDriverObject:%p FullName:%wZ, DllBase:%I64x, Size:%x\n", __FUNCTION__, pDriverObject, &pLdr->FullDllName, pLdr->DllBase, pLdr->SizeOfImage));
                }

                ulRemainSize += sizeof(DRIVER_OBJECT);
                pDriverObject += sizeof(DRIVER_OBJECT);
            }
            else
            {
                ulRemainSize += 8;
                pDriverObject += 8;
            }
        }

    NextLoop:
        lpSearchAddr += PAGE_SIZE;
        if (lpSearchAddr >= lpEndAddr)
        {
            break;
        }
    }
}

最后于 2020-10-25 11:17 被FaEry编辑 ,原因:
雪    币: 11356
活跃值: (4939)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 1 2020-10-25 17:40
8
0
FaEry hhkqqs 我在windbg用!poolfind Driv 只搜到零星的几个,非常奇怪,我看了眼IoDriverObjectType上面登记的个数可是一百多个 ...
windbg这个poolfind的指令在6.x版本下能正常用,到了10.0版本不知道为啥少了好多信息。我用特征码的方法找了一遍,基本能全找出来,首先找ULONG_PTR PoolHeader,使得*(PULONGLONG)PoolHeader & 0x7FFFFFFFFF000000 == 0x7669724402000000。进一步判断(*(PULONGLONG)(PoolHeader + 0x10) == DriverObjectDirectory || *(PULONGLONG)(PoolHeader + 0x10) == FileSystemDirectory) && *(PULONGLONG)(PoolHeader + 0x60) == 0x1500004,基本能确定是一个驱动,然后PoolHeader += (((PPOOL_HEADER)PoolHeader)->BlockSize << 4)继续扫下去,兼容Vista SP0。你代码用到的ObGetObjectType不兼容Vista。
雪    币: 5023
活跃值: (2521)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
FaEry 6 2020-10-25 18:07
9
0
hhkqqs windbg这个poolfind的指令在6.x版本下能正常用,到了10.0版本不知道为啥少了好多信息。我用特征码的方法找了一遍,基本能全找出来,首先找ULONG_PTR PoolHeader,使得*( ...
对,win10windbg有点问题所以不太信他的处理结果,用poolheader比较舒服,小内存都能按tag找,头+0xC就是DriverObject了,没做vista xp的兼容了
雪    币: 11356
活跃值: (4939)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 1 2020-10-25 18:49
10
0
FaEry 对,win10windbg有点问题所以不太信他的处理结果,用poolheader比较舒服,小内存都能按tag找,头+0xC就是DriverObject了,没做vista xp的兼容了
搜poolheader虽然流程简单点,直接暴力搜效率还是比较慢的,我看了篇论文,说定位PhysicalMemoryBackupPool的Bitmap可以筛掉很多无效页面,但也要符号或特征码定位
雪    币: 5023
活跃值: (2521)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
FaEry 6 2020-10-25 19:23
11
0
hhkqqs 搜poolheader虽然流程简单点,直接暴力搜效率还是比较慢的,我看了篇论文,说定位PhysicalMemoryBackupPool的Bitmap可以筛掉很多无效页面,但也要符号或特征码定位[em_ ...
找到进bitmap的锁就可以了,目前这种方式最大的问题是访问内存时如果在MmIsAddressValid调用完内存被释放了就有蓝屏风险,lock内存也没用,lock了别人调用ExFreePool发现内存引用不为零也会KeBugCheck蓝屏。蓝瘦
雪    币: 11356
活跃值: (4939)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
hhkqqs 1 2020-10-25 22:00
12
0
FaEry 找到进bitmap的锁就可以了,目前这种方式最大的问题是访问内存时如果在MmIsAddressValid调用完内存被释放了就有蓝屏风险,lock内存也没用,lock了别人调用ExFreePool发现内 ...
毕竟是非分页内存,我一般拉高IRQL防止被切换,当然如果对方的代码也在DPC_LEVEL,照样防不住
雪    币: 5023
活跃值: (2521)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
FaEry 6 2020-10-26 00:05
13
0
hhkqqs 毕竟是非分页内存,我一般拉高IRQL防止被切换,当然如果对方的代码也在DPC_LEVEL,照样防不住[em_10]
哈哈,锁死其他核心,当前核心不允许线程切换就可以了
雪    币: 1126
活跃值: (2076)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
Oday小斯 2020-10-26 21:14
14
0
感谢分享,牛逼
雪    币: 919
活跃值: (1340)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
丿一叶知秋 2020-11-25 16:50
15
0
请问一下,MmNonPagedPoolStart不应该是在MiInitMachineDependent函数中动态计算出来的吗?
雪    币: 5023
活跃值: (2521)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
FaEry 6 2021-1-4 21:01
16
0
丿一叶知秋 请问一下,MmNonPagedPoolStart不应该是在MiInitMachineDependent函数中动态计算出来的吗?
MmNonPagedPoolStart应该是在MmPfnDataBase后面,计算出来的
雪    币: 919
活跃值: (1340)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
丿一叶知秋 2021-1-6 16:17
17
0
FaEry MmNonPagedPoolStart应该是在MmPfnDataBase后面,计算出来的
可能小弟看记错了吧,我大概记得我在看MiInitMachineDependent的时候,MmNonPagedPoolStart是固定的点(xp),然后后面根据注册表,物理内存的大小都划分虚拟空间,最后才建立的MmPfnDataBase的虚拟空间,从而挂上物理页。 
雪    币: 20
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wx_洋洋刘 2021-7-15 23:10
18
0
贴主   请看下私信
雪    币: 519
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
YEHONG 2022-8-23 16:30
19
0
请问有关于32位的内核布局吗?
游客
登录 | 注册 方可回帖
返回