内核初级对抗技术分析
字数 1415 2025-08-30 06:50:12

内核初级对抗技术分析教学文档

前言

Windows内核提供了多个强大的回调接口用于监控或控制系统行为,其中ObRegisterCallbacksCmRegisterCallback是两种最常用于进程保护和注册表监控的机制。本文将通过完整驱动示例演示如何利用这两种回调进行内核级防护,并分析它们在初级对抗中的实际应用效果与局限性。

测试环境: 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等工具读写受保护进程内存

进攻技术

对抗注册表保护

主要方法

  1. 通过CmUnRegisterCallback函数找到Cookie直接调用卸载
  2. 找到回调函数首地址,直接ret
  3. CmpCallBackCount设置为0

实现步骤

  1. 记录Cookie和回调函数入口
  2. 分析CmUnRegisterCallback函数结构,发现关键成员CallbackListHead是双向环状链表
  3. 链表结构体中包含Cookie、Context和回调函数入口地址
  4. 构建结构体并通过特征码定位关键位置

注意事项:

  • 不能在遍历链表时直接调用CmUnRegisterCallback,因为该函数会修改链表结构
  • 应先在循环中记录所有cookies,再循环移除

对抗进程保护

主要方法

  1. 通过ObUnRegisterCallbacks找到_HANDLE值并调用
  2. 找到回调函数入口,直接ret
  3. 注册更高Altitude的拦截

实现步骤

  1. 记录_HANDLE和回调函数入口地址
  2. 获取PsProcessType值(指针的指针)
  3. 分析CallbackList结构,发现是双向环状链表
  4. 在链表成员中找到_HANDLE和回调函数地址
  5. 构建结构体并遍历链表

优势:

  • PsProcessType是全局变量,可直接获取,比注册表监控更容易定位

总结

  1. 内核回调机制是内核安全防护的重要组成部分,但在面对有一定逆向能力和Ring0编写能力的攻击者时,其对抗能力存在上限

  2. 提高防护效果的方法:

    • 合理使用白名单机制
    • 采用路径精确匹配
    • 结合进程行为分析
  3. 需要考虑的问题:

    • 回调滥用可能带来的系统不稳定
    • 兼容性问题
    • 性能影响
  4. 实际应用中应权衡:

    • 性能
    • 安全性
    • 系统稳定性
内核初级对抗技术分析教学文档 前言 Windows内核提供了多个强大的回调接口用于监控或控制系统行为,其中 ObRegisterCallbacks 和 CmRegisterCallback 是两种最常用于进程保护和注册表监控的机制。本文将通过完整驱动示例演示如何利用这两种回调进行内核级防护,并分析它们在初级对抗中的实际应用效果与局限性。 测试环境 : Windows 10 基础知识 CmRegisterCallback 用于拦截注册表的创建、修改、重命名、删除等操作,使用回调函数可判断操作是否合法并决定是否放行。常用于: 拦截恶意程序添加启动项 保护系统关键注册表项不被修改 ObRegisterCallbacks 提供了对进程、线程、句柄对象访问的监控机制。常用于: 阻止非授权进程访问指定目标进程 实现类似"句柄防注入"功能 防御技术 注册表监控 关键API 关键代码实现 实现效果 : 拦截指定名称的注册表项创建操作 手动创建注册表项时会先创建"新项 #1"再重命名,因此需要同时拦截 RegNtRenameKey 进程保护监控 在Windows体系中,应用层通过句柄来索引内核对象。通过 ObRegisterCallbacks 实现进程保护的核心是利用Windows内核的对象回调机制,在进程对象的关键操作(如句柄创建、权限获取)发生时介入校验,动态修改访问权限。 关键API 关键代码实现 关键点 : 通过设置 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编写能力的攻击者时,其对抗能力存在上限 提高防护效果的方法: 合理使用白名单机制 采用路径精确匹配 结合进程行为分析 需要考虑的问题: 回调滥用可能带来的系统不稳定 兼容性问题 性能影响 实际应用中应权衡: 性能 安全性 系统稳定性