peb(process environment block)是一个进程的环境块,其中保存了许多系统级别的信息,如进程的基地址、进程的环境变量、进程的命令行参数等。在windows内核中,peb被实现成了一个结构体,可以在kernel mode中通过undocumented native api(如zwqueryinformationprocess)读取。
在本篇文章中,我们将介绍如何使用golang语言实现一个简单的PEB查看器。
获取当前进程的句柄。
在golang中,我们可以使用syscall包中的GetCurrentProcess函数来获取当前进程的句柄。
handle, err := syscall.GetCurrentProcess()
if err != nil {
fmt.Println("获取当前进程句柄失败:", err)
return
}
defer syscall.CloseHandle(handle)查询当前进程的信息,包括PEB的地址。
立即学习“go语言免费学习笔记(深入)”;
在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中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号