驱动保护进程 句柄降权 杀软自保 游戏破图标技术原理和实现

news2025/7/13 13:55:29

文章目录

      • 实现效果
      • 实现原理
      • 代码实现
      • 实现效果
      • 句柄降权对抗(实现破游戏图标和关闭杀软自保)
      • 降权对抗延伸
        • 游戏降权对抗
        • 杀软自保对抗
        • 隐藏Object钩子回调
      • 完整代码

实现效果

效果如图所示:

无法结束进程:

在这里插入图片描述

CE无图标:

在这里插入图片描述

内存无法读取

在这里插入图片描述

可以看到被保护的进程无法被结束,同时在CE内无法看到图标,并且无法被读取内存。驱动的进程保护,游戏的无图标保护内存和杀软的自我保护,实际上都是一个原理。真是一招鲜吃遍天啊。

实现原理

在这里插入图片描述

所有的实现其实都来自于一个API->ObRegisterCallbacks 。这个函数可以让你设置一个回调函数,当我们对进程或者线程的句柄进程操作时,可以在操作前和操作后拿到进程相关的信息,从而实现句柄降权,这样就可以阻止掉需要使用句柄的相关操作,例如读写内存和结束进程。

函数原型如下:

NTSTATUS ObRegisterCallbacks(
  [in]  POB_CALLBACK_REGISTRATION CallbackRegistration,
  [out] PVOID                     *RegistrationHandle
);
  1. 第一个参数是一个结构体,里面是要设置的回调的相关信息
  2. 第二个参数是返回给我们的一个句柄,在卸载的时候需要用到

我们来看下这个关键的结构体

typedef struct _OB_CALLBACK_REGISTRATION {
  USHORT                    Version;
  USHORT                    OperationRegistrationCount;
  UNICODE_STRING            Altitude;
  PVOID                     RegistrationContext;
  OB_OPERATION_REGISTRATION *OperationRegistration;
} OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;
  1. 第一个是版本号,可以用ObGetFilterVersion()函数获得
  2. 第二个是最后一个成员OperationRegistration的数量
  3. 第三个是一个Unicode 字符串,指定驱动程序的高度。(这个我也没看懂是个啥玩意)
  4. 第四个是回调函数的参数
  5. 最后一个参数比较关键,指向的是一个结构体数组,我们需要在这个结构体里面去指定操作前的回调函数和操作后的回调函数,这个结构可以设置多个。

来看一下这个结构体的成员

typedef struct _OB_OPERATION_REGISTRATION {
  POBJECT_TYPE                *ObjectType;
  OB_OPERATION                Operations;
  POB_PRE_OPERATION_CALLBACK  PreOperation;
  POB_POST_OPERATION_CALLBACK PostOperation;
} OB_OPERATION_REGISTRATION, *POB_OPERATION_REGISTRATION;
  1. 第一个是回调函数的对象类型指针,要么是PsProcessType,要么是PsThreadType
  2. 第二个是标志位
  3. 第三个是操作前回调
  4. 第四个是操作后回调

代码实现

接下来按照这个步骤来写代码

  1. 初始化OB_OPERATION_REGISTRATION结构体
  2. 初始化OB_CALLBACK_REGISTRATION结构体
  3. 调用ObRegisterCallbacks注册回调函数
  4. 编写回调函数

首先初始化OB_OPERATION_REGISTRATION结构体

OB_OPERATION_REGISTRATION obOperationRegistrations;
obOperationRegistrations.ObjectType = PsProcessType;
obOperationRegistrations.Operations |= OB_OPERATION_HANDLE_CREATE;
obOperationRegistrations.Operations |= OB_OPERATION_HANDLE_DUPLICATE;
obOperationRegistrations.PreOperation = PreOperationCallback;
obOperationRegistrations.PostOperation = NULL;

这个结构可以注册多个,我们只注册一个,处理进程句柄就够了。操作后的回调我们也不需要,直接填NULL

然后再来初始化第二个结构体OB_CALLBACK_REGISTRATION

OB_CALLBACK_REGISTRATION obCallbackRegistration = { 0 };
UNICODE_STRING altitude = { 0 };
RtlInitUnicodeString(&altitude, L"1000");
obCallbackRegistration.Version = ObGetFilterVersion();
obCallbackRegistration.OperationRegistrationCount = 1;
obCallbackRegistration.RegistrationContext = NULL;
obCallbackRegistration.Altitude = altitude;
obCallbackRegistration.OperationRegistration = &obOperationRegistrations;

最后再来调用API注册回调

ObRegisterCallbacks(&obCallbackRegistration, &g_RegistrationHandle);

