PEB(Process Environment Block)是一個進程的環境區塊,其中保存了許多系統層級的信息,如進程的基底位址、進程的環境變數、進程的命令列參數等。在Windows核心中,PEB被實作成了一個結構體,可以在Kernel Mode中透過Undocumented Native API(如ZwQueryInformationProcess)讀取。
在本篇文章中,我們將介紹如何使用golang語言實作一個簡單的PEB檢視器。
讀取PEB的步驟
-
取得目前程序的句柄。
在golang中,我們可以使用syscall套件中的GetCurrentProcess函數來取得目前程序的句柄。
handle, err := syscall.GetCurrentProcess() if err != nil { fmt.Println("获取当前进程句柄失败:", err) return } defer syscall.CloseHandle(handle)
-
查詢目前進程的信息,包括PEB的位址。
在Windows中,我們可以使用ZwQueryInformationProcess或NtQueryInformationProcess來讀取進程資訊。不過,在golang中這些API並沒有直接暴露出來,因此我們需要使用unsafe套件來呼叫系統函數。
var pbi PROCESS_BASIC_INFORMATION var returnLength uint32 ntStatus := NtQueryInformationProcess( handle, PROCESS_BASIC_INFORMATION_CLASS, uintptr(unsafe.Pointer(&pbi)), uint32(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&returnLength)), ) if ntStatus != STATUS_SUCCESS { fmt.Println("获取进程PEB信息失败:", ntStatus) return }
在上面的程式碼中,我們定義了一個PROCESS_BASIC_INFORMATION結構體,用來保存NtQueryInformationProcess函數傳回的進程資訊。我們透過指定PROCESS_BASIC_INFORMATION_CLASS枚舉值來告訴系統我們需要讀取的信息,這裡我們需要的是PEB資訊。另外,我們還需要提供一個緩衝區來保存傳回的信息,和這個緩衝區的大小。
具體的實作可以參考這個專案[https://github.com/processhacker/phnt](https://github.com/processhacker/phnt),它實作了一些系統API,並且提供了一些資料結構,例如PROCESS_BASIC_INFORMATION。
-
讀取PEB結構體中的資訊。
PEB是一個非常重要的結構體,其中保存了許多進程的資訊。下面是PEB的定義:
typedef struct _PEB { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; BOOLEAN SpareBool; HANDLE Mutant; PVOID ImageBaseAddress; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID SubSystemData; PVOID ProcessHeap; PRTL_CRITICAL_SECTION FastPebLock; PVOID AtlThunkSListPtr; PVOID IFEOKey; PVOID CrossProcessFlags; PVOID UserSharedInfoPtr; ULONG SystemReserved[1]; ULONG AtlThunkSListPtr32; PVOID ApiSetMap; } PEB, *PPEB;
我們可以使用golang的unsafe套件來讀取這些資料。例如,我們可以使用下面的程式碼來讀取PEB的ImageBaseAddress:
type PEB struct { InheritedAddressSpace bool ReadImageFileExecOptions bool BeingDebugged bool SpareBool bool Mutant syscall.Handle ImageBaseAddress uintptr Ldr *PEB_LDR_DATA ProcessParameters *RTL_USER_PROCESS_PARAMETERS SubSystemData uintptr ProcessHeap uintptr FastPebLock *RTL_CRITICAL_SECTION AtlThunkSListPtr uintptr IFEOKey uintptr CrossProcessFlags uintptr UserSharedInfoPtr uintptr SystemReserved [1]uint32 AtlThunkSListPtr32 uintptr ApiSetMap uintptr } func (p *PEB) GetImageBaseAddress() uintptr { return p.ImageBaseAddress } peb := (*PEB)(unsafe.Pointer(pbi.PebBaseAddress)) fmt.Printf("ImageBaseAddress: 0x%x\n", peb.GetImageBaseAddress())
在上面的程式碼中,我們先定義了一個PEB結構體,並且給結構體中的欄位都指定了型別。接著,我們實作了一個GetImageBaseAddress函數,用來傳回PEB中的ImageBaseAddress欄位。最後,我們透過將PEB的基底位址轉換為*PEB類型,來讀取PEB中的資訊。
-
讀取進程的模組資訊。
在取得了PEB中的ImageBaseAddress後,我們可以遍歷PEB_LDR_DATA中的InMemoryOrderModuleList來取得進程中載入的所有模組資訊。
typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; struct { PVOID LoadedImports; PVOID EntryPointActivationContext; }; }; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; typedef struct _PEB_LDR_DATA { ULONG Length; BOOLEAN Initialized; HANDLE SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID EntryInProgress; BOOLEAN ShutdownInProgress; HANDLE ShutdownThreadId; } PEB_LDR_DATA, *PPEB_LDR_DATA;
我們可以使用如下的程式碼來遍歷模組資訊:
type LDR_DATA_TABLE_ENTRY struct { InLoadOrderLinks LIST_ENTRY InMemoryOrderLinks LIST_ENTRY InInitializationOrderLinks LIST_ENTRY DllBase uintptr EntryPoint uintptr SizeOfImage uint32 FullDllName UNICODE_STRING BaseDllName UNICODE_STRING Flags uint32 LoadCount uint16 TlsIndex uint16 HashLinks LIST_ENTRY TimeDateStamp uint32 } type PEB_LDR_DATA struct { Length uint32 Initialized bool SsHandle syscall.Handle InLoadOrderModuleList LIST_ENTRY InMemoryOrderModuleList LIST_ENTRY InInitializationOrderModuleList LIST_ENTRY } pebLdrData := (*PEB_LDR_DATA)(unsafe.Pointer(peb.Ldr)) moduleList := (*LIST_ENTRY)(unsafe.Pointer(&pebLdrData.InMemoryOrderModuleList)) for moduleList.Flink != uintptr(unsafe.Pointer(&pebLdrData.InMemoryOrderModuleList)) { ldrDataTableEntry := (*LDR_DATA_TABLE_ENTRY)(unsafe.Pointer(moduleList.Flink)) moduleName := WcharPtrToString(ldrDataTableEntry.BaseDllName.Buffer, uint32(ldrDataTableEntry.BaseDllName.Length/2)) moduleBase := ldrDataTableEntry.DllBase moduleSize := ldrDataTableEntry.SizeOfImage moduleEntry := ldrDataTableEntry.EntryPoint moduleList = (*LIST_ENTRY)(unsafe.Pointer(moduleList.Flink)) fmt.Printf("模块名称:%s,基地址:%x,大小:%x,入口点:%x\n", moduleName, moduleBase, moduleSize, moduleEntry) }
在上面的程式碼中,我們先定義了一個LDR_DATA_TABLE_ENTRY結構體,用來保存模組的資訊。然後我們定義了一個PEB_LDR_DATA結構體,並且將peb.Ldr指標轉換為這個結構體指標。最後,我們遍歷InMemoryOrderModuleList鍊錶,對每個模組進行讀取操作。
在取得到模組的基底位址後,我們可以用ReadProcessMemory函數來讀取模組中的資料。具體的實作可以參考這個專案[https://github.com/AllenDang/w32/blob/master/process_windows.go](https://github.com/AllenDang/w32/blob/master/process_windows.go),它實現了從進程中讀取資料的函數。
不過需要注意的是,如果我們要取得的進程是另一個進程,那麼在讀取進程資料的時候需要指定進程的存取權限。在golang中,我們可以使用CreateToolhelp32Snapshot函數來取得所有進程列表,並且在取得進程句柄時指定特定的存取權限。
const ( PROCESS_QUERY_INFORMATION = 0x0400 PROCESS_VM_READ = 0x0010 PROCESS_VM_WRITE = 0x0020 PROCESS_VM_OPERATION = 0x0008 PROCESS_CREATE_THREAD = 0x0002 PROCESS_CREATE_PROCESS = 0x0080 PROCESS_TERMINATE = 0x0001 PROCESS_ALL_ACCESS = 0x1F0FFF TH32CS_SNAPPROCESS = 0x00000002 ) func openProcess(pid uint32) (handle syscall.Handle, err error) { handle, err = syscall.OpenProcess(PROCESS_VM_READ|PROCESS_QUERY_INFORMATION|PROCESS_VM_WRITE, false, pid) return } func main() { snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) defer syscall.CloseHandle(snapshot) var procEntry PROCESSENTRY32 procEntry.Size = uint32(unsafe.Sizeof(procEntry)) var ( handle syscall.Handle err error ) if Process32First(snapshot, &procEntry) { for { if strings.EqualFold(strings.ToLower(WcharPtrToString(procEntry.ExeFile[:])), "notepad.exe") { fmt.Printf("找到 notepad 进程,pid:%d\n", procEntry.ProcessID) handle, err = openProcess(procEntry.ProcessID) if err != nil { fmt.Println("打开进程失败:", err) } } if !Process32Next(snapshot, &procEntry) { break } } } }
結語
本文介紹如何使用golang語言實作一個簡單的PEB檢視器。 PEB是進程環境區塊,在Windows核心中實現了一個結構體,其中保存了許多系統層級的資訊。透過使用golang的unsafe包,我們可以讀取進程的PEB資訊和模組資訊。不過要注意的是,在讀取另一個進程的PEB資訊和模組資訊時,需要指定存取權限。
以上是golang怎麼實作peb的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Golang更適合高並發任務,而Python在靈活性上更有優勢。 1.Golang通過goroutine和channel高效處理並發。 2.Python依賴threading和asyncio,受GIL影響,但提供多種並發方式。選擇應基於具體需求。

