reg-dump

Fuzzy search the Windows registry.
Log | Files | Refs | README | LICENSE

commit c6b7b9067faf1d65cda0d0d8a273d0c9766f6865
parent 8a0fd0b6a5df5ec969fcf1d6301d089f3766727b
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Fri, 16 Aug 2024 13:52:50 +0800

reg-dump: purge command

Purge all registry entries that match the pattern.

Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>

Diffstat:
Mmain.go | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 109 insertions(+), 2 deletions(-)

diff --git a/main.go b/main.go @@ -34,6 +34,8 @@ func main() { Search(args) case "show": Show(args) + case "purge": + Purge(args) } } @@ -102,7 +104,53 @@ func Search(args []string) { Pattern: exp, } - flowmatic.ManageTasks(-1, walk, manager.Manage, root) + flowmatic.ManageTasks(-1, walk, manager.Search, root) +} + +// Purge the registry of all entries that match the pattern. +func Purge(args []string) { + var ( + log string + pattern string + root string + ) + + flags := flag.NewFlagSet("search", flag.ExitOnError) + flags.StringVar(&log, "log", "", "log file") + flags.StringVar(&pattern, "pattern", "", "regex pattern to match against") + flags.StringVar(&root, "path", "", "root search path") + flags.Parse(args) + + if pattern == "" { + fmt.Println("-pattern is required") + os.Exit(1) + } + + var exp *regexp.Regexp + + if pattern != "" { + exp = regexp.MustCompile(pattern) + } + + var output io.Writer + + if log != "" { + f, err := os.OpenFile(log, os.O_CREATE|os.O_WRONLY, 0o644) + if err != nil { + panic(fmt.Errorf("opening log file: %w", err)) + } + defer f.Close() + output = f + } else { + output = os.Stderr + } + + manager := Manager{ + Logger: slog.New(slog.NewTextHandler(output, nil)), + Pattern: exp, + } + + flowmatic.ManageTasks(-1, walk, manager.Purge, root) } type Manager struct { @@ -115,7 +163,8 @@ type Manager struct { seen []uint64 } -func (m *Manager) Manage(path string, subkeys []string, err error) ([]string, bool) { +// Search outputs matched registry entries. +func (m *Manager) Search(path string, subkeys []string, err error) ([]string, bool) { if err != nil { m.Error("task", path, err) } @@ -140,6 +189,64 @@ func (m *Manager) Manage(path string, subkeys []string, err error) ([]string, bo return subkeys, true } +// Purge deletes matched registry entries. +func (m *Manager) Purge(path string, subkeys []string, err error) ([]string, bool) { + if err != nil { + m.Error("task", path, err) + } + + if m.redundant(path) { + m.Warn("skipping", "path", path) + return nil, true + } + + if m.match(path) && err == nil { + if err := m.purge(path); err != nil { + m.Error("purge", path, err) + } + return nil, true + } + + for ii, subkey := range subkeys { + subkeys[ii] = filepath.Join(path, subkey) + } + + return subkeys, true +} + +// purge the given path. +// If the path contains the pattern, delete the key. +// Else if the key's values contain the pattern in name or value, delete them. +func (m *Manager) purge(path string) error { + if m.Pattern.MatchString(path) { + if err := registry.DeleteKey(registry.CURRENT_USER, path); err != nil { + return fmt.Errorf("deleting key: %w", err) + } + return nil + } + + key, err := registry.OpenKey(registry.CURRENT_USER, path, registry.ALL_ACCESS) + if err != nil { + return fmt.Errorf("opening key with write permissions: %w", err) + } + + defer key.Close() + + forEachValue(key, func(name, value string, err error) bool { + if err != nil { + m.Error("iterating", fmt.Sprintf("%s.%s", path, name), err) + } + if m.Pattern.MatchString(name) || m.Pattern.MatchString(value) { + if err := key.DeleteValue(name); err != nil { + m.Error("deleting", fmt.Sprintf("%s.%s", path, name), err) + } + } + return true + }) + + return nil +} + // redundant is true if the path has already been seen. // // The check is done using a binary search over sorted hashes to avoid retaining