这里延伸一下:ObRegisterCallbacks这个函数是有校验的

在这里插入图片描述

里面会调用MmVerifyCallbackFunction这个函数进行校验

在这里插入图片描述

校验的方法是判断 DriverSection 里面的一个字段是否包含了0x20,所以我们只要把这个字段跟0x20按位与就行了。

另外这个句柄需要放到全局变量里面,方便卸载的时候用,如果卸载驱动的时候没取消回调的注册,会蓝屏!

PVOID g_RegistrationHandle = NULL;

然后再来编写我们的回调函数

OB_PREOP_CALLBACK_STATUS
PreOperationCallback(_In_ PVOID RegistrationContext,
	_Inout_ POB_PRE_OPERATION_INFORMATION PreInfo)
{
	//获取操作的进程对象
	PEPROCESS process = (PEPROCESS)PreInfo->Object;
	
	//获取进程名
	PUCHAR processName = PsGetProcessImageFileName(process);

	if (_stricmp((char*)processName, "Notepad.exe") != 0)
	{
		//不是我们关心的进程,直接return
		return OB_PREOP_SUCCESS;
	}

	if (PreInfo->Operation == OB_OPERATION_HANDLE_CREATE)
	{
		PreInfo->Parameters->CreateHandleInformation.DesiredAccess=0;
	}

	if (PreInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE)
	{
		PreInfo->Parameters->DuplicateHandleInformation.DesiredAccess =0;
	}

	return OB_PREOP_SUCCESS;
}

在回调函数里我们判断是否是我们关心的进程,如果不是,直接把所有权限抹掉。如果要实现游戏的效果,抹掉单个权限,比如禁用掉读写,就可以借助下面这些宏

#define PROCESS_TERMINATE                  (0x0001)
#define PROCESS_CREATE_THREAD              (0x0002)
#define PROCESS_SET_SESSIONID              (0x0004)
#define PROCESS_VM_OPERATION               (0x0008)
#define PROCESS_VM_READ                    (0x0010)
#define PROCESS_VM_WRITE                   (0x0020)
#define PROCESS_DUP_HANDLE                 (0x0040)
#define PROCESS_CREATE_PROCESS             (0x0080)
#define PROCESS_SET_QUOTA                  (0x0100)
#define PROCESS_SET_INFORMATION            (0x0200)
#define PROCESS_QUERY_INFORMATION          (0x0400)
#define PROCESS_SUSPEND_RESUME             (0x0800)
#define PROCESS_QUERY_LIMITED_INFORMATION  (0x1000)
#define PROCESS_SET_LIMITED_INFORMATION    (0x2000)

实现效果

在这里插入图片描述

注册完成之后,在PCHunter的Object钩子这一栏,显示挂钩函数->PreOperation,PCHunter直接标红是可以查到的,还能看到挂钩的驱动名

在这里插入图片描述

句柄降权对抗(实现破游戏图标和关闭杀软自保)

既然知道了原理,那想要摘除就很简单了,能防自然也能攻。


OB_PREOP_CALLBACK_STATUS preCallback(
	_In_ PVOID RegistrationContext,
	_Inout_ POB_PRE_OPERATION_INFORMATION PreInfo
)
{
	if (PreInfo->Operation == OB_OPERATION_HANDLE_CREATE)
	{
		PreInfo->Parameters->CreateHandleInformation.DesiredAccess = PROCESS_ALL_ACCESS;
		PreInfo->Parameters->CreateHandleInformation.OriginalDesiredAccess = PROCESS_ALL_ACCESS;
	}
	else
	{
		PreInfo->Parameters->DuplicateHandleInformation.DesiredAccess = PROCESS_ALL_ACCESS;
		PreInfo->Parameters->DuplicateHandleInformation.OriginalDesiredAccess = PROCESS_ALL_ACCESS;
	}
	
	return OB_PREOP_SUCCESS;
}

还是一样的代码,改一下回调,不管什么进程,都把句柄权限开到最大即可。

关闭杀软:

在这里插入图片描述

游戏破图标:

在这里插入图片描述

降权对抗延伸

游戏降权对抗

既然破图标的操作这么简单粗暴,那么游戏自然不会坐视不管。在下了Object钩子之后,还会增加一层保险,就是剥夺所有线程和进程的游戏操作权限

