ホームページ  >  記事  >  バックエンド開発  >  golangでpebを実装する方法

golangでpebを実装する方法

PHPz
PHPzオリジナル
2023-04-14 11:21:29791ブラウズ

PEB (プロセス環境ブロック) はプロセスの環境ブロックであり、プロセスのベース アドレス、プロセスの環境変数、プロセスのコマンド ライン パラメーターなど、多くのシステム レベルの情報が保存されます。 、など。 Windows カーネルでは、PEB は構造として実装されており、カーネル モードで文書化されていないネイティブ 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 を使用してプロセス情報を読み取ることができます。ただし、これらの API は golang で直接公開されていないため、システム関数を呼び出すには安全でないパッケージを使用する必要があります。

    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
    }

    上記のコードでは、NtQueryInformationProcess 関数によって返されたプロセス情報を保存するための PROCESS_BASIC_INFORMATION 構造体を定義します。 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 の安全でないパッケージを使用してこれらのデータを読み取ることができます。たとえば、次のコードを使用して 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 構造を定義し、構造内のフィールドの型を指定します。次に、PEB の ImageBaseAddress フィールドを返す GetImageBaseAddress 関数を実装しました。最後に、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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
前の記事:Golangの学び方次の記事:Golangの学び方