• 技术文章 >后端开发 >Golang

    golang怎么实现peb

    PHPzhongPHPzhong2023-04-14 11:21:52原创47

    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_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。

    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\n", 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\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中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:
    上一篇:golang 怎么学 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • 如何在Linux上安装golang 1.6版本• 如何在Win和MacOS中安装Git和Golang• 如何创建Go语言项目• Golang如何进行数据库查询• golang怎么进行浮点数的除法操作(两种方式)
    1/1

    PHP中文网