Maison  >  Article  >  développement back-end  >  Comment implémenter Peb dans Golang

Comment implémenter Peb dans Golang

PHPz
PHPzoriginal
2023-04-14 11:21:29791parcourir

PEB (Process Environment Block) est le bloc d'environnement d'un processus, qui stocke de nombreuses informations au niveau du système, telles que l'adresse de base du processus, les variables d'environnement du processus, les paramètres de ligne de commande du processus, etc. . Dans le noyau Windows, PEB est implémenté sous forme de structure, qui peut être lue via une API native non documentée (telle que ZwQueryInformationProcess) en mode noyau.

Dans cet article, nous présenterons comment implémenter un simple visualiseur PEB en utilisant le langage Golang.

Étapes pour lire PEB

  1. Prenez le contrôle du processus en cours.

    En Golang, nous pouvons utiliser la fonction GetCurrentProcess dans le package syscall pour obtenir le handle du processus en cours.

    handle, err := syscall.GetCurrentProcess()
    if err != nil {
        fmt.Println("获取当前进程句柄失败:", err)
        return
    }
    defer syscall.CloseHandle(handle)
  2. Interrogez les informations du processus en cours, y compris l'adresse du PEB.

    Sous Windows, nous pouvons utiliser ZwQueryInformationProcess ou NtQueryInformationProcess pour lire les informations sur le processus. Cependant, ces API ne sont pas directement exposées dans Golang, nous devons donc utiliser le package unsafe pour appeler les fonctions système.

    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
    }

    Dans le code ci-dessus, nous définissons une structure PROCESS_BASIC_INFORMATION pour enregistrer les informations de processus renvoyées par la fonction NtQueryInformationProcess. Nous indiquons au système les informations que nous devons lire en spécifiant la valeur d'énumération PROCESS_BASIC_INFORMATION_CLASS. Ce dont nous avons besoin ici, ce sont les informations PEB. De plus, nous devons également fournir un tampon pour sauvegarder les informations renvoyées et la taille de ce tampon.

    Pour une implémentation spécifique, veuillez vous référer à ce projet [https://github.com/processhacker/phnt](https://github.com/processhacker/phnt), qui implémente certaines API système et fournit certaines structures de données pour. exemple PROCESS_BASIC_INFORMATION.

  3. Lisez les informations dans la structure PEB.

    PEB est une structure très importante qui stocke des informations sur de nombreux processus. Voici la définition de 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;

    Nous pouvons utiliser le package unsafe de golang pour lire ces données. Par exemple, nous pouvons utiliser le code suivant pour lire l'ImageBaseAddress de PEB :

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

    Dans le code ci-dessus, nous définissons d'abord une structure PEB et spécifions les types pour les champs de la structure. Ensuite, nous avons implémenté une fonction GetImageBaseAddress pour renvoyer le champ ImageBaseAddress dans le PEB. Enfin, nous lisons les informations dans le PEB en convertissant l'adresse de base du PEB en type *PEB.

  4. Lisez les informations du module du processus.

    Après avoir obtenu l'ImageBaseAddress dans PEB, nous pouvons parcourir la InMemoryOrderModuleList dans PEB_LDR_DATA pour obtenir toutes les informations sur le module chargé dans le processus.

    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;

    Nous pouvons utiliser le code suivant pour parcourir les informations du module :

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

    Dans le code ci-dessus, nous définissons d'abord une structure LDR_DATA_TABLE_ENTRY pour enregistrer les informations du module. Ensuite, nous définissons une structure PEB_LDR_DATA et convertissons le pointeur peb.Ldr en ce pointeur de structure. Enfin, nous parcourons la liste InMemoryOrderModuleList et lisons chaque module.

    Après avoir obtenu l'adresse de base du module, nous pouvons utiliser la fonction ReadProcessMemory pour lire les données dans le module. Pour une implémentation spécifique, veuillez vous référer à ce projet [https://github.com/AllenDang/w32/blob/master/process_windows.go](https://github.com/AllenDang/w32/blob/master/process_windows.go ), Il implémente des fonctions de lecture des données du processus.

    Cependant, il convient de noter que si le processus que nous souhaitons obtenir est un autre processus, nous devons alors spécifier les autorisations d'accès du processus lors de la lecture des données du processus. Dans Golang, nous pouvons utiliser la fonction CreateToolhelp32Snapshot pour obtenir une liste de tous les processus et spécifier des autorisations d'accès spécifiques lors de l'obtention du handle de processus.

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

Conclusion

Cet article présente comment implémenter un simple visualiseur PEB en utilisant le langage Golang. PEB est un bloc d'environnement de processus, implémenté sous forme de structure dans le noyau Windows, qui stocke de nombreuses informations au niveau du système. En utilisant le package non sécurisé de Golang, nous pouvons lire les informations PEB et les informations sur le module du processus. Cependant, il convient de noter que lors de la lecture des informations PEB et des informations de module d'un autre processus, vous devez spécifier les autorisations d'accès.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Article précédent:Comment apprendre le golangArticle suivant:Comment apprendre le golang