Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 35 additions & 15 deletions internal/memory/search/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package search
import (
"fmt"
"sort"
"sync"

"memodroid/internal/driver"
)
Expand Down Expand Up @@ -38,8 +39,8 @@ func (s *Session) Filter(mode FilterMode, target []byte) error {
start uintptr
buf []byte
}
cache := make(map[int]*regionData)

// Determine which regions contain candidates so we only read those.
type candEntry struct {
addr uintptr
prev []byte
Expand All @@ -59,32 +60,51 @@ func (s *Session) Filter(mode FilterMode, target []byte) error {
return -1, nil
}

readRegion := func(idx int, r *driver.Region) *regionData {
if d, ok := cache[idx]; ok {
return d
}
buf, err := s.Driver.ReadRegion(s.PID, r.Start, int(r.End-r.Start))
if err != nil {
cache[idx] = nil
return nil
// Identify needed regions.
needed := make(map[int]struct{})
for _, e := range entries {
if idx, _ := regionFor(e.addr); idx >= 0 {
needed[idx] = struct{}{}
}
d := &regionData{start: r.Start, buf: buf}
cache[idx] = d
return d
}

// Pre-load needed regions in parallel.
cache := make(map[int]*regionData)
var cacheMu sync.Mutex
var wg sync.WaitGroup
sem := make(chan struct{}, scanWorkers)

for idx := range needed {
idx := idx
r := &regions[idx]
wg.Add(1)
sem <- struct{}{}
go func() {
defer wg.Done()
defer func() { <-sem }()
buf, readErr := s.Driver.ReadRegion(s.PID, r.Start, int(r.End-r.Start))
cacheMu.Lock()
if readErr != nil {
cache[idx] = nil
} else {
cache[idx] = &regionData{start: r.Start, buf: buf}
}
cacheMu.Unlock()
}()
}
wg.Wait()

next := make(map[uintptr][]byte)
for _, e := range entries {
sz := fixedSize
if s.ValueType == TypeBytes {
sz = len(e.prev)
}

idx, r := regionFor(e.addr)
idx, _ := regionFor(e.addr)
var cur []byte
if idx >= 0 {
d := readRegion(idx, r)
if d != nil {
if d := cache[idx]; d != nil {
off := int(e.addr - d.start)
if off >= 0 && off+sz <= len(d.buf) {
cur = d.buf[off : off+sz]
Expand Down
85 changes: 65 additions & 20 deletions internal/memory/search/search.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package search

import (
"sync"

"memodroid/internal/driver"
)

// scanWorkers limits concurrent ADB calls during parallel memory reads.
const scanWorkers = 8

// Search scans all rw memory regions for target and stores results in the session.
func (s *Session) Search(target []byte) error {
return s.SearchFiltered(target, driver.RegionAll, 0, 0)
Expand All @@ -22,23 +27,43 @@ func (s *Session) SearchFiltered(target []byte, filter driver.RegionFilter, cust
searchBytesInRegions(s.Driver, s.PID, regions, target, found)
} else {
size := s.ValueType.Size()
var mu sync.Mutex
var wg sync.WaitGroup
sem := make(chan struct{}, scanWorkers)

for _, r := range regions {
regionSize := int(r.End - r.Start)
if regionSize <= 0 {
continue
}
buf, err := s.Driver.ReadRegion(s.PID, r.Start, regionSize)
if err != nil {
continue
}
for i := 0; i+size <= len(buf); i += size {
if EqualBytes(buf[i:i+size], target) {
cp := make([]byte, size)
copy(cp, buf[i:i+size])
found[r.Start+uintptr(i)] = cp
wg.Add(1)
sem <- struct{}{}
go func(r driver.Region, regionSize int) {
defer wg.Done()
defer func() { <-sem }()

buf, err := s.Driver.ReadRegion(s.PID, r.Start, regionSize)
if err != nil {
return
}
}
local := make(map[uintptr][]byte)
for i := 0; i+size <= len(buf); i += size {
if EqualBytes(buf[i:i+size], target) {
cp := make([]byte, size)
copy(cp, buf[i:i+size])
local[r.Start+uintptr(i)] = cp
}
}
if len(local) > 0 {
mu.Lock()
for addr, val := range local {
found[addr] = val
}
mu.Unlock()
}
}(r, regionSize)
}
wg.Wait()
}

s.Candidates = found
Expand All @@ -48,21 +73,41 @@ func (s *Session) SearchFiltered(target []byte, filter driver.RegionFilter, cust

func searchBytesInRegions(drv driver.Driver, pid int, regions []driver.Region, target []byte, found map[uintptr][]byte) {
tlen := len(target)
var mu sync.Mutex
var wg sync.WaitGroup
sem := make(chan struct{}, scanWorkers)

for _, r := range regions {
size := int(r.End - r.Start)
if size <= 0 || size < tlen {
continue
}
buf, err := drv.ReadRegion(pid, r.Start, size)
if err != nil {
continue
}
for i := 0; i <= len(buf)-tlen; i++ {
if EqualBytes(buf[i:i+tlen], target) {
cp := make([]byte, tlen)
copy(cp, target)
found[r.Start+uintptr(i)] = cp
wg.Add(1)
sem <- struct{}{}
go func(r driver.Region, size int) {
defer wg.Done()
defer func() { <-sem }()

buf, err := drv.ReadRegion(pid, r.Start, size)
if err != nil {
return
}
}
local := make(map[uintptr][]byte)
for i := 0; i <= len(buf)-tlen; i++ {
if EqualBytes(buf[i:i+tlen], target) {
cp := make([]byte, tlen)
copy(cp, target)
local[r.Start+uintptr(i)] = cp
}
}
if len(local) > 0 {
mu.Lock()
for addr, val := range local {
found[addr] = val
}
mu.Unlock()
}
}(r, size)
}
wg.Wait()
}
Loading