驱动开发:内核中进程与句柄互转

简介: 在内核开发中,经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符(PID)来标识,而句柄是指对内核对象的引用。在Windows内核中,`EProcess`结构表示一个进程,而HANDLE是一个句柄。为了实现进程与句柄之间的转换,我们需要使用一些内核函数。对于进程PID和句柄的互相转换,可以使用函数如`OpenProcess`和`GetProcessId`。OpenProcess函数接受一个PID作为参数,并返回一个句柄。GetProcessId函数接受一个句柄作为参数,并返回该进程的PID。

在内核开发中,经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符(PID)来标识,而句柄是指对内核对象的引用。在Windows内核中,EProcess结构表示一个进程,而HANDLE是一个句柄。

为了实现进程与句柄之间的转换,我们需要使用一些内核函数。对于进程PID和句柄的互相转换,可以使用函数如OpenProcessGetProcessId。OpenProcess函数接受一个PID作为参数,并返回一个句柄。GetProcessId函数接受一个句柄作为参数,并返回该进程的PID。

对于进程PID和EProcess结构的互相转换,可以使用函数如PsGetProcessIdPsGetCurrentProcess。PsGetProcessId函数接受一个EProcess结构作为参数,并返回该进程的PID。PsGetCurrentProcess函数返回当前进程的EProcess结构。

最后,对于句柄和EProcess结构的互相转换,可以使用函数如ObReferenceObjectByHandle和PsGetProcessId。ObReferenceObjectByHandle函数接受一个句柄和一个对象类型作为参数,并返回对该对象的引用。PsGetProcessId函数接受一个EProcess结构作为参数,并返回该进程的PID。

掌握这些内核函数的使用,可以方便地实现进程与句柄之间的互相转换。在进行进程和线程的内核开发之前,了解这些转换功能是非常重要的。

进程PID与进程HANDLE之间的互相转换: 进程PID转化为HANDLE句柄,可通过ZwOpenProcess这个内核函数,传入PID传出进程HANDLE句柄,如果需要将HANDLE句柄转化为PID则可通过ZwQueryInformationProcess这个内核函数来实现,具体转换实现方法如下所示;

在内核开发中,经常需要进行进程PID和句柄HANDLE之间的互相转换。将进程PID转化为句柄HANDLE的方法是通过调用ZwOpenProcess内核函数,传入PID作为参数,函数返回对应进程的句柄HANDLE。具体实现方法是,定义一个OBJECT_ATTRIBUTES结构体和CLIENT_ID结构体,将进程PID赋值给CLIENT_ID结构体的UniqueProcess字段,调用ZwOpenProcess函数打开进程,如果函数执行成功,将返回进程句柄HANDLE,否则返回NULL。

将句柄HANDLE转化为进程PID的方法是通过调用ZwQueryInformationProcess内核函数,传入进程句柄和信息类别作为参数,函数返回有关指定进程的信息,包括进程PID。具体实现方法是,定义一个PROCESS_BASIC_INFORMATION结构体和一个NTSTATUS变量,调用ZwQueryInformationProcess函数查询进程基本信息,如果函数执行成功,将返回进程PID,否则返回0。

其中ZwQueryInformationProcess是一个未被导出的函数如需使用要通过MmGetSystemRoutineAddress动态获取到,该函数的原型定义如下:

NTSTATUS ZwQueryInformationProcess(
  HANDLE           ProcessHandle,
  PROCESSINFOCLASS ProcessInformationClass,
  PVOID            ProcessInformation,
  ULONG            ProcessInformationLength,
  PULONG           ReturnLength
);

函数可以接受一个进程句柄ProcessHandle、一个PROCESSINFOCLASS枚举类型的参数ProcessInformationClass、一个用于存储返回信息的缓冲区ProcessInformation、缓冲区大小ProcessInformationLength和一个指向ULONG类型变量的指针ReturnLength作为参数。

