您好,欢迎访问一九零五行业门户网

golang怎么实现peb

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_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。
读取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的详细内容。
其它类似信息

推荐信息