VOID StripHandlePermission()
{
	__try
	{
		CheckDebugPort(g_FlagProcessPid);
		CheckDebugPort((HANDLE)g_StarterPid);
		PSYSTEM_HANDLE_INFORMATION_EX HandleInfo = QueryHandleTable();
		if (HandleInfo) {
			for (int i = 0; i < HandleInfo->NumberOfHandles; i++)
			{
				//7 是 process 属性
				if (HandleInfo->Information[i].ObjectTypeNumber == 7 || HandleInfo->Information[i].ObjectTypeNumber == OB_TYPE_INDEX_PROCESS || HandleInfo->Information[i].ObjectTypeNumber == OB_TYPE_INDEX_THREAD)
				{
					if (g_FlagProcessPid == (HANDLE)-1)
						break;
					if (HandleInfo->Information[i].ProcessId == (ULONG)g_FlagProcessPid || HandleInfo->Information[i].ProcessId == 4)
						continue;
					bool bCheck = ((HandleInfo->Information[i].GrantedAccess & PROCESS_VM_READ) == PROCESS_VM_READ ||
						(HandleInfo->Information[i].GrantedAccess & PROCESS_VM_OPERATION) == PROCESS_VM_OPERATION ||
						(HandleInfo->Information[i].GrantedAccess & PROCESS_VM_WRITE) == PROCESS_VM_WRITE);
					PEPROCESS pEprocess = (PEPROCESS)HandleInfo->Information[i].Object;
					if (pEprocess) {
						HANDLE handle_pid = *(PHANDLE)((PUCHAR)pEprocess + g_OsData.UniqueProcessId);
						HANDLE handle_pid2 = *(PHANDLE)((PUCHAR)pEprocess + g_OsData.InheritedFromUniqueProcessId);
						if (bCheck && (handle_pid == g_FlagProcessPid || handle_pid2 == g_FlagProcessPid)) {
							pEprocess = NULL;
							NTSTATUS status = PsLookupProcessByProcessId((HANDLE)HandleInfo->Information[i].ProcessId, &pEprocess);
							if (NT_SUCCESS(status)) {
								//DebugPrint("Full Acess Handle! pid: %d \n", HandleInfo->Information[i].ProcessId);
								PHANDLE_TABLE HandleTable = *(PHANDLE_TABLE*)((PUCHAR)pEprocess + g_OsData.ObjTable);
								if (MmIsAddressValid((void*)HandleTable)) {
									ExEnumHandleTable(HandleTable, g_isWin7 ? (DWORD64*)&StripHandleCallback_win7 : (DWORD64*)&StripHandleCallback_win10, (PVOID)HandleInfo->Information[i].Handle, NULL);
								}
								ObDereferenceObject(pEprocess);
							}
						}
					}
				}
			}
			ExFreePoolWithTag(HandleInfo, POOL_TAG);
		}
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return;
	}
	
}

这段代码实现的功能就是枚举进程列表,然后遍历所有进程的私有句柄表,查看句柄类型为7的进程对象,如果这个进程对象的ID是游戏的ID,那么说明游戏句柄被我正在遍历的这个进程打开了,就把这个句柄的读写权限抹除。这样可以起到保护游戏进程的作用

游戏完全可以创建一个线程,定时去执行这个操作。这样的话即使我们的回调恢复了句柄完整的属性,也无济无事,还需要把这个部分给处理掉。

如果把恢复权限的回调代码也定时执行的话就会出现争抢句柄权限的局面,导致游戏的内存一闪一闪,这样做并不能完全解决问题。

那么如果我们想要防止游戏定时把我们的句柄降权,可以采取的方法如下:

1.构建EPROCESS结构。将目标进程的EPROCESS结构复制到我们构建的那块内存中。
2.将新构建的EPROCESS结构中的PID、进程名清空或修改。
3.将目标进程的第一级页表的内容复制到一块新内存中,并将新构建的EPROCESS结构的DirectoryTableBase改为新内存的物理地址,从而实现CR3的替换。
4.修改我们自身进程的私有句柄表中的目标进程的句柄结构的Object为新构建的EPROCESS结构。
5.此时我们再拿着新的句柄操作的进程与目标进程是同一个进程空间,但是指向的进程结构却是不同的。

这个步骤可能不太好理解,这里我画了个图:

在这里插入图片描述

  1. 首先把游戏进程的进程结构体复制出来一份,然后把PID跟名字清空
  2. 接着把游戏进程的CR3替换成我们自己的CR3

以上两步实现了一个EPROCESS结构的替换,因为CR3变了所以不管你怎么操作原来的EPROCESS结构体,那么修改的也不是真正的游戏进程了。

由于我们的CE还需要去操作游戏内存,所以需要把这一份新的游戏进程的EPROCESS的地址放到CE进程的句柄表内,也就是第4步的操作。

这样的话就实现了句柄防止降权,也就实现了提权操作。还有一种方法是把私有句柄表的链表给摘掉,这样的话也可以防止降权。