在调用该函数时,ProcessInformationClass参数指定要获取的进程信息的类型。例如,如果要获取进程的基本信息,则需要将该参数设置为ProcessBasicInformation;如果要获取进程的映像文件名,则需要将该参数设置为ProcessImageFileName。调用成功后,返回的信息存储在ProcessInformation缓冲区中。

在调用该函数时,如果ProcessInformation缓冲区的大小小于需要返回的信息大小,则该函数将返回STATUS_INFO_LENGTH_MISMATCH错误代码,并将所需信息的大小存储在ReturnLength指针指向的ULONG类型变量中。

ZwQueryInformationProcess函数的返回值为NTSTATUS类型,表示函数执行的结果状态。如果函数执行成功,则返回STATUS_SUCCESS,否则返回其他错误代码。

掌握这些转换方法可以方便地在内核开发中进行进程PID和句柄HANDLE之间的互相转换。

#include <ntifs.h>

// 定义函数指针
typedef NTSTATUS(*PfnZwQueryInformationProcess)(
    __in HANDLE ProcessHandle,
    __in PROCESSINFOCLASS ProcessInformationClass,
    __out_bcount(ProcessInformationLength) PVOID ProcessInformation,
    __in ULONG ProcessInformationLength,
    __out_opt PULONG ReturnLength
);

PfnZwQueryInformationProcess ZwQueryInformationProcess;

// 传入PID传出HANDLE句柄
HANDLE PidToHandle(ULONG PID)
{
   
   
    HANDLE hProcessHandle;
    OBJECT_ATTRIBUTES obj;
    CLIENT_ID clientid;

    clientid.UniqueProcess = PID;
    clientid.UniqueThread = 0;

    // 属性初始化
    InitializeObjectAttributes(&obj, 0, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);

    NTSTATUS status = ZwOpenProcess(&hProcessHandle, PROCESS_ALL_ACCESS, &obj, &clientid);
    if (status == STATUS_SUCCESS)
    {
   
   
        // DbgPrint("[*] 已打开 \n");
        ZwClose(&hProcessHandle);
        return hProcessHandle;
    }

    return 0;
}

