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, 云资产清单, 内存扫描, 日志审计, 逆向工程