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


    在内核开发中,经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符(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。

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

    4.1.1 进程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
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    函数可以接受一个进程句柄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 
    
    // 定义函数指针
    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;
    }
    
    • 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

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

    4.1.2 进程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 
    #include 
    
    // 将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;
    }
    
    • 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

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

    4.1.3 进程HANDLE与EPROCESS转换

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

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

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

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

    #include 
    #include 
    
    // 传入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;
    }
    
    • 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

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

  • 相关阅读:
    IDEA 部署服务到 Docker 容器
    国家建筑装配式内装产业基地在沪成立,副主任单位优积科技协同助推绿色低碳循环发展
    第十四章《多线程》第8节:线程池
    计算机毕业设计选什么题目好?springboot 学生考勤管理系统
    nextjs引入tailwindcss 、antd、sass
    I3C bus
    LeetCode 0290. 单词规律
    9月25日,每日信息差
    SpringBoot-黑马程序员-学习笔记(二)
    【web后端】C#类与继承、随机数写法、虚函数、例题
  • 原文地址:https://blog.csdn.net/lyshark_csdn/article/details/134454982