// checkLibraries retrieves the linked libraries of a process and compares them with the // regexes of library checks func (s search) checkLibraries(proc process.Process, procname string) (matchedall bool) { matchedall = true if s.checkmask&checkLib == 0 { // this search has no library check return } for i, c := range s.checks { if c.code&checkLib == 0 { continue } libs, err, serr := listlibs.GetMatchingLoadedLibraries(proc, c.regex) if err != nil { stats.Failures = append(stats.Failures, err.Error()) } if len(serr) > 0 && s.Options.LogFailures { stats.Failures = append(stats.Failures, err.Error()) if debug { for _, err := range serr { fmt.Printf("checkLibraries: soft error -> %v\n", err) } } } if len(libs) > 0 { if debug { fmt.Printf("checkLibraries: proc name '%s' pid %d has libraries matching regex '%s'\n", procname, proc.Pid(), c.value) } c.storeMatch(proc) } else { matchedall = false } s.checks[i] = c } return }
func listLoadedLibraries(p process.Process) (libraries []string, harderror error, softerrors []error) { mapsFile, harderror := os.Open(common.MapsFilePathFromPid(p.Pid())) if harderror != nil { return } defer mapsFile.Close() scanner := bufio.NewScanner(mapsFile) processName, harderror, softerrors := p.Name() if harderror != nil { return } libs := make([]string, 0, 10) for scanner.Scan() { line := scanner.Text() items := common.SplitMapsFileEntry(line) if len(items) != 6 { return libs, fmt.Errorf("Unrecognised maps line: %s", line), softerrors } path := items[5] if path == processName { continue } if path == "/dev/zero" || path == "/dev/zero (deleted)" { continue } if path == "" { continue } if path[0] == '[' { continue } if inSlice(path, libs) { continue } libs = append(libs, path) } return libs, nil, nil }
func (c *check) storeMatch(proc process.Process) { if debug { fmt.Printf("storing process id %d that matched check %d\n", proc.Pid(), c.code) } store := true for _, storedPs := range c.matchedPs { // only store files once per check if proc.Pid() == storedPs.Pid() { store = false } } if store { c.matched++ c.matchedPs = append(c.matchedPs, proc) } return }
func copyMemory(p process.Process, address uintptr, buffer []byte) (harderror error, softerrors []error) { mem, harderror := os.Open(common.MemFilePathFromPid(p.Pid())) if harderror != nil { harderror := fmt.Errorf("Error while reading %d bytes starting at %x: %s", len(buffer), address, harderror) return harderror, softerrors } defer mem.Close() bytes_read, harderror := mem.ReadAt(buffer, int64(address)) if harderror != nil { harderror := fmt.Errorf("Error while reading %d bytes starting at %x: %s", len(buffer), address, harderror) return harderror, softerrors } if bytes_read != len(buffer) { return fmt.Errorf("Could not read the entire buffer"), softerrors } return nil, softerrors }
// checkName compares the "name" (binary full path) of a process against name checks func (s search) checkName(proc process.Process, procname string) (matchedall bool) { matchedall = true if s.checkmask&checkName == 0 { // this search has no name check return } for i, c := range s.checks { if c.code&checkName == 0 { continue } if debug { fmt.Println("checkName: evaluating", procname, proc.Pid(), "against check", c.value) } if c.regex.MatchString(procname) { if debug { fmt.Printf("checkName: proc name '%s' pid %d matches regex '%s'\n", procname, proc.Pid(), c.value) } c.storeMatch(proc) } else { if debug { fmt.Printf("checkName: proc name '%s' pid %d does not match regex '%s'\n", procname, proc.Pid(), c.value) } matchedall = false } s.checks[i] = c } return }
func nextReadableMemoryRegion(p process.Process, address uintptr) (region MemoryRegion, harderror error, softerrors []error) { mapsFile, harderror := os.Open(common.MapsFilePathFromPid(p.Pid())) if harderror != nil { return } defer mapsFile.Close() region = MemoryRegion{} scanner := bufio.NewScanner(mapsFile) for scanner.Scan() { line := scanner.Text() items := common.SplitMapsFileEntry(line) if len(items) != 6 { return region, fmt.Errorf("Unrecognised maps line: %s", line), softerrors } start, end, err := common.ParseMapsFileMemoryLimits(items[0]) if err != nil { return region, err, softerrors } if end <= address { continue } // Skip vsyscall as it can't be read. It's a special page mapped by the kernel to accelerate some syscalls. if items[5] == "[vsyscall]" { continue } // Check if memory is unreadable if items[1][0] == '-' { // If we were already reading a region this will just finish it. We only report the softerror when we // were actually trying to read it. if region.Address != 0 { return region, nil, softerrors } softerrors = append(softerrors, fmt.Errorf("Unreadable memory %s", items[0])) continue } size := uint(end - start) // Begenning of a region if region.Address == 0 { region = MemoryRegion{Address: start, Size: size} continue } // Continuation of a region if region.Address+uintptr(region.Size) == start { region.Size += size continue } // This map is outside the current region, so we are ready return region, nil, softerrors } // No region left if err := scanner.Err(); err != nil { return NoRegionAvailable, err, softerrors } // The last map was a valid region, so it was not closed by an invalid/non-contiguous one and we have to return it if region.Address > 0 { return region, harderror, softerrors } return NoRegionAvailable, nil, softerrors }
func (r Runner) walkProcMemory(proc process.Process, procname string) (err error) { // find longest byte string to search for, which determines the buffer size bufsize := uint(4096) // find lowest offset, which determines start address offset := ^uintptr(0) >> 1 // verify that at least one search is interested in inspecting the memory // of this process shouldWalkMemory := false // if at least one search wants to log failures, do so, otherwise omit them logFailures := false for label, search := range r.Parameters.Searches { // if the search is not active or the search as no content or by check to run, skip it if !search.isactive || (search.checkmask&checkContent == 0 && search.checkmask&checkByte == 0) { search.deactivate() r.Parameters.Searches[label] = search continue } shouldWalkMemory = true // find the largest bufsize needed for _, c := range search.checks { if c.code&checkByte != 0 { if uint(len(c.bytes)) > (bufsize / 2) { bufsize = 2 * uint(len(c.bytes)) // pad to always have an even bufsize if bufsize%2 != 0 { bufsize++ } } } } // find the smallest offset needed if uintptr(search.Options.Offset) < offset { offset = uintptr(search.Options.Offset) } if search.Options.LogFailures { logFailures = true } } if !shouldWalkMemory { if debug { fmt.Println("walkProcMemory: no check needs to read the memory of process", proc.Pid(), procname) } return } // keep track of the number of bytes read to exit when maxlength is reached var readBytes float64 walkfn := func(curStartAddr uintptr, buf []byte) (keepSearching bool) { if readBytes == 0 { readBytes += float64(len(buf)) } else { readBytes += float64(len(buf) / 2) } if debug { fmt.Println("walkProcMemory: reading", bufsize, "bytes starting at addr", curStartAddr, "; read", readBytes, "bytes so far") } for label, search := range r.Parameters.Searches { matchedall := true if !search.isactive { continue } // if the search is meant to stop at a given address, and we're passed // that point then deactivate the search now if readBytes >= search.Options.MaxLength { search.deactivate() goto skip } keepSearching = true for i, c := range search.checks { switch c.code { case checkContent: if c.regex.FindIndex(buf) == nil { // not found matchedall = false continue } c.storeMatch(proc) search.checks[i] = c case checkByte: if bytes.Index(buf, c.bytes) < 0 { // not found matchedall = false continue } c.storeMatch(proc) search.checks[i] = c } } // if all the checks have matched on this search, deactivate it if matchedall { search.deactivate() } skip: r.Parameters.Searches[label] = search } if debug && !keepSearching { fmt.Println("walkProcMemory: stopping the memory search for", proc.Pid(), procname) } return } if debug { fmt.Println("walkProcMemory: reading memory of", proc.Pid(), procname) } err, serr := memaccess.SlidingWalkMemory(proc, offset, bufsize, walkfn) if err != nil { return err } if logFailures { for _, err = range serr { stats.Failures = append(stats.Failures, err.Error()) if debug { fmt.Printf("walkProcMemory: soft error -> %v\n", err) } } } stats.MemoryRead += readBytes return }