驱动外挂的原理及检测手段
因为PatchGuard技术的存在导致游戏在驱动层的保护不能像以前那样通过SSDT Hook或者IDT Hook来做了,游戏厂家不能擅自关闭PatchGuard来强行Hook.这样留给驱动挂的空间就很大了
我将以一个自瞄挂的原理为例子展示驱动外挂的几种实现方式及检测手段

相同点
要想实现自瞄驱动挂基本上都是读取游戏数据然后直接操作鼠标,不同之处就是操纵鼠标的方式
下面是所有驱动挂的几个相同之处

相同点1 - 获取鼠标的驱动对象
一个自瞄驱动挂的实现首先必然需要获得鼠标的驱动对象,在任何驱动挂里应该都是一样的
可以通过ObReferenceObjectByName来获取鼠标驱动对象
其中ObReferenceObjectByName是未公开的函数,声明一下就能用.其声明如下

NTSTATUS ObReferenceObjectByName(
PUNICODE_STRING ObjectName,
ULONG Attributes,
PACCESS_STATE AccessState,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID ParseContest,
PVOID* Object
);

extern POBJECT_TYPE* IoDriverObjectType; // 这是ObjectType参数的实参
其中鼠标驱动的名称为\Driver\MouClass
那么具体获取鼠标驱动对象过程如下:

NTSTATUS status = STATUS_SUCCESS;

UNICODE_STRING mouseName;
RtlInitUnicodeString(&mouseName, L"\Driver\MouClass");
// 获取到鼠标驱动对象将保存至此
PDRIVER_OBJECT mouseDriver;

status = ObReferenceObjectByName(&mouseName, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, &mouseDriver);
if (!NT_SUCCESS(status)) {
return status;
} else {
// 获取失败了需要解引用
ObDereferenceObject(mouseDriver);
}
这样一个鼠标驱动对象就在mouseName变量中了

相同点2 - 控制驱动挂的手段
用户要想控制驱动首先要通过CreateFile来打开驱动设备
也就是说在驱动里必然先要创建好一个设备供用户打开,具体步骤如下

// 设备名和符号名的定义
#define ITRUTH_DEVICE_NAME L"\Device\iTruth_Device_20d04fe0"
#define ITRUTH_SYMB_NAME L"\DosDevice\iTruth_Device"

// 函数里设备创建过程
UNICODE_STRING dev_name;
RtlInitUnicodeString(&dev_name, ITRUTH_DEVICE_NAME);
UNICODE_STRING sddl;
// SDDL语法请参考https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/sddl-for-device-objects
RtlInitUnicodeString(&sddl, L"D:P(A;;GA;;;BU)");
// 这个请自己定,每个驱动都不能一样
GUID dev_guid = { 0x3cff2c3aL, 0x320f, 0xf5aa, “iTruth” };

// 创建设备
status = IoCreateDeviceSecure(
DriverObject,
0,
&dev_name,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&sddl,
&dev_guid,
&iTruth_Device
);

if (NT_SUCCESS(status)) {UNICODE_STRING dos_dev_name;RtlInitUnicodeString(&dos_dev_name, ITRUTH_SYMB_NAME);
// 为设备绑定符号链接,用户只能通过这个符号链接打开设备IoCreateSymbolicLink(&dos_dev_name, &dev_name);// 绑定处理函数DriverObject->MajorFunction[IRP_MJ_CREATE] = iTruth_DriverDispatch;DriverObject->MajorFunction[IRP_MJ_CLOSE] = iTruth_DriverDispatch;DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = iTruth_DriverDispatch;
}

在用户层控制驱动基本上就是DeviceIoControl函数了,此函数向指定驱动发送IO控制码(CTL_CODE),其中控制码可以由一个名为CTL_CODE的宏来定义,这个宏的声明如下

#define CTL_CODE(DeviceType, Function, Method, Access) (
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
)
第一个参数是驱动的类型,驱动挂一般就填FILE_DEVICE_UNKNOWN即可.
第二个是操作码,这个可以自已定.
第三个是传递缓冲区的方式,METHOD_BUFFERED是比较简单的方式
第四个是完成此动作需要的权限,不知道就填FILE_ANY_ACCESS
下面是IOCTL的两个例子:

#define IOCTL_ID_DBGPRINT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x700, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_ID_FUNCHOOK CTL_CODE(FILE_DEVICE_UNKNOWN, 0x701, METHOD_BUFFERED, FILE_WRITE_DATA)
用户层的CTL_CODE和内核里的应该是一样的,所以这个控制码的定义一般单独放在一个头文件内
驱动的入口函数是DriverEntry,其中第一个参数是PDRIVER_OBJECT类型的代表了当前驱动
这个参数里有一个名为MajorFunction的数组,里面包含了各种驱动处理函数
这个数组第__IRP_MJ_DEVICE_CONTROL__(0x0e)个元素是处理IO的回调函数,这个值是自己指定的
一个典型的处理IRP_MJ_DEVICE_CONTROL的函数如下

