PEB (プロセス環境ブロック) はプロセスの環境ブロックであり、プロセスのベース アドレス、プロセスの環境変数、プロセスのコマンド ライン パラメーターなど、多くのシステム レベルの情報が保存されます。 、など。 Windows カーネルでは、PEB は構造として実装されており、カーネル モードで文書化されていないネイティブ API (ZwQueryInformationProcess など) を通じて読み取ることができます。
この記事では、golang 言語を使用して簡単な PEB ビューアを実装する方法を紹介します。
現在のプロセスのハンドルを取得します。
golang では、syscall パッケージの GetCurrentProcess 関数を使用して、現在のプロセスのハンドルを取得できます。
handle, err := syscall.GetCurrentProcess() if err != nil { fmt.Println("获取当前进程句柄失败:", err) return } defer syscall.CloseHandle(handle)
PEB のアドレスを含む、現在のプロセスの情報をクエリします。
Windows では、ZwQueryInformationProcess または NtQueryInformationProcess を使用してプロセス情報を読み取ることができます。ただし、これらの API は golang で直接公開されていないため、システム関数を呼び出すには安全でないパッケージを使用する必要があります。
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 }
上記のコードでは、NtQueryInformationProcess 関数によって返されたプロセス情報を保存するための PROCESS_BASIC_INFORMATION 構造体を定義します。 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 の安全でないパッケージを使用してこれらのデータを読み取ることができます。たとえば、次のコードを使用して 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 構造を定義し、構造内のフィールドの型を指定します。次に、PEB の ImageBaseAddress フィールドを返す GetImageBaseAddress 関数を実装しました。最後に、PEB のベース アドレスを *PEB タイプに変換することで、PEB 内の情報を読み取ります。
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を実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。