首页
论坛
专栏
课程

[调试逆向] [原创]【很有时间系列】讲讲怎么枚举MmMapViewInSystemSpace分配的内存

hzqst 2018-9-14 17:24 1874
之前曝光了一个利用MmMapViewInSystemSpace往驱动空间所在区域分配内存的外挂
传送门:https://bbs.pediy.com/thread-230129.htm

闲的蛋疼就研究了一下怎么枚举这种方法分配出来的内存

首先明确一下虚拟内存的分配流程:
vista以上:
MmMapViewInSystemSpace ->MiMapViewInSystemSpace->MiInsertInSystemSpace->MiReserveSystemPtes(&MiSystemPteInfo, PteCount)(不同系统可能不同,但是总之是预留PTE)
xp:
MmMapViewInSystemSpace ->MiMapViewInSystemSpace->MiInsertInSystemSpace-> 从SystemSpaceBitMap找一块满足sizein64k大小的空的bitmap出来,对应到预先分配好的SystemSpaceViewStart里,直接作为分配出的地址

物理内存的分配流程:
刚才分配的PTE present位为0,访问时触发pagefault再分配物理内存←原理和NtMapViewOfSection差不多,都是只管分配VA,访问时#pf才给分配物理内存
↑这种情况下分配物理内存时不会往MmPfnDatabase中写入pfn对应的pte信息(毕竟一个section可以被多个PTE映射不是么?),导致MmGetVirtualForPhysical无法从物理地址反查虚拟地址

我们可以找个有符号的win7看一下
发现和wrk差不多,直接看wrk算了

关键代码

    Entry = (ULONG_PTR) MI_64K_ALIGN(Base) + SizeIn64k;

    Hash = (ULONG) ((Entry >> 16) % Session->SystemSpaceHashKey);

    while (Session->SystemSpaceViewTable[Hash].Entry != 0) {
        Hash += 1;
        if (Hash >= Session->SystemSpaceHashSize) {
            Hash = 0;
        }
    }

    Session->SystemSpaceHashEntries += 1;

    Session->SystemSpaceViewTable[Hash].Entry = Entry;
    Session->SystemSpaceViewTable[Hash].ControlArea = ControlArea;


那么只要遍历MmSession的SystemSpaceViewTable就能找到所有的va

entry的算法是这样的
entry=64k对齐(虚拟地址)  +  多少个64k
也就是说entry的高48位是虚拟地址,低16位是大小(指有几个64k)



从分配前的检查也可以看出,不能分配超过(65535=64k-1)个64k,否则超过低16位能保存的大小了

算出来的entry会简单计算一个hash放到 SystemSpaceViewTable 里

那么我们遍历这个表就能拿到所有的SystemSpaceView VA和大小了,遍历前记得加锁SystemSpaceViewLockPointer

PMMSESSION_WIN7 Session = (PMMSESSION_WIN7)MmSession;
for (ULONG i = 0; i < Session->SystemSpaceHashSize; i ++) {
	if (Session->SystemSpaceViewTable[i].Entry != 0) {
			PVOID BaseAddress = (PVOID)(((Session->SystemSpaceViewTable[i].Entry >> 16) << 16) | 0xFFFF000000000000ull);//Higher 48 bit of Entry is BaseAddress aligned to 64k
			SIZE_T ViewSize = (Session->SystemSpaceViewTable[i].Entry & 0xFFFFull) * 0x10000;//lowest 16 bit of Entry is SizeIn64k
			//Now you get BaseAddress and ViewSize, ViewSize is count in bytes.
		}
	}
}

32位系统的区别仅在于高16位是虚拟地址,低16位是大小,其他和64位没有任何区别 
因此以上代码甚至只需要把PMMESSION结构换成32位版本就行了

至于PMMSESSION结构,自己找个有符号的win7自提啦

MMSESSION  MmSession变量在MmMapViewInSystemSpace入口一个lea rdx就有

以上遍历兼容xp~win8.1

遍历结果:



然后看一下win10的 MiInsertInSystemSpace ,貌似变化有丶大,不用hash表而是用红黑树了


typedef struct _MMSESSION_WIN10
{
	EX_PUSH_LOCK SystemSpaceViewLock;
	PEX_PUSH_LOCK SystemSpaceViewLockPointer;
	PRTL_AVL_TREE ViewRoot;
	ULONG ViewCount;
	ULONG BitmapFailures;
}MMSESSION_WIN10, *PMMSESSION_WIN10;

ViewRoot这个 RTL_BALANCED_NODE 其实是一个侵入式数据结构,他的大小远不止sizeof( RTL_AVL_NODE )这么一点,而是

而是有0x60这么大
遍历这个红黑树需要你有 RTL_BALANCED_NODE 所在完整的结构,我随便逆了一下,只拿了几个重要的成员