NTSTATUS DriverDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION irp_loc = IoGetCurrentIrpStackLocation(Irp);

if (DeviceObject == myDevice) {if (irp_loc->MajorFunction == IRP_MJ_CREATE || irp_loc->MajorFunction == IRP_MJ_CLOSE) {IoCompleteRequest(Irp, IO_NO_INCREMENT);return STATUS_SUCCESS;}for (PLIST_ENTRY pl = dev_list_head.Flink; pl != (PLIST_ENTRY)&dev_list_head.Flink; pl = pl->Flink) {PITRUTH_DEV_ENTRY dev_entry = CONTAINING_RECORD(pl, ITRUTH_DEV_ENTRY, dev_list);if (dev_entry->flt_dev_obj == DeviceObject) {if (irp_loc->MajorFunction == IRP_MJ_DEVICE_CONTROL) {switch (irp_loc->Parameters.DeviceIoControl.IoControlCode) {//这里可以看到我们判断当前需要的功能部分case IOCTL_ID_DBGPRINT:KdPrint((Irp->AssociatedIrp.SystemBuffer));Irp->IoStatus.Status = STATUS_SUCCESS;break;case IOCTL_ID_FUNCHOOK:KdPrint(("Kernel: Hook\n"));Irp->IoStatus.Status = STATUS_SUCCESS;break;default:IoSkipCurrentIrpStackLocation(Irp);return IoCallDriver(dev_entry->next_dev_obj, Irp);}}IoSkipCurrentIrpStackLocation(Irp);return IoCallDriver(dev_entry->next_dev_obj, Irp);}}
}

可以看到里面有根据我们的CTL_CODE来执行功能的switch语句,这个就是具体的控制原理

不同的鼠标控制手段
最简单的手段 - 通过过滤设备来控制鼠标
原理
这种手段本质上是使用名为IoAttachDeviceToDeviceStack的函数将自己创建的设备对象绑定到设备对象链中的最高层,然后使用自己Driver Dispatch回调函数来修改鼠标输入
绑定过程如下:

// 在相同点那里已经展示了鼠标驱动设备的获取方式,所以这里省略
// 获取鼠标驱动设备链中的第一个设备
targetDevice = mouseDriver->DeviceObject;
// 遍历设备链中的所有设备
while (targetDevice)
{
// 创建一个过滤设备
status = IoCreateDevice(pDriver, sizeof(DEV_EXTENSION), NULL, targetDevice->DeviceType, targetDevice->Characteristics, FALSE, &filterDevice);
if (!NT_SUCCESS(status))
{
filterDevice = targetDevice = NULL;
return status;
}
// 在这步绑定
nextDevice = IoAttachDeviceToDeviceStack(filterDevice, targetDevice);
if (!nextDevice)
{
IoDeleteDevice(filterDevice);
filterDevice = NULL;
return status;
}
targetDevice = targetDevice->NextDevice;
}
到了这步绑定好读取的分发函数那么鼠标的读取IRP就会发送到我们的驱动然后我们即可对其处理

pDriver->MajorFunction[IRP_MJ_READ] = MouseIRPMJRead;

下面是鼠标读取IRP的处理

NTSTATUS MouseIRPMJRead(PDEVICE_OBJECT pDevice, PIRP pIrp, PVOID Context)
{
UNREFERENCED_PARAMETER(pDevice);
UNREFERENCED_PARAMETER(Context);
PIO_STACK_LOCATION stack;
PMOUSE_INPUT_DATA myData;
stack = IoGetCurrentIrpStackLocation(pIrp);
if (NT_SUCCESS(pIrp->IoStatus.Status))
{
// 获鼠标盘数据
myData = pIrp->AssociatedIrp.SystemBuffer;
// 这里即可开始读取游戏数据并更改鼠标的IRP
}
if (pIrp->PendingReturned)
{
IoMarkIrpPending(pIrp);
}
return pIrp->IoStatus.Status;
}
检测手段
毕竟是在设备栈上添加自己的设备,那么只需要一个设备黑名单即可.
通过遍历设备栈只要找到了外挂创建的设备即判定非法
判断的核心代码:

// 设备对象的拓展结构
typedef struct _DEVICE_EXTENSION {
PDEVICE_OBJECT pDevice;
UNICODE_STRING ustrDeviceName;
UNICODE_STRING ustrSymLinkName;
} DEVICE_EXTENSION, * PDEVICE_EXTENSION;

// 检测代码
targetDevice = mouseDriver->DeviceObject;
// 遍历设备链中的所有设备
while (targetDevice)
{
PDEVICE_EXTENSION exdev = (PDEVICE_EXTENSION)targetDevice;
// 现在targetDevice->ustrDeviceName就是设备名了,下面即可自行判断这个设备名是否合法targetDevice = targetDevice->NextDevice;
}

比较好的方式 - Hook或直接调用MouseServiceClassCallBack
关键在于找到这个函数,寻找这个函数可以遍历设备对象也可以搜特征码
遍历设备对象的寻找目标函数的方式如下

// 全局定义MouseClassServiceCallback
typedef VOID
(*MouseClassServiceCallback) (
PDEVICE_OBJECT DeviceObject,
PMOUSE_INPUT_DATA InputDataStart,
PMOUSE_INPUT_DATA InputDataEnd,
PULONG InputDataConsumed
);
// 保存原始函数
MouseClassServiceCallback orig_MouseClassServiceCallback = NULL;

// 我们已经获取过鼠标的设备驱动保存在了mouseDriver中,现在获取鼠标的端口驱动
UNICODE_STRING mouNtName;
PDRIVER_OBJECT mouhidDriverObj;
RtlInitUnicodeString(&mouNtName, L"\Driver\Mouhid");
status = ObReferenceObjectByName(
&mouNtName,
OBJ_CASE_INSENSITIVE,
NULL,
0,
IoDriverObjectType,
KernelMode,
NULL,
&mouhidDriverObj
);
if (!NT_SUCCESS(status)) {
return status;
}
// 遍历mouclass下所有设备
PDRIVER_OBJECT targetDriverObj = mouseDriver->DeviceObject;
ULONG mouDriverStart = (ULONG)GetModlueBaseAdress(“mouclass.sys”, 0);
ULONG mouDriverSize = 0x2000;
MouseClassServiceCallback* MouSrvAddr = NULL;
while(targetDriverObj)
{
// 遍历我们先找到的端口驱动的设备扩展下的每个指针
for(PBYTE exdev = (PBYTE)mouhidDriverObj; i<4096; ++i; exdev+=sizeof(PBYTE))
{
if (!MmIsAddressValid(exdev)) {
break;
}

//如果在设备扩展中找到一个地址位于mouClass模块中,就认为这是我们要的回调函数地址
PVOID tmp = *(PVOID*)exdev;if ((tmp > mouDriverStart)&&(tmp < (PBYTE)mouDriverStart+mouDriverSize)) {orig_MouseClassServiceCallback  = (MouseClassServiceCallback)tmp;MouSrvAddr = (PVOID*)exdev;goto Done;
}

}
targetDriverObj = targetDriverObj->NextDevice;
}
Done:
// 这里就获取到了哪个函数的地址并保存到了orig_MouseClassServiceCallback中
现在我介绍那两种方式以及检测手段

Hook方式
原理
直接改函数地址即可

*MouSrvAddr = myHookFuncAddr;

这种方式比较方便的地方是不用自己构建MOUSE_INPUT_DATA

检测手段
这种方式直接检测函数地址即可

直接调用方式
原理
我们刚刚获取了函数指针那么直接调用就能使鼠标移动,麻烦的是要自己构造MOUSE_INPUT_DATA

检测手段
这种方式目前没有很好的检测手段,如果各位大佬有办法请务必让本菜见识下

杂谈
当然也是有通过Hook IDT的鼠标中断来实现的,这种方式麻烦的地方在于要为CPU里每个核心都做一遍Hook操作.而且也能简单的通过特征码的方式简单的检测出来
最重要的一点其实还在于如果做IDT Hook那么还不如直接修改空闲中断的DPL和中断程序地址来做中断提权,让我们的外挂程序有Ring0权限.我感觉这样才是更好的办法

驱动外挂的原理及检测手段(自瞄篇)相关推荐

  1. 游戏反外挂技术原理讲解

    永远在路上 没有破解不了的反外挂系统,反外挂是一个对抗过程,需要不断升级.我们反外挂小组会采取对抗方式提升防御,也会研究竞品来获取灵感.反外挂也是非常有意思的,可以学到很多很多底层知识. 善战者无赫赫 ...

  2. SYN攻击原理以及检测防范技术

    SYN攻击原理以及检测防范技术 据统计,在所有黑客攻击事件中,SYN攻击是最常见又最容易被利用的一种攻击手法.相信很多人还记得2000年YAHOO网站遭受的攻击事例,当时黑客利用的就是简单而有效的SY ...

  3. 耳机驱动调试(插拔检测与按键检测)

    耳机驱动调试(插拔检测与按键检测) 小白一枚,欢各位大佬指出错误 耳机类型判断 三段和四段.欧标和美标 现在许多设备的耳机接口都采用3.5mm的耳机接口,其中终端就是,终端可以兼容三段和四段耳机:三段 ...

  4. 网络游戏外挂编写原理

    网络游戏外挂编写原理 一. 前言 所谓游戏外挂,其实是一种游戏外辅程序,它可以协助玩家自动产生游戏动作.修改游戏网络数据包以及修改游戏内存数据等, 以实现玩家用最少的时间和金钱去完成功力升级和过关斩将 ...

  5. coverity代码检测工具介绍_FOREPOST:一种使用反馈驱动学习软件测试的性能检测工具...

    FOREPOST:一种使用反馈驱动学习软件测试的性能检测工具 摘要 性能测试的一个目标是找出某些特定情况,在这些情况下对于某些输入值组合,应用程序意外地展示出更糟糕的特性.性能测试的一个基本问题是如何 ...

  6. 三线压力传感器原理_电喷摩托车进气压力传感器原理与检测

    功能:检测发动机的进气压力,判断进气行程和负荷状况,用于ECU决定喷油时间和点火正时. 原理:利用膜片弯曲,造成电阻变化,检测压力. 故障现象:低速不良:动力不足,耗油,冒黑烟:容易熄火. 故障原因: ...

  7. (已解决)ubuntu16.04 Nvidia驱动安装成功却无法检测到外接显示器

    ubuntu16.04 Nvidia驱动安装成功却无法检测到外接显示器 双系统win10 + ubuntu16.04,Intel集显+Nvidia独显 问题描述: 电脑重新组装过后,windows下连 ...

  8. 【计算机原理与接口技术(UNIX)⑲ 完结篇】——可编程计数器 8254 [ 流光发生器、8254工作方式检测程序的设计]

    ✅ 通过对 [计算机与UNIX汇编原理 ① ~ ⑫]的学习,我们已经大致掌握了汇编程序设计的相关知识 接下来,我将其分栏名改为 [计算机原理与接口技术(UNIX) ],重点将放在 "计算机原 ...

  9. 老调重弹:JDBC系列之驱动加载原理全面解析)

    前言 最近在研究Mybatis框架,由于该框架基于JDBC,想要很好地理解和学习Mybatis,必须要对JDBC有较深入的了解.所以便把JDBC 这个东东翻出来,好好总结一番,作为自己的笔记,也是给读 ...

  10. 老调重弹:JDBC系列 之 驱动加载原理全面解析

    前言 最近在研究Mybatis框架,由于该框架基于JDBC,想要很好地理解和学习Mybatis,必须要对JDBC有较深入的了解.所以便把JDBC 这个东东翻出来,好好总结一番,作为自己的笔记,也是给读 ...

最新文章

  1. SQL CASE WHEN用法
  2. 内嵌Tomcat的Connector对象的静态代码块
  3. java中,如何实现输入一个正整数,并将这个数字反转输出,比如输入123,输出321
  4. python交换两个值原理_python 两数交换新写法原理
  5. 辽宁省计算机辅助普通话水平测试应试指南,计算机辅助普通话水平测试 应 试 指 南...
  6. 13数据库表空间回收
  7. 短文本相似度比较:simHash简介以及java实现
  8. Qt数据库集成应用封装
  9. 【专栏必读】数据库系统概论第五版(王珊)专栏学习笔记目录导航及课后习题答案详解
  10. 流量分析技术丨分享科来网络通讯协议图2022版本(附下载链接)
  11. java实现生日提醒_生日提醒功能-SQL查询语句在Java中肿么运用?
  12. win7浏览器主页修改不过来_win7系统浏览器主页修改不了的解决方法
  13. Ant Design Pro(5)-3.UI配置
  14. oracle中rebuild,ORACLE中index的rebuild(转)
  15. css让背景颜色与背景图片同时显示
  16. 软件测试的测试内容有那些
  17. 国仁网络资讯:抖音被降权、限流、警告了怎么办;触碰了抖音哪些违规行为。
  18. 英文文学研究助手(Python)
  19. 搬迁学习笔记2——The specified child already has a parent的解决方法
  20. ppt流程图字体太小_老板让你用PPT汇报工作?这5个实用小技巧让你的PPT锦上添花!...

热门文章

  1. 数据库设计(三)——数据库设计规范
  2. vbs返回结果给java_返回vbs脚本
  3. bootstrap-multiselect.js插件、chosen插件、clipboard复制插件、layer.photos、lightbox插件相册预览插件学习
  4. CAD图纸怎么加密?CAD图纸加密真的安全?
  5. 减速器的参数优化 毕业设计 matlab,matlab减速器计算机辅助设计系统+程序+CAD图纸+源程序...
  6. 数据库索引类型介绍及其优缺点、区别、适用场景
  7. 使用钢片刷锡膏的一种解决方案
  8. 【Android】 开发即时聊天工具 YQ (仿QQ) 教程:目录
  9. 企业信息化政务信息化浙里办
  10. 盘片式过滤器是盘式过滤器吗?