杀软自保对抗

杀软的自保也是通过ObRegisterCallbacks实现的,这个操作基本可以防住大部分的R3的病毒。

但是在一些系统进程中会有完整的读写权限的句柄,比如lsass.execsrss.exe,只要把这个拥有完整权限的句柄复制一份,就可以直接在三环关闭杀软了。相关的代码各位可以百度,网上肯定有现成的。

隐藏Object钩子回调

既然我们下的Object回调PCHunter直接标红,那么别人查起来也不难。有这么一个操作可以隐藏自己设置的回调,就是把进程保护回调直接注册到ntoskerl.exe,让操作系统的内核模块帮我们保护进程。这样的话在PCHunter里面就是没有任何异常的,而且别人想查起来也很难。这个操作是之前看某个驱动大佬的课程学习到的,当时直呼牛逼。具体细节就不展开了。

完整代码

https://download.csdn.net/download/qq_38474570/87085676?spm=1001.2014.3001.5501

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/38278.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STC 51单片机41——汇编 串口连续发送数据

; 仿真时&#xff0c;单步运行&#xff0c;记得设置虚拟串口数据 ORG 0000H MOV TMOD ,#20H ;定时器1&#xff0c;工作方式2&#xff0c;8位重装载 MOV TH1,#0FDH ; 波特率9600 MOV TL1,#0FDH SETB TR1 ; 启动T1 MOV SCON ,#40H ; 串口工作方式1 …

[激光原理与应用-20]:《激光原理与技术》-6- 谐振腔的结构、作用、工作原理

目录 第1章 谐振腔简介 1.1 什么是谐振腔 1.2 什么是光学谐振腔 1.3 谐振腔的作用 1.4 什么是镜片镀膜 第2章 谐振腔的结构与工作原理 2.1 谐振腔的结构 2.2 谐振腔的分类 2.3 激活介质与谐振腔的工作原理 第1章 谐振腔简介 1.1 什么是谐振腔 谐振腔&#xff0c;是…

[Java反序列化]—CommonsCollections4

0x01&#xff1a; 这条链子 前半段跟CC3 一样 &#xff0c;都是动态加载字节码的过程&#xff0c;后边的构造用到了两个类&#xff0c;PriorityQueue和TransformingComparator Gadget chain:ObjectInputStream.readObject()PriorityQueue.readObject()...TransformingComparat…

如何Cisco的学员 摆脱游客登录

Cisco Packet Tracer 是一款强大的网络模拟工具&#xff0c;可用于在虚拟实验环境中练习网络、物联网和网络安全技能。您不需要任何硬件&#xff0c;即可获得实操经验&#xff01; 相信很多小伙伴在使用cisco packet tracer这个思科数据包跟踪器 - 网络仿真工具时在登录的时候…

云原生k8s的前世今生--Docker

不可修改(immutability)和幂等性(idempotent)是一致依赖困扰软件发布与运维的两个难题。以nodejs等解析执行的应用而言&#xff0c;如何控制发布后的软件不被修改运行&#xff0c;发布后&#xff0c;经常发现因为运行环境差异导致各种水土不服&#xff1a;比如应用程序对底层操…

【C++数据结构】渐近记法

渐近记法 3.1 引言 考察程序的操作计数和执行步数有两个重要的原因&#xff1a; 1)预测程序运行时间如何随着实例特征的变化而变化&#xff1b; 2)对两个功能相同的程序&#xff0c;比较它们的时间复杂度。 存在问题&#xff1a; 在使用操作计数时&#xff0c;我们关注的…

Vue生命周期--Vue实例创建过程详解

每个Vue实例在被创建时都要经过一系列的初始化过程--例如&#xff0c;需要设置数据监听、编译模板、将实例挂载到DOM并在数据变化时更新DOM等。同时在这个过程中也会运行一些叫做 生命周期钩子的函数&#xff0c;这给了用户在不同阶段添加自己的代码的机会。 生命周期图示&…

Linux入侵排查

Linux入侵排查 实验内容 当系统被黑客入侵、需短时间查找入侵来源&#xff0c;还原入侵事故过程&#xff0c;同时给出解决方案与防范措施。以下给出一些入侵排查思路 netstat -antpl 查看当前端口状态是LISTEN的端口 针对对外开放的端口&#xff0c;查看日志 先查看ssh爆破的…

OpenGL ES 学习(三) -- 绘制平面图形

