0
点赞
收藏
分享

微信扫一扫

Win32 驱动隐藏进程内存

原理

在进程的 _EPROCESS 中有一个 _RTL_AVL_TREE 类型的 VadRoot 成员,它是一个存放进程内存块的二叉树结构,如果我们找到了这个二叉树中我们想要隐藏的内存,直接将这个内存在二叉树中 “抹去”(其实是让上一个节点的 EndingVpn 指向下个节点的 EndingVpn ,让两个节点融合) 就可以达到隐藏的效果。

Win32 驱动隐藏进程内存_系统进程

过程

比如你在代码中申请了两块内存:

int main()
{

LPVOID p1 = VirtualAlloc(NULL, 0x10000, MEM_COMMIT, PAGE_READWRITE);
LPVOID p2 = VirtualAlloc(NULL, 0x10000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
printf("p1 = %p / p2 = %p", p1, p2);


getchar();
return 0;
}

这样打印出来:

Win32 驱动隐藏进程内存_系统进程_02

找到这个进程的 _EPROCESS ,拿到它对应的 VadRoot:

Win32 驱动隐藏进程内存_结点_03

使用 !vad 命令打印所有的节点,这里我们看到 windbg 中的值和进程中分配的内存地址并不完全一样,这是因为 x86 cpu 默认内存页大小 = 4k(也就是 0x1000),所以这里还要再乘以 0x1000 才是真正的内存地址。

Win32 驱动隐藏进程内存_系统进程_04

代码实现

以下代码来自:

#include <ntddk.h>


//---------------------//
// MMVAD结构体简单定义 //
//---------------------//
typedef struct _MMVAD {
ULONG StartingVpn;
ULONG EndingVpn;
struct _MMVAD * Parent;
struct _MMVAD * LeftChild;
struct _MMVAD * RightChild;
}MMVAD,*PMMVAD;



VOID Unload(IN PDRIVER_OBJECT pDriverObject) {
DbgPrint("Driver UnLoad!");
}

//-----------//
// 遍历VAD树 //
//-----------//
PMMVAD vad_enum(PMMVAD pVad,ULONG target_StartingVpn) {

//---------------------------//
// 遍历目标VAD,并返回其指针 //
//---------------------------//
if (pVad) {
if (target_StartingVpn == pVad->StartingVpn) {
_asm int 3
return pVad;
}
else {
if (pVad->LeftChild) {
PMMVAD p1 = vad_enum(pVad->LeftChild, target_StartingVpn);
// 如果结点不为空,则直接返回就好。
// 否则继续判断其右子树结点。
if (p1)
return p1;
}
if (pVad->RightChild) {
PMMVAD p2 = vad_enum(pVad->RightChild, target_StartingVpn);
if (p2)
return p2;
}
return NULL;
}
}
return NULL;
}

//-------------------------------------------------------------//
// 在内核中进程遍历的原理就是先获取系统进程EPROCESS结构 //
// 然后依照其链表来获取其他的进程 //
// 依次遍历出来 //
//-------------------------------------------------------------//
NTSTATUS process_enum() {

PEPROCESS pEprocess = NULL; // 得到系统进程地址
PEPROCESS pFirstEprocess = NULL;
ULONG ulProcessName = 0; // 字符串指针,指向进程名称
ULONG ulProcessID = 0; // 进程ID
ANSI_STRING target_str; // 带检测进程的名称
ANSI_STRING ansi_string; //
ULONG VadRoot;

//----------------------------//
// 得到当前系统进程的EPROCESS //
//----------------------------//
pEprocess = PsGetCurrentProcess();
if (pEprocess == NULL) {
DbgPrint("获取当前系统进程EPROCESS错误..");
return STATUS_SUCCESS;
}
DbgPrint("pEprocess addr is %x0x8\r\n", pEprocess);
pFirstEprocess = pEprocess;

while (pEprocess) {

ulProcessName = (ULONG)pEprocess + 0x174;
ulProcessID = *(ULONG*)((ULONG)pEprocess + 0x84);
VadRoot = *(ULONG*)((ULONG)pEprocess + 0x11c);

//--------------------------------------//
// 将目标进程与当前进程的进程名进行对比 //
//--------------------------------------//
RtlInitAnsiString(&ansi_string, (PCSTR)ulProcessName);
RtlInitAnsiString(&target_str, "test.exe");
if (RtlEqualString(&ansi_string, &target_str, TRUE)) {
DbgPrint("检测到进程字符串,%x", ulProcessID);

PMMVAD p1 = vad_enum((PMMVAD)VadRoot,0x3a0); // 遍历第一个结点
PMMVAD p2 = vad_enum((PMMVAD)VadRoot, 0x3b0); // 遍历找到第二个结点
_asm int 3
if(p1 && p2)
p1->EndingVpn = p2->EndingVpn; // 将第二个结点完全隐藏起来


return STATUS_SUCCESS;
}
pEprocess = (PEPROCESS)(*(ULONG*)((ULONG)pEprocess + 0x88) - 0x88);
if (pEprocess == pFirstEprocess || *(ULONG*)((ULONG)pEprocess + 0x84) <= 0) {
DbgPrint("遍历结束!未检测到进程ID!\r\n");
break;
}
}
return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING registeryPat) {
DbgPrint("Driver Loaded!");
pDriverObject->DriverUnload = Unload;
process_enum();
return STATUS_SUCCESS;
}

版权声明:本博客文章与代码均为学习时整理的笔记,文章 [均为原创] 作品,转载请 [添加出处] ,您添加出处是我创作的动力!





举报

相关推荐

0 条评论