Heim  >  Artikel  >  Backend-Entwicklung  >  So implementieren Sie Peb in Golang

So implementieren Sie Peb in Golang

PHPz
PHPzOriginal
2023-04-14 11:21:29838Durchsuche

PEB (Process Environment Block) ist der Umgebungsblock eines Prozesses, der viele Informationen auf Systemebene speichert, wie z. B. die Basisadresse des Prozesses, die Umgebungsvariablen des Prozesses, die Befehlszeilenparameter des Prozesses usw . Im Windows-Kernel ist PEB als Struktur implementiert, die über die undokumentierte native API (z. B. ZwQueryInformationProcess) im Kernelmodus gelesen werden kann.

In diesem Artikel stellen wir vor, wie man einen einfachen PEB-Viewer mithilfe der Golang-Sprache implementiert.

Schritte zum Lesen von PEB

  1. Erhalten Sie den Überblick über den aktuellen Prozess.

    In Golang können wir die Funktion GetCurrentProcess im Syscall-Paket verwenden, um das Handle des aktuellen Prozesses abzurufen.

    handle, err := syscall.GetCurrentProcess()
    if err != nil {
        fmt.Println("获取当前进程句柄失败:", err)
        return
    }
    defer syscall.CloseHandle(handle)
  2. Fragen Sie die Informationen des aktuellen Prozesses ab, einschließlich der Adresse des PEB.

    In Windows können wir ZwQueryInformationProcess oder NtQueryInformationProcess verwenden, um Prozessinformationen zu lesen. Diese APIs werden jedoch nicht direkt in Golang verfügbar gemacht, daher müssen wir das unsichere Paket verwenden, um Systemfunktionen aufzurufen.

    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
    }

    Im obigen Code definieren wir eine PROCESS_BASIC_INFORMATION-Struktur, um die von der NtQueryInformationProcess-Funktion zurückgegebenen Prozessinformationen zu speichern. Wir teilen dem System die Informationen mit, die wir lesen müssen, indem wir den Enumerationswert PROCESS_BASIC_INFORMATION_CLASS angeben. Was wir hier benötigen, sind die PEB-Informationen. Darüber hinaus müssen wir einen Puffer zum Speichern der zurückgegebenen Informationen und die Größe dieses Puffers bereitstellen.

    Informationen zur spezifischen Implementierung finden Sie in diesem Projekt [https://github.com/processhacker/phnt](https://github.com/processhacker/phnt), das einige System-APIs implementiert und einige Datenstrukturen bereitstellt Beispiel PROCESS_BASIC_INFORMATION.

  3. Lesen Sie die Informationen in der PEB-Struktur.

    PEB ist eine sehr wichtige Struktur, die Informationen über viele Prozesse speichert. Hier ist die Definition von 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;

    Wir können das unsichere Paket von Golang verwenden, um diese Daten zu lesen. Beispielsweise können wir den folgenden Code verwenden, um die ImageBaseAddress von PEB zu lesen:

    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())

    Im obigen Code definieren wir zunächst eine PEB-Struktur und geben Typen für die Felder in der Struktur an. Als Nächstes haben wir eine GetImageBaseAddress-Funktion implementiert, um das ImageBaseAddress-Feld im PEB zurückzugeben. Schließlich lesen wir die Informationen im PEB, indem wir die Basisadresse des PEB in den *PEB-Typ konvertieren.

  4. Lesen Sie die Modulinformationen des Prozesses.

    Nachdem wir die ImageBaseAddress in PEB erhalten haben, können wir die InMemoryOrderModuleList in PEB_LDR_DATA durchlaufen, um alle im Prozess geladenen Modulinformationen abzurufen.

    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;

    Wir können den folgenden Code verwenden, um Modulinformationen zu durchlaufen:

    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)
    }

    Im obigen Code definieren wir zunächst eine LDR_DATA_TABLE_ENTRY-Struktur, um Modulinformationen zu speichern. Dann definieren wir eine PEB_LDR_DATA-Struktur und konvertieren den peb.Ldr-Zeiger in diesen Strukturzeiger. Schließlich durchlaufen wir die InMemoryOrderModuleList-Liste und lesen jedes Modul.

    Nachdem wir die Basisadresse des Moduls erhalten haben, können wir die ReadProcessMemory-Funktion verwenden, um die Daten im Modul zu lesen. Informationen zur spezifischen Implementierung finden Sie in diesem Projekt [https://github.com/AllenDang/w32/blob/master/process_windows.go](https://github.com/AllenDang/w32/blob/master/process_windows.go). ), Es implementiert Funktionen zum Lesen von Daten aus dem Prozess.

    Es ist jedoch zu beachten, dass wir beim Lesen der Prozessdaten die Zugriffsberechtigungen des Prozesses angeben müssen, wenn es sich bei dem Prozess, den wir erhalten möchten, um einen anderen Prozess handelt. In Golang können wir die Funktion CreateToolhelp32Snapshot verwenden, um eine Liste aller Prozesse abzurufen und beim Abrufen des Prozesshandles spezifische Zugriffsberechtigungen anzugeben.

    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
                }
            }
        }
    }

Fazit

In diesem Artikel wird vorgestellt, wie man einen einfachen PEB-Viewer mithilfe der Golang-Sprache implementiert. PEB ist ein Prozessumgebungsblock, der als Struktur im Windows-Kernel implementiert ist und viele Informationen auf Systemebene speichert. Durch die Verwendung des unsicheren Pakets von Golang können wir die PEB-Informationen und Modulinformationen des Prozesses lesen. Es ist jedoch zu beachten, dass Sie beim Lesen der PEB-Informationen und Modulinformationen eines anderen Prozesses Zugriffsberechtigungen angeben müssen.

Das obige ist der detaillierte Inhalt vonSo implementieren Sie Peb in Golang. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:Wie man Golang lerntNächster Artikel:Wie man Golang lernt