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

如何向ZwCreateThreadEx例程传递参数?含HANDLE与UNICODE_STRING示例

如何给ZwCreateThreadEx的线程函数传递HANDLE和UNICODE_STRING参数?

兄弟,我明白你现在的需求——之前的代码能创建无参数的隐藏线程,但现在要给线程函数传HANDLE和UNICODE_STRING类型的参数对吧?其实核心思路很简单:因为ZwCreateThreadEx的线程入口函数只接受一个LPVOID类型的参数,所以我们可以把多个需要传递的参数打包成一个自定义结构体,然后把这个结构体的指针作为参数传入,在线程函数内部再把指针转换回结构体类型,就能拿到我们需要的参数了。

下面是具体的实现步骤和完整代码示例:

1. 定义包含目标参数的结构体

首先我们要定义一个结构体,把需要传递的HANDLE和UNICODE_STRING都放进去:

// 自定义参数结构体,包含需要传递的两种类型参数
typedef struct _THREAD_PARAMS {
    HANDLE hTargetHandle;
    UNICODE_STRING usTargetString;
} THREAD_PARAMS, *PTHREAD_PARAMS;

2. 修改线程函数以接收并解析参数

把原来的线程函数改成接收结构体指针的形式,然后从结构体里取出我们需要的参数:

void WINAPI ContinueExecution(LPVOID param) {
    // 将传入的LPVOID参数转换为我们的结构体指针
    PTHREAD_PARAMS pParams = (PTHREAD_PARAMS)param;

    // 现在就可以使用结构体里的参数了
    printf("线程已启动,传入的HANDLE值: 0x%p\n", pParams->hTargetHandle);
    
    // 打印UNICODE_STRING内容(注意要转换为多字节字符串方便输出)
    char szBuffer[256] = {0};
    WideCharToMultiByte(CP_ACP, 0, pParams->usTargetString.Buffer, 
                        pParams->usTargetString.Length / sizeof(WCHAR), 
                        szBuffer, sizeof(szBuffer), NULL, NULL);
    printf("传入的字符串内容: %s\n", szBuffer);

    // 如果参数是堆分配的,记得在线程结束前释放(根据实际分配方式调整)
    HeapFree(GetProcessHeap(), 0, param);
}

3. 准备参数并调用ZwCreateThreadEx

在主函数里,我们需要实例化这个结构体,填充参数,然后把结构体指针作为StartParameter传递给ZwCreateThreadEx:

#include "stdafx.h"
#include <conio.h>
#include <windows.h>
#include <Winternl.h>
#pragma comment(lib,"ntdll.lib")

// 这里放上面定义的_THREAD_PARAMS结构体和ContinueExecution函数

int main() {
    // 1. 准备要传递的参数
    THREAD_PARAMS* pThreadParams = (THREAD_PARAMS*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(THREAD_PARAMS));
    if (!pThreadParams) {
        printf("分配参数内存失败!\n");
        return -1;
    }

    // 填充HANDLE参数(这里用当前进程的句柄作为示例)
    pThreadParams->hTargetHandle = GetCurrentProcess();

    // 填充UNICODE_STRING参数(这里初始化一个示例字符串)
    WCHAR szTestString[] = L"Hello from hidden thread!";
    pThreadParams->usTargetString.Buffer = szTestString;
    pThreadParams->usTargetString.Length = wcslen(szTestString) * sizeof(WCHAR);
    pThreadParams->usTargetString.MaximumLength = (wcslen(szTestString) + 1) * sizeof(WCHAR);

    // 2. 调用ZwCreateThreadEx创建线程
    HANDLE hHiddenThread = NULL;
    NTSTATUS status = ZwCreateThreadEx(
        &hHiddenThread,
        THREAD_ALL_ACCESS,
        NULL,
        GetCurrentProcess(),
        (PTHREAD_START_ROUTINE)ContinueExecution,
        pThreadParams,  // 这里传递我们的结构体指针作为参数
        FALSE,
        0,
        0,
        0,
        NULL
    );

    if (NT_SUCCESS(status)) {
        printf("隐藏线程创建成功!线程句柄: 0x%p\n", hHiddenThread);
        // 等待线程执行完成(可选,根据需求调整)
        WaitForSingleObject(hHiddenThread, INFINITE);
        CloseHandle(hHiddenThread);
    } else {
        printf("创建线程失败,NTSTATUS: 0x%X\n", status);
        HeapFree(GetProcessHeap(), 0, pThreadParams);
    }

    _getch();
    return 0;
}

关键注意事项

  • 参数的生命周期:如果结构体是在栈上分配的(比如局部变量),要确保主线程不会在线程函数执行完之前退出或者销毁这个栈变量,否则线程函数会访问到无效内存。所以更安全的方式是用HeapAlloc在堆上分配结构体,就像示例里那样,然后在线程函数结束前释放。
  • UNICODE_STRING的缓冲区:如果UNICODE_STRING的Buffer是动态分配的(比如用HeapAlloc分配的宽字符串),也要确保在线程函数里正确释放,避免内存泄漏。
  • 类型转换:在线程函数里一定要把LPVOID正确转换为我们的结构体指针,否则会出现类型错误或者内存访问异常。

内容的提问来源于stack exchange,提问作者user9672569

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:30:55