// HANDLE句柄转换为PID
ULONG HandleToPid(HANDLE handle)
{
   
   
    PROCESS_BASIC_INFORMATION ProcessBasicInfor;

    // 初始化字符串,并获取动态地址
    UNICODE_STRING UtrZwQueryInformationProcessName = RTL_CONSTANT_STRING(L"ZwQueryInformationProcess");
    ZwQueryInformationProcess = (PfnZwQueryInformationProcess)MmGetSystemRoutineAddress(&UtrZwQueryInformationProcessName);

    // 调用查询
    ZwQueryInformationProcess(
        handle,
        ProcessBasicInformation,
        (PVOID)&ProcessBasicInfor,
        sizeof(ProcessBasicInfor),
        NULL);

    // 返回进程PID
    return ProcessBasicInfor.UniqueProcessId;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
   
   
    DbgPrint("[-] 驱动卸载 \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
   
   
    DbgPrint("Hello LyShark \n");

    // 将PID转换为HANDLE
    HANDLE ptr = PidToHandle(6932);
    DbgPrint("[*] PID  --> HANDLE = %p \n", ptr);

    // 句柄转为PID
    ULONG pid = HandleToPid(ptr);

    DbgPrint("[*] HANDLE  --> PID = %d \n", pid);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行如上这段代码片段,将把进程PID转为HANDLE句柄,再通过句柄将其转为PID,输出效果图如下所示;

image.png

进程PID转换为EProcess结构: 通过PsLookUpProcessByProcessId函数,该函数传入一个PID则可获取到该PID的EProcess结构体,具体转换实现方法如下所示;

本段代码展示了如何使用Windows内核API函数PsLookupProcessByProcessId将一个PID(Process ID)转换为对应的EProcess结构体,EProcess是Windows内核中描述进程的数据结构之一。

代码段中定义了一个名为PidToObject的函数,该函数的输入参数是一个PID,输出参数是对应的EProcess结构体。

在函数中,通过调用PsLookupProcessByProcessId函数来获取对应PID的EProcess结构体,如果获取成功,则调用ObDereferenceObject函数来减少EProcess对象的引用计数,并返回获取到的EProcess指针;否则返回0。

DriverEntry函数中,调用了PidToObject函数将PID 6932转换为对应的EProcess结构体,并使用DbgPrint函数输出了转换结果。最后设置了驱动程序卸载函数为UnDriver,当驱动程序被卸载时,UnDriver函数会被调用。

#include <ntifs.h>
#include <windef.h>

// 将Pid转换为Object or EProcess
PEPROCESS PidToObject(ULONG Pid)
{
   
   
    PEPROCESS pEprocess;

    NTSTATUS status = PsLookupProcessByProcessId((HANDLE)Pid, &pEprocess);

    if (status == STATUS_SUCCESS)
    {
   
   
        ObDereferenceObject(pEprocess);
        return pEprocess;
    }

    return 0;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
   
   
    DbgPrint("[-] 驱动卸载 \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
   
   
    DbgPrint("Hello LyShark \n");

    // 将PID转换为PEPROCESS
    PEPROCESS ptr = PidToObject(6932);
    DbgPrint("[*] PID  --> PEPROCESS = %p \n", ptr);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行如上这段代码片段,将把进程PID转为EProcess结构,输出效果图如下所示;

image.png

进程HANDLE与EPROCESS互相转换:Handle转换为EProcess结构可使用内核函数ObReferenceObjectByHandle实现,反过来EProcess转换为Handle句柄可使用ObOpenObjectByPointer内核函数实现,具体转换实现方法如下所示;

首先,将Handle转换为EProcess结构体,可以使用ObReferenceObjectByHandle内核函数。该函数接受一个Handle参数,以及对应的对象类型(这里为EProcess),并返回对应对象的指针。此函数会对返回的对象增加引用计数,因此在使用完毕后,需要使用ObDereferenceObject将引用计数减少。

其次,将EProcess结构体转换为Handle句柄,可以使用ObOpenObjectByPointer内核函数。该函数接受一个指向对象的指针(这里为EProcess结构体的指针),以及所需的访问权限和对象类型,并返回对应的Handle句柄。此函数会将返回的句柄添加到当前进程的句柄表中,因此在使用完毕后,需要使用CloseHandle函数将句柄关闭,以避免资源泄漏。

综上所述,我们可以通过这两个内核函数实现HandleEProcess之间的相互转换,转换代码如下所示;

#include <ntifs.h>
#include <windef.h>

// 传入PID传出HANDLE句柄
HANDLE PidToHandle(ULONG PID)
{
   
   
    HANDLE hProcessHandle;
    OBJECT_ATTRIBUTES obj;
    CLIENT_ID clientid;

    clientid.UniqueProcess = PID;
    clientid.UniqueThread = 0;

    // 属性初始化
    InitializeObjectAttributes(&obj, 0, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);

    NTSTATUS status = ZwOpenProcess(&hProcessHandle, PROCESS_ALL_ACCESS, &obj, &clientid);
    if (status == STATUS_SUCCESS)
    {
   
   
        // DbgPrint("[*] 已打开 \n");
        ZwClose(&hProcessHandle);
        return hProcessHandle;
    }

    return 0;
}

// 将Handle转换为EProcess结构
PEPROCESS HandleToEprocess(HANDLE handle)
{
   
   
    PEPROCESS pEprocess;

    NTSTATUS status = ObReferenceObjectByHandle(handle, GENERIC_ALL, *PsProcessType, KernelMode, &pEprocess, NULL);
    if (status == STATUS_SUCCESS)
    {
   
   
        return pEprocess;
    }

    return 0;
}

// EProcess转换为Handle句柄
HANDLE EprocessToHandle(PEPROCESS eprocess)
{
   
   
    HANDLE hProcessHandle = (HANDLE)-1;

    NTSTATUS status = ObOpenObjectByPointer(
        eprocess,
        OBJ_KERNEL_HANDLE,
        0,
        0,
        *PsProcessType,
        KernelMode,
        &hProcessHandle
        );

    if (status == STATUS_SUCCESS)
    {
   
   
        return hProcessHandle;
    }

    return 0;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
   
   
    DbgPrint("[-] 驱动卸载 \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
   
   
    DbgPrint("Hello LyShark \n");

    // 将Handle转换为EProcess结构
    PEPROCESS eprocess = HandleToEprocess(PidToHandle(6932));
    DbgPrint("[*] HANDLE --> EProcess = %p \n", eprocess);

    // 将EProcess结构转换为Handle
    HANDLE handle = EprocessToHandle(eprocess);
    DbgPrint("[*] EProcess --> HANDLE = %p \n", handle);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译并运行如上这段代码片段,将把进程HANDLEEProcess结构互转,输出效果图如下所示;

image.png

目录
相关文章
|
2月前
|
消息中间件 存储 算法
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
84 0
|
13天前
|
算法 Linux 调度
深入理解Linux内核的进程调度机制
【4月更文挑战第17天】在多任务操作系统中,进程调度是核心功能之一,它决定了处理机资源的分配。本文旨在剖析Linux操作系统内核的进程调度机制,详细讨论其调度策略、调度算法及实现原理,并探讨了其对系统性能的影响。通过分析CFS(完全公平调度器)和实时调度策略,揭示了Linux如何在保证响应速度与公平性之间取得平衡。文章还将评估最新的调度技术趋势,如容器化和云计算环境下的调度优化。
|
19天前
|
算法 Linux 调度
深度解析:Linux内核的进程调度机制
【4月更文挑战第12天】 在多任务操作系统如Linux中,进程调度机制是系统的核心组成部分之一,它决定了处理器资源如何分配给多个竞争的进程。本文深入探讨了Linux内核中的进程调度策略和相关算法,包括其设计哲学、实现原理及对系统性能的影响。通过分析进程调度器的工作原理,我们能够理解操作系统如何平衡效率、公平性和响应性,进而优化系统表现和用户体验。
|
2月前
|
监控 C++
C++ Qt开发:QProcess进程管理模块
Qt是一个跨平台的C++图形库,简化了窗体应用开发,支持通过拖放组件提升效率。本章节关注`QProcess`组件,它用于控制和管理进程,例如执行命令、运行可执行文件及与外部进程通信。`QProcess`提供多种方法如`start`、`waitForStarted`和`waitForFinished`等,实现启动、监控和交互。示例展示了如何使用`QProcess`获取系统进程和信息,通过`tasklist`和`systeminfo`命令,并将结果展示在`QTreeWidget`中。
31 0
C++ Qt开发:QProcess进程管理模块
|
2月前
|
存储 安全 Linux
深入Linux进程内核:揭开进程工作原理的神秘面纱
深入Linux进程内核:揭开进程工作原理的神秘面纱
55 0
|
2月前
|
消息中间件 Linux 调度
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
74 0
|
7天前
|
NoSQL Linux 程序员
【linux进程信号(一)】信号的概念以及产生信号的方式
【linux进程信号(一)】信号的概念以及产生信号的方式
|
7天前
|
Java Shell Linux
【linux进程控制(三)】进程程序替换--如何自己实现一个bash解释器?
【linux进程控制(三)】进程程序替换--如何自己实现一个bash解释器?
|
7天前
|
算法 Linux Shell
【linux进程(二)】如何创建子进程?--fork函数深度剖析
【linux进程(二)】如何创建子进程?--fork函数深度剖析
|
7天前
|
存储 Linux Shell
【linux进程(一)】深入理解进程概念--什么是进程?PCB的底层是什么?
【linux进程(一)】深入理解进程概念--什么是进程?PCB的底层是什么?