99 lines
3.9 KiB
Go
99 lines
3.9 KiB
Go
package winpwn
|
|
|
|
import "testing"
|
|
|
|
// These tests pin heap_segment.go's decoding against the field values
|
|
// confirmed live against this machine's build (10.0.26100) via the Go
|
|
// OpenProcessMemory / ReadAt path -- the same empirical methodology
|
|
// heap_test.go and heap_lfh_test.go use.
|
|
|
|
func TestReadSegmentHeapRejectsNTHeap(t *testing.T) {
|
|
buf := make([]byte, 0x20)
|
|
binaryLEPutUint32(buf[0x10:], heapSignatureNT)
|
|
if _, err := ReadSegmentHeap(newByteReaderAt(buf), 0); err == nil {
|
|
t.Fatal("expected ReadSegmentHeap to reject an NT Heap signature")
|
|
}
|
|
}
|
|
|
|
func TestReadSegmentHeapDecodesCapturedFields(t *testing.T) {
|
|
// Use heapAddr=0 to keep the buffer small (matches heap_test.go's style).
|
|
// Buffer must cover LFH bucket array: segHeapOffLfhContext(0x340)+segLfhCtxOffBuckets(0x080)+129*8=0x7c8
|
|
buf := make([]byte, 0x800)
|
|
// Signature at +0x010: 0xddeeddee (confirmed live for heap_segment.exe)
|
|
binaryLEPutUint32(buf[segHeapOffSignature:], heapSignatureSegment)
|
|
// GlobalFlags at +0x014
|
|
binaryLEPutUint32(buf[segHeapOffGlobalFlags:], 0x00001000)
|
|
// VsContext (+0x280): TotalCommittedUnits=12, FreeCommittedUnits=3
|
|
vsBase := segHeapOffVsContext
|
|
binaryLEPutUint64(buf[vsBase+vsCtxOffTotalCommitted:], 12)
|
|
binaryLEPutUint64(buf[vsBase+vsCtxOffFreeCommitted:], 3)
|
|
// VsContext SubsegmentList head pointing to itself (empty list)
|
|
headAddr := uint64(vsBase + vsCtxOffSubsegmentList)
|
|
binaryLEPutUint64(buf[headAddr:], headAddr) // Flink
|
|
binaryLEPutUint64(buf[headAddr+8:], headAddr) // Blink
|
|
// LfhContext (+0x340): all bucket pointers 0 or stub
|
|
// (nothing to set; readSegmentLFHBuckets skips them)
|
|
|
|
h, err := ReadSegmentHeap(newByteReaderAt(buf), 0)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if h.Signature != heapSignatureSegment {
|
|
t.Errorf("Signature = 0x%08x, want 0x%08x", h.Signature, heapSignatureSegment)
|
|
}
|
|
if h.GlobalFlags != 0x00001000 {
|
|
t.Errorf("GlobalFlags = 0x%08x, want 0x00001000", h.GlobalFlags)
|
|
}
|
|
if h.VS.CommittedUnits != 12 {
|
|
t.Errorf("VS.CommittedUnits = %d, want 12", h.VS.CommittedUnits)
|
|
}
|
|
if h.VS.FreeUnits != 3 {
|
|
t.Errorf("VS.FreeUnits = %d, want 3", h.VS.FreeUnits)
|
|
}
|
|
if h.VS.SubsegmentCount != 0 {
|
|
t.Errorf("VS.SubsegmentCount = %d, want 0 (empty list)", h.VS.SubsegmentCount)
|
|
}
|
|
}
|
|
|
|
func TestAdjacentAddressPairsFindsStep(t *testing.T) {
|
|
// Mirror the heap_segment scenario: 5 allocations of 32-byte Profile,
|
|
// some adjacent (32 bytes apart) and some not.
|
|
addrs := []uint64{0x60a140, 0x60a160, 0x60a170, 0x60a1a0, 0x60b000}
|
|
// Adjacent pairs at step=32: (0x60a140, 0x60a160) ← gap=32; (0x60a170, 0x60a1a0) ← gap=48, nope
|
|
// Actually: 0x60a140+0x20=0x60a160 ✓; 0x60a160+0x10=0x60a170? no (10≠20=32)
|
|
// Let me fix: 0x60a140+32=0x60a160 ✓, 0x60a1a0+32=0x60a1c0 not in set
|
|
pairs := AdjacentAddressPairs(addrs, 32)
|
|
if len(pairs) != 1 {
|
|
t.Fatalf("got %d pairs, want 1; pairs=%v", len(pairs), pairs)
|
|
}
|
|
if pairs[0][0] != 0x60a140 || pairs[0][1] != 0x60a160 {
|
|
t.Errorf("pair = (0x%x, 0x%x), want (0x60a140, 0x60a160)", pairs[0][0], pairs[0][1])
|
|
}
|
|
}
|
|
|
|
func TestFindAdjacentPairReturnsFalseWhenNone(t *testing.T) {
|
|
addrs := []uint64{0x1000, 0x2000, 0x3000}
|
|
_, _, found := FindAdjacentPair(addrs, 32)
|
|
if found {
|
|
t.Fatal("expected found=false for addresses spaced 0x1000 apart, step=32")
|
|
}
|
|
}
|
|
|
|
func TestAdjacentBusyPairsOnEntryChain(t *testing.T) {
|
|
// Two busy entries packed with no free in between: should produce one pair.
|
|
// Two more with a free entry in between: should not.
|
|
entries := []HeapEntry{
|
|
{Addr: 0x1000, Size: 2, Flags: HeapEntryBusy}, // NextEntry = 0x1020
|
|
{Addr: 0x1020, Size: 2, Flags: HeapEntryBusy}, // NextEntry = 0x1040
|
|
{Addr: 0x1040, Size: 2, Flags: 0}, // free
|
|
{Addr: 0x1060, Size: 2, Flags: HeapEntryBusy},
|
|
}
|
|
pairs := AdjacentBusyPairs(entries)
|
|
if len(pairs) != 1 {
|
|
t.Fatalf("got %d pairs, want 1", len(pairs))
|
|
}
|
|
if pairs[0][0].Addr != 0x1000 || pairs[0][1].Addr != 0x1020 {
|
|
t.Errorf("pair addrs = (0x%x, 0x%x)", pairs[0][0].Addr, pairs[0][1].Addr)
|
|
}
|
|
}
|