首页
社区
课程
招聘
[推荐]windows VT层内存隐藏 让反作弊驱动当傀儡(附实现和检测思路)
发表于: 2025-10-23 02:01 3338

[推荐]windows VT层内存隐藏 让反作弊驱动当傀儡(附实现和检测思路)

2025-10-23 02:01
3338

我习惯先讲废话 不想看废话的往下拉


事情是这样的 我呢 理论应该上大二了 但是因为玉米证  和 叫驴 就喜欢研究这些东西玩(现在高三 但是在家自学 准备上个大专算了 摆烂 可能也不准备上过学 去送外卖也可以)


哎 扯多了 

上面不重要


有一天 我的朋友发来一张聊天截图

  

这陌生的UI界面 一看就是T某G啊  


哦 这不重要   这真是我朋友奥 我没有tg... 


哦这也不重要 


一开始我看到他说VT为所欲为 我就知道 不过是EPT HOOK了内核拷贝内存的函数  这不两年前就有人这么做了吗

 [原创]逆向分析某VT加持的无畏契约纯内核挂-软件逆向-看雪论坛-安全社区|非营利性质技术交流社区

直到我看到后面 我朋友的朋友说 是傀儡在ace-base上的..

我单走一个6 


讲真我听了这个效果 我一下就知道怎么搞了  抄作业这一块我还是很在行的哈哈哈./


不保证次外挂的思路和我的思路一样  因为是朋友的朋友我不好意思要样本 要来了我也不想分析 分析了我也不知道怎么贴图 然后他这个是有映射驱动的 所以我觉得好麻烦 反正也有思路 直接写吧 写完发看雪 还能给各位当茶余饭后的乐子看 哈哈哈


我的实现方法

那可太简单了/.

"写完了回来备注一下 以下说的vt驱动 代表整个主要驱动 不单止vt部分代码"


周之所众 今年腾讯游戏安全大赛的决赛就用了 intel的vt ept技术// 然后当时研究了一下 vt(我没报名奥 我逆向能力不到位)


Extended Page Table

这个东西吧 主打一个 眼见为虚  执行为实



如果第一次听到这个词 不要慌 我们只需要知道 三 件事  

1.开启ept后 你可以单独去设置pte的内存属性  

2.当GUEST要执行这个页的时候 但是此时他的 执行属性x=0 那就会触发ept 违规 读写同理

3.违规后你可以修改属性


是不是很简单

好了 你已经学会ept hook了 


把大象塞进冰箱分三步 那 ept hook也分三步

  1. 创建一个连续的物理页(不连续咋办 不连续上转转回收了 然后用MmAllocateContiguousMemorySpecifyCache申请) 然后正常写入jmp 或者int3 或者 别的指令

  2. 修改read=1;write=1; execute=0;

  3. 当ept执行违规的时候 把page_frame_number改成我们申请出来并且做了jmp的页表上 再把执行放开 读写关闭

            读写违规的时候把执行关闭 读写放开 pfn改原来的页 

是不是巨简单


那无痕隐藏驱动不过只是epthook的进阶罢了

 那我来说说我的实现思路

 首先 你需要一个rapper 啊不 mapper  说错了 

然后对这个mapper(我直接拿GS那个开源的驱动改奥 懒得写)改造一下 


先暂停掉傀儡驱动的检测 避免他线程的执行影响到我们的执行 (如果你能找一个傀儡驱动 没用回调没用线程  或者把你的驱动程序最小化 塞到他加载完毕之后就没有再执行的区域的话 就可以不用操作这一步 ) 可能那个wg不是这么干的 我不知道 但我懒得去找合适的本体了 我就也用axe-base吧(找了一圈没找到合适的 发现就这个wg用的axe很合适 因为比较大.. 我就喜欢大的 哈哈哈  我只是学习 tx别搞我 我啥也没干 我就一个臭打yj的)

