内核初级对抗技术分析
字数 1415 2025-08-30 06:50:12
内核初级对抗技术分析教学文档
前言
Windows内核提供了多个强大的回调接口用于监控或控制系统行为,其中ObRegisterCallbacks和CmRegisterCallback是两种最常用于进程保护和注册表监控的机制。本文将通过完整驱动示例演示如何利用这两种回调进行内核级防护,并分析它们在初级对抗中的实际应用效果与局限性。
测试环境: Windows 10
基础知识
CmRegisterCallback
用于拦截注册表的创建、修改、重命名、删除等操作,使用回调函数可判断操作是否合法并决定是否放行。常用于:
- 拦截恶意程序添加启动项
- 保护系统关键注册表项不被修改
ObRegisterCallbacks
提供了对进程、线程、句柄对象访问的监控机制。常用于:
- 阻止非授权进程访问指定目标进程
- 实现类似"句柄防注入"功能
防御技术
注册表监控
关键API
NTSTATUS CmRegisterCallback(
IN PEX_CALLBACK_FUNCTION Function, // 指向RegistryCallback例程的指针
IN PVOID Context, // 传递给RegistryCallback的CallbackContext,一般为NULL
OUT PLARGE_INTEGER Cookie // 标识回调例程的值,用于CmUnRegisterCallback
);
NTSTATUS RegistryCallback(
__in PVOID CallbackContext, // 注册时传递的Context值,一般为NULL
__in_opt PVOID Argument1, // REG_NOTIFY_CLASS类型值,表示操作类型
__in_opt PVOID Argument2 // 指向特定于操作的结构指针
);
关键代码实现
// 定义全局变量标识
LARGE_INTEGER Cookie = { 0 };
// DriverEntry安装
NTSTATUS status = CmRegisterCallback(RegistryCallback, NULL, &Cookie);
// 注册表回调函数
NTSTATUS RegistryCallback(
PVOID CallbackContext,
PVOID Argument1,
PVOID Argument2)
{
UNREFERENCED_PARAMETER(CallbackContext);
UNICODE_STRING tempName = { 0 };
RtlInitUnicodeString(&tempName, L"*GSAHOJGHJERIWO"); // 设置要匹配的目标名称模式
REG_NOTIFY_CLASS notifyClass = (REG_NOTIFY_CLASS)(ULONG_PTR)Argument1;
NTSTATUS status = STATUS_SUCCESS;
switch (notifyClass)
{
case RegNtRenameKey: {
PREG_RENAME_KEY_INFORMATION pInfo = (PREG_RENAME_KEY_INFORMATION)Argument2;
if (pInfo && pInfo->NewName) {
if (FsRtlIsNameInExpression(&tempName, pInfo->NewName, TRUE, NULL)) {
DbgPrint("Bad Rename\n");
status = STATUS_UNSUCCESSFUL;
}
}
break;
}
case RegNtPreCreateKey:
case RegNtPreCreateKeyEx: {
PREG_CREATE_KEY_INFORMATION pInfo = (PREG_CREATE_KEY_INFORMATION)Argument2;
if (pInfo && pInfo->CompleteName) {
if (FsRtlIsNameInExpression(&tempName, pInfo->CompleteName, TRUE, NULL)) {
DbgPrint("Bad Create\n");
status = STATUS_UNSUCCESSFUL;
}
}
break;
}
default:
break;
}
return status;
}
实现效果:
- 拦截指定名称的注册表项创建操作
- 手动创建注册表项时会先创建"新项 #1"再重命名,因此需要同时拦截
RegNtRenameKey
进程保护监控
在Windows体系中,应用层通过句柄来索引内核对象。通过ObRegisterCallbacks实现进程保护的核心是利用Windows内核的对象回调机制,在进程对象的关键操作(如句柄创建、权限获取)发生时介入校验,动态修改访问权限。
关键API
NTSTATUS ObRegisterCallbacks(
[in] POB_CALLBACK_REGISTRATION CallbackRegistration, // 指定回调例程和其他注册信息
[out] PVOID *RegistrationHandle // 标识注册的回调例程集
);
关键代码实现
// DriverEntry安装
PLDR_DATA_TABLE_ENTRY ldr = (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
ldr->Flags |= 0x20;
OB_CALLBACK_REGISTRATION ob = { 0 };
OB_OPERATION_REGISTRATION oor = { 0 };
UNICODE_STRING attde = { 0 };
ob.Version = ObGetFilterVersion();
ob.OperationRegistrationCount = 1;
ob.OperationRegistration = &oor;
RtlInitUnicodeString(&attde, L"321999"); // 注册高度(Altitude)
ob.RegistrationContext = NULL;
ob.Altitude = attde;
oor.ObjectType = PsProcessType; // 注册进程类型
oor.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; // 触发条件
oor.PreOperation = ProtectProcess; // 前操作回调函数
oor.PostOperation = NULL; // 后操作回调函数
status = ObRegisterCallbacks(&ob, &_HANDLE);
// 回调函数
OB_PREOP_CALLBACK_STATUS ProtectProcess(
PVOID RegistrationContext,
POB_PRE_OPERATION_INFORMATION OperationInformation)
{
if (*PsProcessType != OperationInformation->ObjectType) {
return OB_PREOP_SUCCESS;
}
PEPROCESS targetProcess = (PEPROCESS)OperationInformation->Object;
PUCHAR targetName = PsGetProcessImageFileName(targetProcess);
PUCHAR sourceName = PsGetProcessImageFileName(PsGetCurrentProcess());
// 目标是notepad
if (targetName && _stricmp((char*)targetName, "notepad.exe") == 0) {
// 白名单:允许系统关键进程访问notepad
if (sourceName &&
(_stricmp((char*)sourceName, "explorer.exe") == 0 ||
_stricmp((char*)sourceName, "csrss.exe") == 0 ||
_stricmp((char*)sourceName, "services.exe") == 0 ||
_stricmp((char*)sourceName, "smss.exe") == 0 ||
_stricmp((char*)sourceName, "wininit.exe") == 0 ||
_stricmp((char*)sourceName, "lsass.exe") == 0 ||
_stricmp((char*)sourceName, "notepad.exe") == 0))
{
return OB_PREOP_SUCCESS;
}
// 非白名单访问notepad,拦截
DbgPrint("阻止进程 %s 访问 notepad.exe\n", sourceName);
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;
}
return OB_PREOP_SUCCESS;
}
关键点:
- 通过设置
DesiredAccess = 0清除所有请求的访问权限 - 后续进程试图通过该句柄执行任何操作都会因权限不足而失败
- 可有效阻止如Cheat Engine等工具读写受保护进程内存
进攻技术
对抗注册表保护
主要方法
- 通过
CmUnRegisterCallback函数找到Cookie直接调用卸载 - 找到回调函数首地址,直接ret
- 将
CmpCallBackCount设置为0
实现步骤
- 记录Cookie和回调函数入口
- 分析
CmUnRegisterCallback函数结构,发现关键成员CallbackListHead是双向环状链表 - 链表结构体中包含Cookie、Context和回调函数入口地址
- 构建结构体并通过特征码定位关键位置
注意事项:
- 不能在遍历链表时直接调用
CmUnRegisterCallback,因为该函数会修改链表结构 - 应先在循环中记录所有cookies,再循环移除
对抗进程保护
主要方法
- 通过
ObUnRegisterCallbacks找到_HANDLE值并调用 - 找到回调函数入口,直接ret
- 注册更高Altitude的拦截
实现步骤
- 记录
_HANDLE和回调函数入口地址 - 获取
PsProcessType值(指针的指针) - 分析
CallbackList结构,发现是双向环状链表 - 在链表成员中找到
_HANDLE和回调函数地址 - 构建结构体并遍历链表
优势:
PsProcessType是全局变量,可直接获取,比注册表监控更容易定位
总结
-
内核回调机制是内核安全防护的重要组成部分,但在面对有一定逆向能力和Ring0编写能力的攻击者时,其对抗能力存在上限
-
提高防护效果的方法:
- 合理使用白名单机制
- 采用路径精确匹配
- 结合进程行为分析
-
需要考虑的问题:
- 回调滥用可能带来的系统不稳定
- 兼容性问题
- 性能影响
-
实际应用中应权衡:
- 性能
- 安全性
- 系统稳定性