之前曝光了一個利用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下遍歷結果確實有丶多,遞迴很容易爆棧:
以上,完。
原文作者:hzqst
原文連結:https://bbs.pediy.com/thread-246843.htm
轉載請註明:轉自看雪學院
看雪閱讀推薦:
1、[翻譯] 利用DNS重繫結攻擊專用網路
2、[翻譯]利用機器學習檢測惡意PowerShell
3、[原創]看雪.京東 2018CTF 第十五題 智慧裝置 Writeup
4、[原創]淺談編碼與記憶體----自我總結與經驗分享
5、[翻譯]radare2高階