// 遍历所有线程并挂起符合条件的线程
NTSTATUS
EnumerateAndSuspendDriverThreads(
	PVOID  DriverBase,
	ULONG  DriverSize
)
{
	NTSTATUS                  status;
	PVOID                     buffer = NULL;
	ULONG                     bufferSize = 0;
	PSYSTEM_PROCESS_INFORMATION pProcInfo;
	ULONG                     needed;

	// 第一遍获取缓冲区大小
	status = ZwQuerySystemInformation(
		SystemProcessInformation,
		NULL,
		0,
		&needed
	);
	if (status != STATUS_INFO_LENGTH_MISMATCH) {
		return status;
	}
	bufferSize = needed + 0x1000; // 给点额外空间
	buffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferSize, 'pstG');
	if (!buffer) {
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	status = ZwQuerySystemInformation(
		SystemProcessInformation,
		buffer,
		bufferSize,
		&needed
	);
	if (!NT_SUCCESS(status)) {
		ExFreePoolWithTag(buffer, 'pstG');
		return status;
	}

	// 遍历所有进程、线程
	pProcInfo = (PSYSTEM_PROCESS_INFORMATION)buffer;
	while (TRUE) {
		ULONG threadCount = pProcInfo->NumberOfThreads;
		PSYSTEM_THREAD_INFORMATION pThreadInfo = pProcInfo->Threads;

		for (ULONG i = 0; i < threadCount; i++, pThreadInfo++) {
			//DbgPrint("Thread ID: %d\n", pThreadInfo->ClientId.UniqueThread);
			PVOID startAddr = pThreadInfo->StartAddress;
			// 判断该线程入口地址是否在 1.sys 驱动的地址区间内
			if ((ULONG_PTR)startAddr >= (ULONG_PTR)DriverBase &&
				(ULONG_PTR)startAddr < (ULONG_PTR)DriverBase + DriverSize)
			{
				// 找到“属于该驱动”的线程
				HANDLE hThread = NULL;
				PETHREAD eThread = NULL;
				CLIENT_ID cid = pThreadInfo->ClientId;
				// 1) 通过线程 ID 找到内核的线程对象
				status = PsLookupThreadByThreadId(cid.UniqueThread, &eThread);
				if (NT_SUCCESS(status)) {
					// 2) 调用 KeSuspendThread 暂停该线程
					ULONG A=0;
					//DbgPrint("Suspending thread ID: %d %x\n", cid.UniqueThread, SuspendOnceByThreadId(cid.UniqueThread));
					 // 这里调用 NtSuspendThread 函数来挂起线程
                    // Add the declaration for NtSuspendThread to resolve the undefined identifier error.  
				/*	ObReferenceObjectByHandleWithTag(
						hThread,
						THREAD_ALL_ACCESS,
						*PsThreadType,
						KernelMode,
						0x75537350u,
						(PVOID*)&eThread,
						NULL
					);*/

					status = NtSuspendThread(eThread, &A); //你找不到NtSuspendThread ? 那这就是门槛 我怕滥用 我不告诉你我怎么找的 嘻嘻~ 

					// 3) 释放引用
					ObDereferenceObject(eThread);
				}
				// 如果需要记录或者打印,可以在此处做 log
			}
		}

		if (pProcInfo->NextEntryOffset == 0) {
			break;
		}
		pProcInfo = (PSYSTEM_PROCESS_INFORMATION)
			((PUCHAR)pProcInfo + pProcInfo->NextEntryOffset);
	}

	ExFreePoolWithTag(buffer, 'pstG');
	return STATUS_SUCCESS;
}

 



那线程暂停之后 哪怕我们直接把我们自己驱动的内存map进 这个傀儡驱动的内存区域 都没所谓 只不过这样肯定可以被扫描到嘛  内存和文件对比一下直接就标记了


申请缓冲区 储存原始的驱动程序数据  方便vt驱动去实现 页表隐藏


后面怎么执行可以自己去github搜普通的mapper看看 反正都是改装 map基本都一个样



VT驱动的实现


vt驱动在进入之后 先接受缓冲区中的数据 然后申请连续物理地址的内存 然后拷贝数据进去 然后做好标记 再把傀儡驱动地址的物理页的执行属性关掉




哦对 我很懒 所以我直接在hv上面加屎山代码 你们看个思路就好  

读写和执行二选一就好了 反正有违规都会进host 然后交给host处理
屏蔽掉的是hv自己的隐藏内存的代码 他是把pfn直接改到一个全是0的页上了 这样你读出来都是0 但是这玩意执行不了。 适合把驱动pe头隐藏了哈哈哈
该说不说 hv确实是一个很吊的项目( 虽然很多检测过不去 ) 哈哈哈 



理论上 ept违规里面 我们只需要判断是不是傀儡驱动的地址引发的 如果是 再根据具体的情况去返回pfn 和 放开权限即可

