feat(go): port memory-map detector tranche
This commit is contained in:
parent
c6f7219de8
commit
6a27e6e770
13 changed files with 377 additions and 41 deletions
|
|
@ -78,12 +78,13 @@ func CaptureWithPaths(paths Paths) (model.State, error) {
|
|||
}
|
||||
base := filepath.Join(procRoot, entry.Name())
|
||||
processes = append(processes, model.Process{
|
||||
PID: pid,
|
||||
Comm: strings.TrimSpace(readText(filepath.Join(base, "comm"))),
|
||||
Cmdline: strings.TrimSpace(strings.ReplaceAll(readText(filepath.Join(base, "cmdline")), "\x00", " ")),
|
||||
Exe: readLink(filepath.Join(base, "exe")),
|
||||
Environ: parseEnviron(readText(filepath.Join(base, "environ"))),
|
||||
FDTargets: readFDTargets(filepath.Join(base, "fd")),
|
||||
PID: pid,
|
||||
Comm: strings.TrimSpace(readText(filepath.Join(base, "comm"))),
|
||||
Cmdline: strings.TrimSpace(strings.ReplaceAll(readText(filepath.Join(base, "cmdline")), "\x00", " ")),
|
||||
Exe: readLink(filepath.Join(base, "exe")),
|
||||
Environ: parseEnviron(readText(filepath.Join(base, "environ"))),
|
||||
FDTargets: readFDTargets(filepath.Join(base, "fd")),
|
||||
MemoryMaps: parseMemoryMaps(readText(filepath.Join(base, "maps"))),
|
||||
})
|
||||
}
|
||||
sort.Slice(processes, func(i, j int) bool { return processes[i].PID < processes[j].PID })
|
||||
|
|
@ -193,6 +194,49 @@ func readFDTargets(path string) map[string]string {
|
|||
return result
|
||||
}
|
||||
|
||||
// parseMemoryMaps mirrors Python's split(maxsplit=5) parser. A process may
|
||||
// disappear or deny maps access between directory enumeration and this read;
|
||||
// returning an empty slice preserves the detector's fail-open behavior.
|
||||
func parseMemoryMaps(text string) []model.MemoryMap {
|
||||
result := make([]model.MemoryMap, 0)
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 5 {
|
||||
continue
|
||||
}
|
||||
bounds := strings.SplitN(fields[0], "-", 2)
|
||||
if len(bounds) != 2 {
|
||||
continue
|
||||
}
|
||||
start, err := strconv.ParseUint(bounds[0], 16, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
end, err := strconv.ParseUint(bounds[1], 16, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
path := ""
|
||||
if len(fields) > 5 {
|
||||
// Keep pathname spaces intact, just as Python's maxsplit=5 does.
|
||||
remaining := strings.TrimSpace(line)
|
||||
for index := 0; index < 5; index++ {
|
||||
space := strings.IndexAny(remaining, " \t")
|
||||
if space < 0 {
|
||||
remaining = ""
|
||||
break
|
||||
}
|
||||
remaining = strings.TrimSpace(remaining[space:])
|
||||
}
|
||||
path = remaining
|
||||
}
|
||||
result = append(result, model.MemoryMap{
|
||||
Start: start, End: end, Perms: fields[1], Path: path,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readText(path string) string {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ func TestCaptureUsesInjectableProcRoot(t *testing.T) {
|
|||
if err := os.WriteFile(filepath.Join(proc, "cmdline"), []byte("bash\x00-i\x00"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(proc, "maps"), []byte("7f00-8000 rwxp 00000000 00:00 0 /memfd:stage (deleted)\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(proc, "environ"), []byte("LD_PRELOAD=/tmp/x.so\x00A=B\x00"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -56,11 +59,23 @@ func TestCaptureUsesInjectableProcRoot(t *testing.T) {
|
|||
if got.Environ["LD_PRELOAD"] != "/tmp/x.so" || got.FDTargets["7"] != "/dev/input/event3" {
|
||||
t.Fatalf("missing environment or fd state: %#v", got)
|
||||
}
|
||||
if len(got.MemoryMaps) != 1 || got.MemoryMaps[0].Start != 0x7f00 ||
|
||||
got.MemoryMaps[0].Path != "/memfd:stage (deleted)" {
|
||||
t.Fatalf("missing memory-map state: %#v", got.MemoryMaps)
|
||||
}
|
||||
if state.LDPreload != "/tmp/global.so" {
|
||||
t.Fatalf("unexpected global preload: %q", state.LDPreload)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMemoryMapsPreservesPathSpaces(t *testing.T) {
|
||||
maps := parseMemoryMaps("7f00-8000 r-xp 00000000 00:00 0 /tmp/a path.so\n")
|
||||
if len(maps) != 1 || maps[0].Start != 0x7f00 || maps[0].End != 0x8000 ||
|
||||
maps[0].Perms != "r-xp" || maps[0].Path != "/tmp/a path.so" {
|
||||
t.Fatalf("unexpected maps: %#v", maps)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSSMatchesPythonShapes(t *testing.T) {
|
||||
text := "tcp ESTAB 0 0 10.0.0.2:5555 8.8.8.8:443 users:((\"bash\",pid=42,fd=3)) ino:999\n"
|
||||
sockets := parseSS(text, "tcp")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue