您的位置:首页 > 技术中心 > 其他 >

golang怎么实现peb

时间:2023-04-15 04:48

PEB(Process Environment Block)是一个进程的环境块,其中保存了许多系统级别的信息,如进程的基地址、进程的环境变量、进程的命令行参数等。在Windows内核中,PEB被实现成了一个结构体,可以在Kernel Mode中通过Undocumented Native API(如ZwQueryInformationProcess)读取。

在本篇文章中,我们将介绍如何使用golang语言实现一个简单的PEB查看器。

读取PEB的步骤

  1. 获取当前进程的句柄。

    在golang中,我们可以使用syscall包中的GetCurrentProcess函数来获取当前进程的句柄。

    handle, err := syscall.GetCurrentProcess()if err != nil {    fmt.Println("获取当前进程句柄失败:", err)    return}defer syscall.CloseHandle(handle)
  2. 查询当前进程的信息,包括PEB的地址。

    在Windows中,我们可以使用ZwQueryInformationProcess或者NtQueryInformationProcess来读取进程信息。不过,在golang中这些API并没有直接暴露出来,因此我们需要使用unsafe包来调用系统函数。

    var pbi PROCESS_BASIC_INFORMATIONvar returnLength uint32ntStatus := 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。

  3. 读取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
    ", peb.GetImageBaseAddress())

    在上面的代码中,我们首先定义了一个PEB结构体,并且给结构体中的字段都指定了类型。接着,我们实现了一个GetImageBaseAddress函数,用来返回PEB中的ImageBaseAddress字段。最后,我们通过将PEB的基地址转换为*PEB类型,来读取PEB中的信息。

  4. 读取进程的模块信息。

    在获取了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
    ", 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
    ", 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的详细内容,更多请关注Gxl网其它相关文章!

热门排行

今日推荐

热门手游