Files
NomanNasirMinhas 28f02e2fd2 init
2026-08-16 19:28:23 +03:00

137 lines
3.6 KiB
Go

package verify
import (
"encoding/binary"
"strings"
"unsafe"
"golang.org/x/sys/windows"
"ringer/extract"
)
var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procOpenFileMapping = modkernel32.NewProc("OpenFileMappingW")
)
// openFileMapping opens an existing named file mapping. x/sys/windows has no
// OpenFileMapping wrapper, so this calls the API directly. Unlike
// CreateFileMapping(InvalidHandle, ...), it does not create a new section when
// the name is absent — it returns ERROR_FILE_NOT_FOUND instead.
func openFileMapping(access uint32, inheritHandle bool, name *uint16) (windows.Handle, error) {
var inherit uintptr
if inheritHandle {
inherit = 1
}
r0, _, lastErr := procOpenFileMapping.Call(uintptr(access), inherit, uintptr(unsafe.Pointer(name)))
h := windows.Handle(r0)
if h == 0 {
return 0, lastErr
}
return h, nil
}
// SectionResult is the outcome of mapping and reading a shared-memory section.
type SectionResult struct {
Name string
Success bool
Error string
RegionSize uintptr
Data []byte // first N bytes of the mapped region (raw)
FirstDword uint32 // first 4 bytes, often a ring-buffer write index
}
// SectionCandidates derives candidate user-mode section names from extracted
// strings. Kernel section names (\BaseNamedObjects\X) map to Global\X in the
// user-mode namespace; Global\X is also tried as-is.
func SectionCandidates(strs []extract.StringInfo) []string {
seen := map[string]bool{}
var out []string
add := func(p string) {
if p == "" || seen[p] {
return
}
seen[p] = true
out = append(out, p)
}
for _, s := range strs {
switch s.Category {
case "section":
if strings.HasPrefix(s.Value, `\BaseNamedObjects\`) {
name := strings.TrimPrefix(s.Value, `\BaseNamedObjects\`)
add(`Global\` + name)
add(name)
} else if strings.HasPrefix(s.Value, `Global\`) {
add(s.Value)
}
}
}
return out
}
// VerifySections maps each candidate section and reads its contents. dumpSize
// caps how many bytes are copied back for display.
func VerifySections(names []string, dumpSize int) []SectionResult {
if dumpSize <= 0 {
dumpSize = 4096
}
var results []SectionResult
for _, name := range names {
results = append(results, mapSection(name, dumpSize))
}
return results
}
// mapSection opens a file mapping by name, maps it into the process, and reads
// the first dumpSize bytes.
//
//go:nocheckptr
func mapSection(name string, dumpSize int) SectionResult {
res := SectionResult{Name: name}
p, err := windows.UTF16PtrFromString(name)
if err != nil {
res.Error = err.Error()
return res
}
h, err := openFileMapping(windows.FILE_MAP_READ, false, p)
if err != nil {
res.Error = err.Error()
return res
}
defer windows.CloseHandle(h)
addr, err := windows.MapViewOfFile(h, windows.FILE_MAP_READ, 0, 0, 0)
if err != nil {
res.Error = err.Error()
return res
}
defer windows.UnmapViewOfFile(addr)
var mbi windows.MemoryBasicInformation
if err := windows.VirtualQuery(addr, &mbi, unsafe.Sizeof(mbi)); err != nil {
res.Error = err.Error()
return res
}
res.RegionSize = mbi.RegionSize
read := mbi.RegionSize
if read > uintptr(dumpSize) {
read = uintptr(dumpSize)
}
// addr is a memory-mapped region, not a GC-managed object, so the
// uintptr->pointer conversion is safe here (go vet flags this as a false
// positive). The bytes are copied out immediately so the mapping can be
// released.
view := unsafe.Slice((*byte)(unsafe.Pointer(addr)), int(read))
res.Data = make([]byte, len(view))
copy(res.Data, view)
if len(res.Data) >= 4 {
res.FirstDword = binary.LittleEndian.Uint32(res.Data[:4])
}
res.Success = true
return res
}