驅動開發:內核中進程與句柄互轉

来源:https://www.cnblogs.com/LyShark/archive/2023/06/23/17173641.html
-Advertisement-
Play Games

在內核開發中,經常需要進行進程和句柄之間的互相轉換。進程通常由一個唯一的進程標識符(PID)來標識,而句柄是指對內核對象的引用。在Windows內核中,`EProcess`結構表示一個進程,而HANDLE是一個句柄。為了實現進程與句柄之間的轉換,我們需要使用一些內核函數。對於進程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,輸出效果圖如下所示;

進程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結構,輸出效果圖如下所示;

進程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結構互轉,輸出效果圖如下所示;

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/17173641.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## 前言 多級指針在C語言中是一種特殊的指針類型,它可以指向其他指針的指針。 通過多級指針,我們可以間接地訪問或修改存儲在記憶體中的數據。 在本文中,我們將討論多級指針的概念、使用方法、使用場景以及常見錯誤及其避免方法。 ## 一、人物簡介 - 第一位閃亮登場,有請今後會一直教我們C語言的老師 —— ...
  • [TOC](【後端面經-Spring】Spring 中 bean 的生命周期) ## 1.bean簡介 - bean是一個對象,是由Spring中的IoC創建、實例化的對象。 - 一般的java對象,使用的時候創建,不需要就釋放記憶體進行銷毀,而bean的生命周期更加複雜 - 作用域 - `singl ...
  • # wordCount ```Scala package com.doit.day03 import scala.io.{BufferedSource, Source} object WordCountDemo { def main(args: Array[String]): Unit = { // ...
  • 歡迎來到本篇文章,鴿了好久了,今天繼續寫下 Spring 的內容:Spring 中 Bean 的基本概念、基本寫法和 3 種實例化 Bean 的方式等。 ...
  • 在網上找了好幾個方法, 最後還是出現各種問題,解決不了播放GIF的功能。 最後,通過ChatGPT給出了簡單明瞭的方案(使用第三方庫imageio和matplotlib.animation來實現),調試直接通過。 但有小瑕疵,就是顯示gif時隱藏掉坐標軸的功能無效,於是再做了一下優化。 [最終代碼] ...
  • > Python 是一門功能強大的編程語言,但在處理大規模數據或複雜計算任務時,性能可能成為一個瓶頸。幸運的是,Python 提供了多種方法來提升性能,其中之一是利用並行處理來加速迴圈操作。本文將介紹如何使用並行處理技術來優化 for 迴圈,從而提高 Python 程式的執行速度。我們將討論並行處理 ...
  • 集合組織特性相同的數據;泛型可以定義任何抽象數據類型;生命周期限制所有權的作用域範圍;錯誤處理使程式更健壯。 ...
  • ## 簡介 單例模式是一種常用的軟體設計模式,用於創建類型。通過單例模式的方法創建的類在當前進程中只有一個實例。單例模式的類只能允許一個實例存在。單例模式的作用是保證在整個應用程式的生命周期中,任何一個時刻,單例類的實例都只存在一個。 組成部分: 1. 私有化構造方法。 2. 私有化內部實例。 3. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...