上一章中&#xff0c;已经对 OpenGL 的编程语言 GLSL 和渲染模式有了一定的了解&#xff0c;今天&#xff0c;将运用之前的知识&#xff0c;完成一些平面图形的操作。效果如下&#xff1a; 如果你对 OpenGL 的基本概念或者渲染流程不清晰&#xff0c;建议先看 OpenGL ES 学习…

D. Insert a Progression(绝对值的性质)

Problem - 1671D - Codeforces 给你一个n个整数的序列a1,a2,...,an。你还得到了x个整数1,2,...,x。 每个整数可以插入序列的开头&#xff0c;也可以插入序列的结尾&#xff0c;或者插入序列的任何元素之间。 所得序列a′的得分是其中相邻元素的绝对差异之和&#xff08;∑i1n…

实验跟踪

管理和跟踪机器学习实验。 Intuition 到目前为止&#xff0c;一直在训练和评估不同的基线&#xff0c;但还没有真正跟踪这些实验。将解决这个问题&#xff0c;但定义一个适当的实验跟踪过程&#xff0c;将用于所有未来的实验&#xff08;包括超参数优化&#xff09;。实验跟踪是…

【JavaWeb】CookieSession

文章目录一.Cookie❤️1.Cookie的概念2.创建Cookie3.获取Cookie4.修改Cookie的值5.Cookie的生命周期控制6.Cookie有效路径Path的设置7.免输入用户名登录二.Session❤️1.session的概念2.Session的创建和获取3.Session域数据的存取4.Session的生命周期控制5.浏览器和Session之间…

pytorch深度学习实战lesson28

第二十八课 resnet的梯度计算&#xff08;如何缓解梯度问题&#xff09; 沐神说&#xff1a;“假设你在卷积神经网络里面&#xff0c;只要了解一个神经网络的话&#xff0c;你就了解 rest net 就行了。 rest net 是一个很简单的也是很好用的一个网络。这也是大家会经常在实际中…

OpenCV-Python小应用(六):车道线检测

OpenCV-Python小应用&#xff08;六&#xff09;&#xff1a;车道线检测前言前提条件实验环境基于霍夫变换的车道线检测参考文献前言 本文是个人使用OpenCV-Python的应用案例&#xff0c;由于水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xff…

【成为红帽工程师】第五天 NFS服务器

目录 一、NFS服务器简介 二、NFS的使用 三、客户端使用autofs自动挂载 四、相关实验 一、NFS服务器简介 NFS&#xff08;网络文件系统&#xff09;&#xff0c;是FreeBSD支持的文件系统中的一种&#xff0c;它允许网络中的计算机&#xff08;不同的计算机、不同的操作系统&…

Go学习之路:流程控制语句:for、if、else、switch 和 defer(DAY 1)

文章目录前引流程控制语句&#xff1a;for、if、else、switch 和 defer1.1、for循环语句/语法格式&#xff08;一&#xff09;1.2、for循环语句/省略前置后置语句&#xff08;二&#xff09;1.3、for循环语句/while&#xff08;三&#xff09;1.4、for循环语句/无限循环&#x…

美新科技过会:收入依赖美国、产能利用率低,林东亮等均为香港籍

11月25日&#xff0c;深圳证券交易所创业板披露的信息显示&#xff0c;美新科技股份有限公司&#xff08;下称“美新科技”&#xff09;获得上市委会议通过。据贝多财经了解&#xff0c;美新科技于2022年3月31日在创业板递交上市申请。 本次冲刺创业板上市&#xff0c;美新科技…

SpringCloudGateway--谓词(断言)

目录 一、定义 二、谓词使用 1、After 2、Before 3、Between 4、Cookie 5、Header 6、Host 7、Method 8、Path 9、Query 10、RemoteAddr 11、Weight 一、定义 SpringCloudGateway中三个重要词汇&#xff1a; 路由&#xff08;Route&#xff09;&#xff1a;配置网…

傻白入门芯片设计,芯片键合(Die Bonding)(四)

目录 一、键合( Bonding) 1. 什么是键合(Bonding)&#xff1f; 2. 芯片键合步骤 3&#xff0e;芯片拾取与放置(Pick & Place) 4. 芯片顶出(Ejection)工艺 5. 使用环氧树脂(Epoxy)实现粘合的芯片键合工艺 6. 使用晶片黏结薄膜&#xff08;DAF&#xff09;的芯片键合工…

Redis实战篇(三)秒杀

一、全局唯一ID &#xff08;1&#xff09;定义 全局ID生成器&#xff0c;是一种在分布式系统下用来生成全局唯一ID的工具&#xff0c;一半满足下列特性&#xff1a; 唯一性高可用高性能递增性安全性 为了增加ID的安全性&#xff0c;我们不直接使用Redis自增的数值&#xf…