mirror of
https://github.com/NomanNasirMinhas/Ringer
synced 2026-08-19 05:01:13 +00:00
137 lines
3.6 KiB
Go
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
|
|
}
|