关于VAD的两种内存隐藏方式
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)隐藏效果