利用32位PEB結構實現從程序ID中得到程序完整路徑
阿新 • • 發佈:2019-01-30
//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的結構了。