1. 程式人生 > >利用32位PEB結構實現從程序ID中得到程序完整路徑

利用32位PEB結構實現從程序ID中得到程序完整路徑

//PEB.h
#pragma once		
#include <windows.h>
#include <iostream>
using namespace std;
//巨集,結構體定義,函式宣告;
//#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)


//要自己定義PEB結構:

//PEB結構中的查詢程序完整路徑的結構體
typedef struct _UNICODE_STRING
{
	UINT16 Length;//short型
	UINT16 MaximumLength;
	PWCHAR Buffer;//WCHAR*型(四位元組大小的指標)
}UNICODE_STRING,PUNICODE_STRING;
//UNICODE_STRING在Ring3層不公開,(在驅動層才公開)
typedef struct _RTL_USER_PROCESS_PARAMETERS_X86
{
	UINT32 MaximumLength;
	UINT32 Length;
	UINT32 Flags;
	UINT32 DebugFlags;
	PVOID  ConsoleHandle;
	UINT32 ConsoleFlags;//UINT 4B
	PVOID  StandardInput;
	PVOID  StandardOutput; 
	PVOID  StandardError;
	ULONG32 CurrentDirectory[3];//原來是十二位元組的結構體CURDIR
	UNICODE_STRING DllPath;
	UNICODE_STRING ImagePathName;//完整路徑
	UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS_X86, *PRTL_USER_PROCESS_PARAMETERS_X86;
typedef struct _PEB_X86 
{
	UINT8 InheritedAddressSpace;
	UINT8 ReadImageFileExecOptions;
	UINT8 BeingDebugged;
	UINT8 BitField;//聯合體(位域第一位的成員)
	PVOID Mutant;//互斥體
	PVOID ImageBaseAddress;//模組載入基地址
	PVOID Ldr;//結構體(Ldr實際上為結構體型別指標)
	PRTL_USER_PROCESS_PARAMETERS_X86 ProcessParameters;
}PEB_X86,*PPEB_X86;











#ifdef _WIN32
#define RTL_USER_PROCESS_PARAMETERS RTL_USER_PROCESS_PARAMETERS_X86
#define PPEB PPEB_X86
#define PEB  PEB_X86
#else
#define PPEB PPEB_X64
#define PEB  PEB_X64
#endif 



//有關查詢PEB地址的屬於程序記憶體的結構體
typedef struct _PROCESS_BASIC_INFORMATION
{
	NTSTATUS ExitStatus;
	PPEB     PebBaseAddress;   //地址
	ULONG    AffinityMask;
	LONG     BasePriority;
	ULONG    UniqueProcessId;
	ULONG    InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION;


//函式指標原型定義:
typedef enum _PROCESSINFOCLASS {
	ProcessBasicInformation,
	ProcessQuotaLimits,
	ProcessIoCounters,
	ProcessVmCounters,
	ProcessTimes,
	ProcessBasePriority,
	ProcessRaisePriority,
	ProcessDebugPort,
	ProcessExceptionPort,
	ProcessAccessToken,
	ProcessLdtInformation,
	ProcessLdtSize,
	ProcessDefaultHardErrorMode,
	ProcessIoPortHandlers,          // Note: this is kernel mode only
	ProcessPooledUsageAndLimits,
	ProcessWorkingSetWatch,
	ProcessUserModeIOPL,
	ProcessEnableAlignmentFaultFixup,
	ProcessPriorityClass,
	ProcessWx86Information,
	ProcessHandleCount,
	ProcessAffinityMask,
	ProcessPriorityBoost,
	ProcessDeviceMap,
	ProcessSessionInformation,
	ProcessForegroundInformation,
	ProcessWow64Information,
	ProcessImageFileName,
	ProcessLUIDDeviceMapsEnabled,
	ProcessBreakOnTermination,
	ProcessDebugObjectHandle,
	ProcessDebugFlags,
	ProcessHandleTracing,
	ProcessIoPriority,
	ProcessExecuteFlags,
	ProcessResourceManagement,
	ProcessCookie,
	ProcessImageInformation,
	MaxProcessInfoClass
} PROCESSINFOCLASS;






typedef
NTSTATUS(NTAPI *pfnNtQueryInformationProcess)(
	IN HANDLE ProcessHandle,//,程序控制代碼,查誰
	IN PROCESSINFOCLASS ProcessInformationClass,//列舉型別結構體,查什麼資訊
	OUT PVOID ProcessInformation,//結構體首地址,查出來的東西放在哪裡
	IN UINT32 ProcessInformationLength,//結構體大小
	OUT PUINT32 ReturnLength);//校驗值
//#define NTAPI __stdcall,指呼叫約定
//定義函式指標
//
BOOL GetProcessFullPathByProcessID(ULONG32 ProcessID, WCHAR* BufferData, ULONG BufferLegnth);
// PEB.cpp : 定義控制檯應用程式的入口點。
//

#include "PEB.h"

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
int main()
{
	BOOL bOk = FALSE;
	ULONG32 ProcessID = 0;
	WCHAR   BufferData[MAX_PATH] = { 0 };
	//定義完整路徑陣列,
	//windows規定存放完整路徑的陣列最大為260個位元組;
	printf("Input Process ID\r\n");
	scanf_s("%d", &ProcessID);
	bOk = GetProcessFullPathByProcessID(ProcessID,BufferData, MAX_PATH);
	//用自定義函式實現從程序ID得到程序完整路徑的過程(程序ID,完整路徑(存放的陣列名),陣列長度)
	
	cout << bOk << endl;
	if (bOk==TRUE)
	{ 
		printf("%S\r\n", BufferData);
		//BufferData雙字,故用大S輸出字串;
	}
    return 0;
}

BOOL GetProcessFullPathByProcessID(ULONG32 ProcessID,WCHAR* BufferData,ULONG BufferLegnth)
//先找到peb結構在程序中的地址,再從peb結構中找到完整路徑所在的地址;
//GetPebByProcessID,GetProcessFullPathByPeb,
{
	BOOL						bOk = FALSE;
	NTSTATUS					Status = 0;
	//一種int型返回值
	PEB	                        Peb = { 0 };
	//PEB結構體初始化
	HANDLE						ProcessHandle = NULL;

//*開啟目標程序空間
	ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION| PROCESS_VM_READ, FALSE, ProcessID);
	//ProcessHandle值是目標程序的程序控制代碼,是一個正數(-1是GetCurrentProcess函式,代表自己程序的偽控制代碼);
	//OpenProcess為Windows庫函式,“開啟(對方)程序空間”(想要行使的許可權(查詢|讀),BOOL型,目標程序ID)
	
	if (ProcessHandle == NULL)
	{
		return FALSE;
	}
	pfnNtQueryInformationProcess	NtQueryInformationProcess =
		(pfnNtQueryInformationProcess)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationProcess");
	//GetModuleHandle為Windows庫函式(GetModuleHandleW),“返回載入模組的基地址”(所以模組名字傳為雙字!)
	//GetProcAddress為Windows庫函式,“從當前程序的匯出表中獲得目標函式地址”(實際上就是遍歷匯出表來搜尋這個函式)
	//								本語句意思是從ntdll.dll的匯出表中搜索NtQueryInformationProcess函式!
	//此時函式地址得到的是自己的,(1自己的基地址,2自己的函式)//沒有用目標函式程序
	//	
	//GetProcAddress從當前函式的匯出表搜尋函式	//從當前程序空間的ntdll模組的匯出表中獲得一個函式NtQueryInformationProcess的地址
	//而不從匯入表中獲得(直接呼叫)原因是:
	//有可能就沒有匯入!(涉及到VS編譯未公開函式等原因)GetProcAddress返回的是一個泛型,

	//得到函式地址以後,發生強制型別轉換,
	if (NtQueryInformationProcess == NULL)
	{
		CloseHandle(ProcessHandle);
		ProcessHandle = NULL;
		return FALSE;
	}
	  
//**通過 NtQueryInformationProcess 獲得 ProcessBasicInformation
	PROCESS_BASIC_INFORMATION	pbi = { 0 };
	ULONG32						ReturnLength = 0;
	//開始查程序的ProcessBasicInformation
	Status = NtQueryInformationProcess(ProcessHandle,
		ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION),
		(PUINT32)&ReturnLength);
	//引數:查誰,查什麼,
	
	//Status=0
	//NT_SUCCESS(Status)=1
	if (!NT_SUCCESS(Status))
	{
		CloseHandle(ProcessHandle);
		ProcessHandle = NULL;
		return FALSE;
	}

//***通過ReadProcessMemory 從程序裡面 PebBaseAddress 記憶體資料讀取出來
	bOk = ReadProcessMemory(ProcessHandle, pbi.PebBaseAddress, &Peb, sizeof(PEB), (SIZE_T*)&ReturnLength);
	//ReadProcessMemory是Windows庫函式,“讀程序空間的記憶體”,
	//(目標程序控制代碼,目標地址,讀到peb這個結構體指標裡,結構體大小,校驗值)
	//SIZE_T相當於ULONG_PTR;
	if (bOk == FALSE)
	{
		CloseHandle(ProcessHandle);
		ProcessHandle = NULL;
		return FALSE;
	}

	RTL_USER_PROCESS_PARAMETERS RtlUserProcessParameters = { 0 };
	bOk = ReadProcessMemory(ProcessHandle, Peb.ProcessParameters, &RtlUserProcessParameters,
		sizeof(RTL_USER_PROCESS_PARAMETERS), (SIZE_T*)&ReturnLength);

	if (RtlUserProcessParameters.ImagePathName.Buffer!=NULL)
	{
		ULONG v1 = 0;
		if (RtlUserProcessParameters.ImagePathName.Length<BufferLegnth)
		{
			v1 = RtlUserProcessParameters.ImagePathName.Length;
		}
		else 
		{
			v1 = BufferLegnth-10;
		}
		bOk = ReadProcessMemory(ProcessHandle, RtlUserProcessParameters.ImagePathName.Buffer,
			BufferData,v1,(SIZE_T*)&ReturnLength);	
		if (bOk == FALSE)
		{
			CloseHandle(ProcessHandle);
			ProcessHandle = NULL;
			return FALSE;
		}
	}
	CloseHandle(ProcessHandle);
	return TRUE;
}



本程式只使用於32位!

即只能尋找32位程序的完整路徑!

使用PEB結構以得到程序的完整路徑(包括從程序ID得到程序PEB地址和從PEB結構得到程序完整路徑)

GetProcessFullPathByProcessID相當於兩個函式GetPebByProcessID,GetProcessFullPathByPeb的合併。

從PEB結構中得到程序的完整路徑:

通過PEB結構中的RTL_USER_PROCESS_PARAMETERS型別的ProcessParameters成員,ProcessParameters成員中有ImagePathName成員。

從程序ID中得到程序PEB結構地址:
使用微軟未公開的函式NtQueryInformationProcess,其中用到列舉型別結構體PROCESSINFOCLASS,該結構體的成員PROCESS_BASIC_INFORMATION,從其中再找到PPEB型別的PebBaseAddress成員,該結構體的地址即為peb的地址。找到了peb的地址相當於已經可以開始讀取PEB的結構了。