Golang和C 在性能上的差異主要體現在內存管理、編譯優化和運行時效率等方面。 1)Golang的垃圾回收機制方便但可能影響性能,2)C 的手動內存管理和編譯器優化在遞歸計算中表現更為高效。

selectgolangforhighpperformanceandcorrency,ifealforBackendServicesSandNetwork程序; selectpypypythonforrapiddevelopment,dataScience和machinelearningDuetoitsverserverserverserversator versator anderticality andextility andextentensivelibraries。

Golang和Python各有优势:Golang适合高性能和并发编程,Python适用于数据科学和Web开发。Golang以其并发模型和高效性能著称,Python则以简洁语法和丰富库生态系统著称。

Golang和Python分別在哪些方面更易用和學習曲線更平緩? Golang更適合高並發和高性能需求,學習曲線對有C語言背景的開發者較平緩。 Python更適合數據科學和快速原型設計,學習曲線對初學者非常平緩。

Golang和C 在性能競賽中的表現各有優勢:1)Golang適合高並發和快速開發,2)C 提供更高性能和細粒度控制。選擇應基於項目需求和團隊技術棧。

Golang適合快速開發和並發編程,而C 更適合需要極致性能和底層控制的項目。 1)Golang的並發模型通過goroutine和channel簡化並發編程。 2)C 的模板編程提供泛型代碼和性能優化。 3)Golang的垃圾回收方便但可能影響性能,C 的內存管理複雜但控制精細。

goimpactsdevelopmentpositationality throughspeed,效率和模擬性。 1)速度:gocompilesquicklyandrunseff,IdealforlargeProjects.2)效率:效率:ITScomprehenSevestAndardArdardArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdEcceSteral Depentencies,增強的Depleflovelmentimency.3)簡單性。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

Atom編輯器mac版下載
最受歡迎的的開源編輯器

Dreamweaver CS6
視覺化網頁開發工具

Dreamweaver Mac版
視覺化網頁開發工具