philjackson/memhack

GitHub: philjackson/memhack

一个 scanmem 风格的 Linux 交互式进程内存扫描与修改工具,通过逐步缩小数值匹配范围来定位并改写运行中程序的内存地址。

Stars: 0 | Forks: 0

# memhack 一个类似于 [scanmem](https://github.com/scanmem/scanmem) 风格的交互式内存扫描器,专为 Linux 设计,使用 Go 语言编写。它可以附加到进程上,在其内存中搜索某个值,随着值的变化在连续的扫描中缩小匹配范围,最后将新值写回。 这是一款用于检查和修改运行中程序状态的经典工具——可用于调试、逆向工程,或者在你拥有的进程中修改游戏数值。 ## 构建 ``` go build -o memhack . ``` ## 用法 memhack 以全屏 **TUI**(使用 [Bubble Tea](https://github.com/charmbracelet/bubbletea) 构建)启动。 通过 PID 附加到正在运行的进程,或者让 memhack 自行启动程序(即使在具有严格限制的 `ptrace_scope` 下,启动方式依然有效,因为进程始终可以跟踪其自身的子进程): ``` ./memhack -pid 12345 ./memhack -exec ./mygame ``` 在**未指定目标**的情况下启动时,memhack 会打开一个**进程选择器**,列出你的所有进程。输入 / 进行搜索(按 pid、名称或命令行),使用 ctrl+j/ctrl+k(或方向键)滚动,按 enter 附加,按 ctrl+r 重新加载。随时可以通过 `:ps` 重新打开它。 你也可以在 TUI 内部使用 `:pid 12345` 或 `:run ./mygame` 进行附加。 ### TUI ``` ┌ memhack pid 12345 │ type i32 │ 3 matches ──────────────────┐ │ # Address Value │ │ 0 0x55c8e8e22030 1337 ← live-updating values │ │ 1 0x7ffd672ddfa0 1337 │ │ 2 0x7ffd672ddfa8 1337 │ │ │ │ scan› 1337 │ │ • 3 matches │ └──────────────────────────────────────────────────────────────┘ ``` - 在 `scan›` 行中**输入扫描表达式**并按 **Enter** 进行扫描(首次扫描会找到所有匹配项;随后的扫描会缩小范围)。CLI 支持的表达式在此同样适用:`1337`、`> 100`、`10..20`、`changed`、`inc`、`dec 5` 等。 - **匹配表格**显示当前值,每秒实时刷新数次。 - **Tab** 键可在输入框和表格之间切换焦点。当表格处于焦点状态时,**↑/↓** 选择某行,按 **w**(或 **Enter**)就地修改其值。 - **`:` 命令**:`:pid N`、`:run prog args`、`:type f32`、`:set N value`、`:setall value`、`:reset`、`:undo`、`:q`。 - **Ctrl+Z** 撤销,**Ctrl+R** 重置。使用 `quit`(或 `:q`)、**Ctrl+C** 或 **Ctrl+D** **退出**。 - 在扫描或写入操作运行期间,状态栏会显示一个动态的旋转图标(`⣾ working…`);该图标仅在处理任务时出现。 ### REPL 对于脚本编写或无界面(headless)使用场景,`-repl` 会运行基于行的原始界面: ``` $ ./memhack -repl -exec ./target memhack> 1337 # first scan: find every location holding 1337 memhack[3]> dec # keep only those that have since decreased memhack[1]> list # show remaining matches [0] 0x55c8e8e22030 = 1200 memhack[1]> set 0 9999 # write 9999 into match #0 ``` ### REPL 命令 | 命令 | 含义 | | --- | --- | | `pid ` | 附加到正在运行的进程 | | `run [args]` | 作为子进程启动程序并附加 | | `type [t]` | 显示/设置值的类型(`i8`、`i16`、`i32`、`i64`、`u8`–`u64`、`f32`、`f64`) | | `regions` | 列出目标可扫描的内存区域 | | `` | 扫描:保留等于 `` 的位置(例如 `1337`、`-5`) | | `> < >= <= != ` | 扫描:保留与 `` 构成指定比较关系的位置 | | `..` | 扫描:保留值在 `[lo, hi]` 范围内的位置 | | `changed` / `unchanged` | 保留自上次扫描以来发生改变 / 未改变的位置 | | `inc` / `dec` (`+` / `-`) | 保留值增加 / 减少的位置 | | `inc ` / `dec ` (`+ ` / `- `) | 保留值正好改变了 `` 的位置 | | `list [n]` | 列出当前匹配项及其当前值(默认列出前 50 项) | | `watch [interval]` | 实时更新匹配的值,直到按下 Ctrl-C(例如 `watch 250ms`) | | `count` | 当前匹配项的数量 | | `set ` | 将 `` 写入到匹配项 `#i` | | `setall ` | 将 `` 写入到每一个匹配项 | | `undo` | 撤销上次扫描,恢复到之前的匹配集 | | `reset` | 丢弃匹配项并重新开始 | | `help`, `quit` | 帮助 / 退出 | 典型工作流:扫描一个已知值,让程序改变它,然后使用 `inc`/`dec`/`changed`/`<`/`>`/`lo..hi` 进行扫描以缩小候选范围,重复操作直到只剩下一个地址,最后使用 `set` 修改它。如果某次扫描过滤得太激进,`undo` 可以退回到上一步的匹配集(最多支持撤销 16 步)。 ## 工作原理 - 解析 **`/proc//maps`** 以寻找值得扫描的可读+可写内存区域。 - 读取(使用类似 `pread` 的 `ReadAt`)和写入 **`/proc//mem`** 以执行实际的扫描和修改。 - 使用 **`ptrace`**(`PTRACE_SEIZE`)附加到目标进程。在每次扫描或写入之前,进程会被短暂停止(`PTRACE_INTERRUPT`),随后恢复运行(`PTRACE_CONT`),因此每次访问看到的都是一致的快照,而不会在命令执行期间冻结程序。 - 初始扫描以 1 字节的粒度全面扫描内存(因此能找到未对齐的值);随后的扫描只会重新检查幸存的匹配地址,这也是它们速度快的原因。 - **线程机制。** `ptrace` 将跟踪器绑定到单个 OS 线程,因此所有的进程访问都在一个专用的、通过 `LockOSThread` 锁定的 goroutine 上运行——在 `-repl` 模式下是同步的 REPL goroutine,在 TUI 中则是一个后台 **worker**。TUI 的 Bubble Tea `Cmd` 仅通过 channel 将请求传递给 worker,从而在执行大型扫描时保持 UI 响应迅速。 ## 权限 读取和写入其他进程的内存需要获得对其调用 `ptrace` 的权限: - 同一用户且 `ptrace_scope` 设置较为宽松,**或者** - 拥有 `CAP_SYS_PTRACE`(例如通过 `sudo` 运行),**或者** - 目标是通过 `run`/`-exec` 启动的子进程。 使用 `cat /proc/sys/kernel/yama/ptrace_scope` 检查你的设置。值为 `1`(常见的默认值)时,你只能附加到你自己的后代进程——对于其他情况,请使用 `run` 或 `sudo`。请仅对你拥有的或被授权检查的进程使用 memhack。 ## 布局 ``` main.go entry point, flags, REPL, scan-expression parsing tui.go Bubble Tea model: view, key handling, table + input procpicker.go startup process picker (searchable list) worker.go thread-locked worker owning the process/scanner; controller exposing it to the TUI as tea.Cmds internal/memory/proclist.go /proc enumeration for the picker internal/memory/maps.go /proc//maps parsing internal/memory/mem.go /proc//mem read/write, attach lifecycle internal/memory/ptrace.go ptrace seize/interrupt/cont/detach internal/memory/launch.go launch a child process and attach internal/scan/value.go data types: parse, encode, decode, format internal/scan/scanner.go scan + narrow + write engine ``` ## 状态 / 构想 这是一个早期的脚手架版本。后续的自然演进方向: - 字节数组 / 字符串 / 正则表达式扫描,以及初始值未知的扫描。 - 数值“冻结”——一个不断重写某个地址的后台循环。 - 对齐选项(将扫描对齐到类型宽度以提升速度)。 - 反汇编 / 指针映射追踪。 ## 许可证 [MIT](LICENSE) © Phil Jackson
标签:EVTX分析, Go, Ruby工具, TUI, 云资产清单, 内存扫描, 日志审计, 逆向工程