69 lines
2.4 KiB
Go
69 lines
2.4 KiB
Go
//go:build windows
|
|
|
|
package winpwn
|
|
|
|
import (
|
|
"fmt"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
// Offsets within ntdll's x64 _PEB, confirmed via `dt ntdll!_PEB` the same
|
|
// way heap.go's _HEAP offsets were -- see heap.go's top comment.
|
|
const (
|
|
pebOffNumberOfHeaps = 0x0e8
|
|
pebOffProcessHeaps = 0x0f0 // PVOID*, an array of NumberOfHeaps heap addresses
|
|
)
|
|
|
|
// ListProcessHeaps enumerates every heap that exists in pid's address
|
|
// space by walking PEB.ProcessHeaps from outside the process -- the exact
|
|
// same array GetProcessHeaps() reads from inside one, just reached via
|
|
// ReadProcessMemory the way ResolveModuleBase (procmem_windows.go) reads
|
|
// PEB.Ldr for the loaded-module list instead of needing a leak. The
|
|
// default process heap (PEB.ProcessHeap) is always included, since
|
|
// HeapCreate registers every heap -- including the default one ntdll
|
|
// creates before main() even runs -- into this same array.
|
|
//
|
|
// This is the natural companion to DetectHeapKind/ReadHeap: once you have
|
|
// a PID and nothing else, ListProcessHeaps is how you find an address
|
|
// worth handing to either of them, instead of needing a leaked heap handle
|
|
// from the target's own output first.
|
|
func ListProcessHeaps(pid uint32) ([]uint64, error) {
|
|
mem, err := OpenProcessMemory(pid, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer mem.Close()
|
|
|
|
var pbi windows.PROCESS_BASIC_INFORMATION
|
|
var retLen uint32
|
|
if err := windows.NtQueryInformationProcess(mem.Handle, windows.ProcessBasicInformation,
|
|
unsafe.Pointer(&pbi), uint32(unsafe.Sizeof(pbi)), &retLen); err != nil {
|
|
return nil, fmt.Errorf("NtQueryInformationProcess(ProcessBasicInformation): %w", err)
|
|
}
|
|
pebAddr := uint64(uintptr(unsafe.Pointer(pbi.PebBaseAddress)))
|
|
if pebAddr == 0 {
|
|
return nil, fmt.Errorf("PEB address for pid %d is null", pid)
|
|
}
|
|
|
|
numHeaps, err := readUint32AtValue(mem, int64(pebAddr)+pebOffNumberOfHeaps)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading PEB.NumberOfHeaps: %w", err)
|
|
}
|
|
arrayAddr, err := readUint64At(mem, int64(pebAddr)+pebOffProcessHeaps)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading PEB.ProcessHeaps: %w", err)
|
|
}
|
|
|
|
heaps := make([]uint64, 0, numHeaps)
|
|
for i := uint32(0); i < numHeaps; i++ {
|
|
addr, err := readUint64At(mem, int64(arrayAddr)+int64(i)*8)
|
|
if err != nil {
|
|
return heaps, fmt.Errorf("reading ProcessHeaps[%d] (of %d): %w", i, numHeaps, err)
|
|
}
|
|
heaps = append(heaps, addr)
|
|
}
|
|
return heaps, nil
|
|
}
|