0x00 前言
我们要想在32位下实现进程保护很简单,通过SSDT hook重写函数即可实现,但是在64位系统下因为引入了PG
和DSE
的原因,导致SSDT hook
实现起来处处受限。但微软同样为了系统安全,增加了一个对象回调函数的接口,利用该回调可以实现对对象请求的过滤保护自身的进程,目前大部分64位下的安全软件保护机制都是基于该方法,我们深入进行探究
0x01 驱动保护
其实在最开始研究的时候我的思路是通过研究游戏这一块的保护来进行延申,说到游戏保护这块做得比较好的就是鹅厂的tp了
在经过搜索引擎的洗礼之后,我发现tp是通过hypervisor
进行驱动保护,但是这里细节十分复杂,对我这种小白十分不友好,简略来说就是接管内核里面常见的读写API来阻止读写
在研究的过程中,我又发现了另外一种驱动保护的方法:剥离句柄回调
我们在前面提到过微软为了安全性的考虑在64位下面不允许我们去动内核里面的代码了,而是通过回调函数的机制来进行操作,比如说要实现进程的监控就注册PsSetCreateProcessNotifyRoutineEx
的回调,如果想要实现模块的监控就注册PsSetLoadImageNotifyRoutine
的回调
那么如果想要进行调试,必须用到的一个API
就是OpenProcess
,最终在内核调用的函数就是NtOpenProcess
,而NtOpenProcess
会触发ObRegisterCallbacks
这个回调函数。那么只要能够捕获ObRegisterCallbacks
发送的这些回调信息,将一些敏感的剥离,就能够达到防止进程读写的效果
那么我们去msdn里面找一下ObRegisterCallbacks
这个函数
NTSTATUS ObRegisterCallbacks (
_In_ POB_CALLBACK_REGISTRATION CallBackRegistration,
_Out_ PVOID *RegistrationHandle
);
image-20220430161205580.png
第一个参数 CallBackRegistration
是个指向 OB_CALLBACK_REGISTRATION
类型的结构体的指针。当 ObRegisterCallbacks
例程注册 ObjectPreCallback
或 ObjectPostCallback
回调例程时这个结构体指定回调例程和其他注册信息的的列表,结构如下
typedef struct _OB_CALLBACK_REGISTRATION {
USHORT Version;
USHORT OperationRegistrationCount;
UNICODE_STRING Altitude;
PVOID RegistrationContext;
OB_OPERATION_REGISTRATION *OperationRegistration;
} OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;
第五个成员 OperationRegistration
比较关键,这是个指向 OB_OPERATION_REGISTRATION
类型结构体数组的指针。每个 OB_OPERATION_REGISTRATION
结构体指定 ObjectPreCallback
和 ObjectPostCallback
回调例程以及那些例程被调用的操作类型,结构如下
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;
在这里需要注意的是第三个成员 PreOperation
。这是个指向 ObjectPreCallback
例程的指针,系统会在请求的操作发生之前调用这个例程,通过这个 ObjectPreCallback
例程来达到我们的目的
OB_PREOP_CALLBACK_STATUS ObjectPreCallback (
_In_ PVOID RegistrationContext,
_In_ POB_PRE_OPERATION_INFORMATION OperationInformation
);
这里延伸一下,如果想要使用ObRegisterCallbacks
,windows会首先通过MmVerifyCallbackFunction
这个函数去实现强制完整性检查
强制完整性检查是一种确保正在加载的二进制文件在加载前需要使用签名的策略,
IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY
标志在链接时通过使用/integritycheck
链接器标志在PE头中进行设置,让正在加载的二进制文件必须签名,这个标志使windows内存管理器在加载时对二进制文件进行签名检查那么微软就是通过加载二进制文件时是否存在标志来确认驱动的发布者身份是否为已知状态,这就是强制完整性检查
我在IDA里面逆了一下MmVerifyCallbackFunction
这个函数,发现其逻辑就是通过比较[rax+68h]
是否包含了0x20来判断是否拥有正确的数字签名
image-20220430162029355.png
这里的rax
表示DriverSection
,而DriverSection
指向的是_LDR_DATA_TABLE_ENTRY
结构,那么[rax + 0x68]
指向的就是ProcessStaticImport
image-20220430162109373.png
那么如果我们要使用ObRegisterCallbacks
这个函数就需要拥有数字签名,这里我们就可以将DriverObject->DriverSection->Flags
的值与0x20
按位或即可
在64位系统里面一些杀软的自保护都是通过设置OBOperationRegistration
这个回调函数实现,关键代码如下
VOID InstallCallBacks()
{
NTSTATUS NtHandleCallback = STATUS_UNSUCCESSFUL;
NTSTATUS NtThreadCallback = STATUS_UNSUCCESSFUL;
OB_OPERATION_REGISTRATION OBOperationRegistration[2];
OB_CALLBACK_REGISTRATION OBOCallbackRegistration;
REG_CONTEXT regContext;
UNICODE_STRING usAltitude;
memset(&OBOpe