在核心開發中,經常需要進行程序和控制程式碼之間的互相轉換。程序通常由一個唯一的程序識別符號(PID)來標識,而控制程式碼是指對核心物件的參照。在Windows核心中,EProcess
結構表示一個程序,而HANDLE是一個控制程式碼。
為了實現程序與控制程式碼之間的轉換,我們需要使用一些核心函數。對於程序PID和控制程式碼的互相轉換,可以使用函數如OpenProcess
和GetProcessId
。OpenProcess函數接受一個PID作為引數,並返回一個控制程式碼。GetProcessId函數接受一個控制程式碼作為引數,並返回該程序的PID。
對於程序PID和EProcess
結構的互相轉換,可以使用函數如PsGetProcessId
和PsGetCurrentProcess
。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
函數將控制程式碼關閉,以避免資源洩漏。
綜上所述,我們可以通過這兩個核心函數實現Handle
和EProcess
之間的相互轉換,轉換程式碼如下所示;
#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;
}
編譯並執行如上這段程式碼片段,將把程序HANDLE
與EProcess
結構互轉,輸出效果圖如下所示;