但是 假如说 某一个违规是我们自己的函数触发的呢? 例如 我们自己读取自己内存里面的数据的时候 此时 假如 我们需要访问的那个地址在傀儡驱动上此时的选线是 可以读写 不能执行 
那这个操作 就不会触发ept违规 然后我们能成功读取到一个 位于某个地址的数据 但是 这个数据不是我们自己vt驱动的数据 而是傀儡驱动此时此刻在这个地址上的数据 (看到这里点个赞呗 点赞的都是大帅逼) 那这种情况下 我们的windows  90%会因为读取到错误数据接下来执行了错误的操作 然后蓝屏 (另外10%的蓝屏几率 被货拉拉 拉走了QWQ 运气好罢了)

或者说 把整个vt驱动封装成一个shellcode? 不访问自己的内存? 不太现实 算了吧 

我掏空脑袋 还是想出来了 当我们ept违规时 我们先判断是什么违规 是读写违规还是执行违规?

以下 原始页 和原始数据页 是两个东西 大家区分一下 如果我写错了也见谅 因为一时兴起 弄完已经很晚了  所以凌晨还在写这个文章哈哈哈哈



       1. 执行违规   执行违规就代表当前的属性应该是 可读写但不可执行 那我们把pfn 改到vt驱动用的页上(理论上就是原始页 因为我们直接写进去的 申请出来的页是用作读写  那才是原始数据页) 然后 最重要的是 开启mtf 然后在mtf被触发之后 关闭mtf 并将刚才的那个被执行了的页的读写属性改成1 执行改成0 pfn改到数据页(这里有前提 就是vt驱动的这一整页都只会被执行 而不会被读取  如果会被读取那可能要把属性全部改成0 等待下次违规)

         2. 读写违规   读写违规时 判断rip是否来自 自己驱动模块(自己驱动模块就是傀儡驱动模块的内存地址)如果是的 那就是我们自己vt驱动想要访问自己的内存 那就pfn改成 原始页 也就是放行  如果不是自己的内存地址(你要是hook了别的模块 可能也是别的模块jmp进来了) 那多半是 你像隐藏的对象 访问你了 此时依旧打开 读写权限 违规改0 然后pfn改到原始数据页 然后开启mtf 同理如上



测试

懒得贴图了 反正就是dump下来 加载前后  除了一些可写的数据有极少部分不一样以外 其他只读 执行部分的代码 完全一模一样 拖进hexcompare里面去对比 看不到一点高亮的地方 总之就是很成功 除了有点卡以外 反正你们自己看着优化咯 


传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-10-24 18:24 被NMNP码农编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (9)
雪    币: 392
活跃值: (1028)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
啊 我忘了说检测思路了  就正常检测vt被 还有用那个https://bbs.kanxue.com/thread-287911.htm 这个去检测 可能也有点吃性能 但是最好的做法就是 反作弊的开发者 提前去把vt的坑位占了 嗯对! 就这样 我睡觉了 受不了了
2025-10-23 02:04
0
雪    币: 4298
活跃值: (6250)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2025-10-29 09:46
0
雪    币: 45
活跃值: (115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
目前我是双开vtd,然后是双击转换(类似双机内存),你这个思路不知道能不能过掉a某e的部分检测!现在A某e对我下发云加密后在杀的猛一点就会被t1-3-7天随机!不知道用了你这个之后会不会好
2025-10-30 07:43
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
感谢分享
2025-11-5 14:56
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6

若言 
目前我是双开vtd,然后是双击转换(类似双机内存),你这个思路不知道能不能过掉a某e的部分检测!现在A某e对我下发云加密后在杀的猛一点就会被t1-3-7天随机!不知道用了你这个之后会不会好
2025-11-5 15:14
0
雪    币: 148
活跃值: (2814)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
7
帖子没什么意思人倒是有点意思 可以私我进群 顺便一提 这思路存在有点时间了 而且不需要虚拟化 具体原因成功进来后去群里问吧
2025-11-5 18:06
0
雪    币: 210
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
章鱼C 帖子没什么意思人倒是有点意思 可以私我进群 顺便一提 这思路存在有点时间了 而且不需要虚拟化 具体原因成功进来后去群里问吧
不用虚拟化怎么实现的 别的地方来读取的时候返回傀儡驱动内存呢
2025-12-5 20:30
0
雪    币: 1898
活跃值: (542)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
666666
2025-12-14 18:05
0
雪    币: 146
活跃值: (250)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
mark
6天前
0
游客
登录 | 注册 方可回帖
返回