You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于WDK与C++:内核驱动监测notepad启动后运行osk.exe的实现问询

内核驱动中监测notepad启动后启动osk.exe的实现方案

一、核心思路说明

内核态无法直接调用用户态的CreateProcess,常用两种可行路径:

  1. 用内核原生的ZwCreateProcessEx+ZwCreateThreadEx直接创建进程
  2. 创建系统线程,切换到目标用户进程(如notepad)的上下文,调用用户层CreateProcessW启动osk.exe(这种方式更贴合用户态逻辑,能保证osk在同一桌面会话中显示)

以下重点讲解第二种方案,同时补充你提到的ZwOpenProcess/ZwOpenFile的使用场景。

二、具体实现步骤

1. 在LoadImage回调中触发启动逻辑

PsSetLoadImageNotifyRoutine的回调运行在PASSIVE_LEVEL,可直接创建系统线程执行启动逻辑,但要先判断是否是notepad.exe加载:

VOID LoadImageCallback(
    PUNICODE_STRING FullImageName,
    HANDLE ProcessId,
    PIMAGE_INFO ImageInfo
)
{
    if (FullImageName && ImageInfo->ImageBase != NULL)
    {
        // 提取文件名并判断是否为notepad.exe(忽略大小写)
        PWCHAR fileName = wcsrchr(FullImageName->Buffer, L'\\');
        if (fileName && _wcsicmp(fileName + 1, L"notepad.exe") == 0)
        {
            // 创建系统线程,传入notepad的进程ID
            HANDLE hThread = NULL;
            NTSTATUS status = PsCreateSystemThread(
                &hThread,
                THREAD_ALL_ACCESS,
                NULL,
                NULL,
                NULL,
                StartOskThread,
                (PVOID)ProcessId
            );
            if (NT_SUCCESS(status) && hThread != NULL)
            {
                ZwClose(hThread);
            }
        }
    }
}

2. 系统线程函数:切换到notepad进程上下文启动osk

系统线程默认运行在内核态,需附加到notepad进程,调用用户态CreateProcessW:

typedef BOOL(WINAPI* PCreateProcessW)(
    LPCWSTR lpApplicationName,
    LPWSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCWSTR lpCurrentDirectory,
    LPSTARTUPINFOW lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
);

VOID StartOskThread(PVOID Context)
{
    HANDLE hNotepadProcess = NULL;
    NTSTATUS status;
    HANDLE hUserThread = NULL;
    PPEB pProcessPeb = NULL;
    PVOID kernel32Base = NULL;
    PCreateProcessW pCreateProcessW = NULL;
    WCHAR oskPath[] = L"C:\\Windows\\System32\\osk.exe";
    STARTUPINFOW si = {0};
    si.cb = sizeof(STARTUPINFOW);

    HANDLE notepadPid = (HANDLE)Context;

    // 用ZwOpenProcess打开notepad进程(你关注的函数之一)
    OBJECT_ATTRIBUTES objAttr;
    InitializeObjectAttributes(&objAttr, NULL, 0, NULL, NULL);
    CLIENT_ID clientId = {.UniqueProcess = notepadPid, .UniqueThread = NULL};
    status = ZwOpenProcess(&hNotepadProcess, PROCESS_ALL_ACCESS, &objAttr, &clientId);
    if (!NT_SUCCESS(status)) goto Cleanup;

    // 获取notepad进程的PEB,定位kernel32.dll基址
    pProcessPeb = PsGetProcessPeb(hNotepadProcess);
    if (!pProcessPeb) goto Cleanup;

    PLIST_ENTRY moduleList = &pProcessPeb->Ldr->InMemoryOrderModuleList;
    PLIST_ENTRY currentEntry = moduleList->Flink;
    while (currentEntry != moduleList)
    {
        PLDR_DATA_TABLE_ENTRY pModuleEntry = CONTAINING_RECORD(currentEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
        if (pModuleEntry->BaseDllName.Length && _wcsicmp(pModuleEntry->BaseDllName.Buffer, L"kernel32.dll") == 0)
        {
            kernel32Base = pModuleEntry->DllBase;
            break;
        }
        currentEntry = currentEntry->Flink;
    }
    if (!kernel32Base) goto Cleanup;

    // 内核态获取用户态函数地址:用RtlFindExportedRoutineByName替代用户态的GetProcAddress
    pCreateProcessW = (PCreateProcessW)RtlFindExportedRoutineByName(kernel32Base, "CreateProcessW");
    if (!pCreateProcessW) goto Cleanup;

    // 在notepad进程中创建用户线程,调用CreateProcessW启动osk
    status = RtlCreateUserThread(
        hNotepadProcess,
        NULL,
        FALSE,
        0,
        0,
        0,
        (PVOID)pCreateProcessW,
        oskPath,
        &hUserThread,
        NULL
    );

    if (NT_SUCCESS(status) && hUserThread != NULL)
    {
        ZwWaitForSingleObject(hUserThread, FALSE, NULL);
        ZwClose(hUserThread);
    }

Cleanup:
    if (hNotepadProcess != NULL) ZwClose(hNotepadProcess);
    PsTerminateSystemThread(NT_SUCCESS(status) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
}

3. ZwOpenProcess/ZwOpenFile的补充说明

如果选择用ZwCreateProcessEx直接在核态创建进程,才会用到这两个函数:

  • ZwOpenFile:打开osk.exe的镜像文件,获取文件句柄作为ZwCreateProcessEx的输入参数
  • ZwOpenProcess:若要继承某个进程的环境/句柄,需先打开该进程获取句柄

简化示例片段:

// 用ZwOpenFile打开osk.exe文件(你关注的函数之二)
UNICODE_STRING oskFilePath;
RtlInitUnicodeString(&oskFilePath, L"\\??\\C:\\Windows\\System32\\osk.exe");
OBJECT_ATTRIBUTES fileAttr;
InitializeObjectAttributes(&fileAttr, &oskFilePath, OBJ_CASE_INSENSITIVE, NULL, NULL);
IO_STATUS_BLOCK ioStatus;
HANDLE hOskFile = NULL;
NTSTATUS status = ZwOpenFile(&hOskFile, GENERIC_READ | SYNCHRONIZE, &fileAttr, &ioStatus, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);

// 用ZwCreateProcessEx创建进程(需配合ZwCreateThreadEx初始化线程)
PROCESS_INFORMATION pi = {0};
status = ZwCreateProcessEx(
    &pi.hProcess,
    PROCESS_ALL_ACCESS,
    NULL,
    NtCurrentProcess(),
    TRUE,
    hOskFile,
    NULL,
    NULL,
    FALSE
);

三、关键注意事项

  • IRQL限制:PsSetLoadImageNotifyRoutine回调运行在PASSIVE_LEVEL,可执行线程创建、文件打开等操作,但不能调用高IRQL函数
  • 权限要求:驱动需持有SeCreateTokenPrivilege、SeAssignPrimaryTokenPrivilege等权限,否则进程创建会失败
  • 会话一致性:必须在用户进程(如notepad)的上下文启动osk,否则osk会运行在会话0,用户无法看到
  • 重复启动防护:需添加全局标记或进程判断,避免notepad多次加载时重复启动osk

内容的提问来源于stack exchange,提问作者김재희

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 22:20:27