typedef struct __declspec(align(8)) _MMVIEW_WIN10
{
	RTL_BALANCED_NODE SectionNode;
	ULONG64 Unkown1;
	ULONG_PTR ViewSize;
	ULONG_PTR Unkown2;
	PVOID ControlArea;
	PVOID FileObject;
	ULONG_PTR Unknown3;
	ULONG_PTR Unknown4;
	PVOID SessionViewVa;
	ULONG Unknown5;
	ULONG Unknown6;
}MMVIEW_WIN10, *PMMVIEW_WIN10;

那我们遍历红黑树岂不是写个递归就完事了

要注意他写入VA的时候会或上一些奇怪的bit,我们要去掉:


void EnumSystemSpaceViewWin10(PMMVIEW_WIN10 view)
{
	PVOID BaseAddress = (PVOID)((ULONG_PTR)view->SessionViewVa & (~3));

	//now you have
	//BaseAddress
	//view->ViewSize

	PMMVIEW_WIN10 right = (PMMVIEW_WIN10)view->SectionNode.Right;

	if (right)
	{
		EnumSystemSpaceViewWin10(right);
	}

	PMMVIEW_WIN10 left = (PMMVIEW_WIN10)view->SectionNode.Left;

	if (left)
	{
		EnumSystemSpaceViewWin10(left);
	}
}


EnumSystemSpaceViewWin10((PMMVIEW_WIN10)Session->ViewRoot);

记得win10也要加锁,而且锁换成了PUSH_LOCK

然后你拿这份代码试了一下,发现在systemspaceview非常多的情况下会栈溢出爆炸
因为递归层数太多了,你需要把递归算法改成循环

修改过的循环遍历代码由于过于丑陋这里就不放了,类似于这种:(以下代码是网上抄的)
void xunhuanzhongxubianli(BinaryTreeNode * root)
{
    stack<BinaryTreeNode*> ss;
    if (root == NULL)
    {
        return;
    }

    BinaryTreeNode * pRoot = root;

    while (pRoot || ss.size() > 0)
    {
        while (pRoot)
        {
            ss.push(pRoot);
            pRoot = pRoot->m_pLeft;
        }
        BinaryTreeNode * temp = ss.top();
        ss.pop();
        cout << temp->m_nValue << " ";
        pRoot = temp->m_pRight;
    }
}

win10下遍历结果确实有丶多,递归很容易爆栈:


以上,完


[防守篇]2018看雪.TSRC CTF 挑战赛(团队赛)11月1日征题开启!

最后于 2018-9-18 14:41 被hzqst编辑 ,原因:
本主题帖已收到 1 次赞赏,累计¥1.00
最新回复 (14)
万剑归宗 1 2018-9-14 17:25
2

0

今天的很有时间系列由hzqst主持
DeepFantasy 2018-9-14 17:34
3

0

群里潜水小弟前来顶帖
xiaofu 4 2018-9-14 17:48
4

0

mark
古朴 2018-9-14 22:21
5

0

mark
kingswb 2018-9-15 06:11
6

0

学习了
cvcvxk 10 2018-9-15 09:11
7

0

还是直接暴力枚举PTES简单和谐粗暴
fengyunabc 1 2018-9-15 13:57
8

0

顶大表哥。
shayi 8 2018-9-17 23:39
9

0

写的不错,整理一下:首先讨论不同版本上加入 system VA entry 的算法,再分别给出枚举方式:对于哈希表用迭代;对于树则从根节点递归调用自身到叶节点;MiInsertInSystemSpace() 负责插入 system VA ; MiReserveSystemPtes() 负责构建出描述该 system VA  的 PTE,各司其职;这种访问 RAM 时才实际分配的懒惰算法有助于节约 PFN 数据库的空间。两个关键的全局数据结构:SystemSpaceViewTable 与 ViewRoot 。看来在微软总部雷德蒙德开发os挺有趣的


最后于 2018-9-18 13:44 被shayi编辑 ,原因:
shayi 8 2018-9-18 14:19
10

0

sxpp 1 2018-9-18 18:10
11

0

shayi 8 2018-9-18 20:09
12

0

我觉得他的 EnumSystemSpaceViewWin10() 最后少了条 return 返回到调用者。
如果没有 return ,搜索一棵树时会是这样(忽略内核栈的大小限制):


有 return 则大不相同,能够回到上个 “岔口” 继续走另一条路,从而枚举完整棵树:

shayi 8 2018-9-18 23:36
13

0

由下图可知,插入红黑树是公平算法(左右节点各有几率),而遍历红黑树是右节点深度优先算法:


hzqst 2 2018-9-19 07:41
14

0

shayi 我觉得他的 EnumSystemSpaceViewWin10() 最后少了条 return 返回到调用者。如果没有 return ,搜索一棵树时会是这样(忽略内核栈的大小限制):有 return 则大 ...
这跟return有啥关系?


我这是标准的递归遍历啊
最后于 2018-9-19 07:41 被hzqst编辑 ,原因:
shayi 8 2018-9-19 14:09
15

0

hzqst shayi 我觉得他的 EnumSystemSpaceViewWin10() 最后少了条 return 返回到调用者。如果没有 return ,搜索一棵树时会 ...
那应该是我看错了,抱歉
返回