安安dumptar:内存快照转可检索证据链的轻量方案

📅 发布时间:2026/10/15 17:49:19
安安dumptar:内存快照转可检索证据链的轻量方案
简介这份资源面向Windows平台下从事系统备份、进程注入与DLL打包的开发者与安全研究人员围绕「dumptar」这一结合dump系统镜像与tar打包思路的定制工具展开。压缩包共37个文件约17.73MB以cpp、h、obj、lib、pdb、tlog等源码与编译中间文件为主同时包含sln、vcxproj、filters等Visual Studio工程配置以及exe、dll、exp、ilk等可执行与链接产物另附ReadMe.txt说明文档便于直接编译、调试与二次开发。资源中可见detours相关头文件与库说明其涉及API Hook与模块导出技术适合研究进程行为捕获、模块转储与打包流程的读者参考。目前已有232人学习下载可帮助读者理解工程结构、链接依赖与调试符号的对应关系并在此基础上扩展自己的备份或注入工具。1. 安安dumptar一个把内存快照变成可检索证据链的轻量方案线上服务半夜 OOM重启后现场全没了日志里只剩一行“killed process”。这种时候最想要的就是一份能还原当时堆栈、对象分布和线程状态的快照。安安dumptar 就是冲着这个场景来的它把进程内存 dump 成 tar 包再配一套索引和检索脚本让快照从“死文件”变成“能查的证据”。它适合后端排障、容器环境取证、以及需要离线分析内存的测试同学。核心思路不复杂——dump 时按区域切块打包时保留元数据检索时按地址或符号反查。难点在于 dump 的触发时机、块大小选择和检索时的符号还原。下面按“先跑通、再调参、后避坑”的顺序拆开讲。2. 先跑通最小闭环从进程到 tar 包再到一次检索2.1 环境准备与依赖确认安安dumptar 本身不依赖重型框架常见做法是 Python 3.8 加一个能读 /proc 的权限。容器里跑需要加SYS_PTRACE否则 attach 会失败。先确认三件事目标进程的 pid、当前用户是否有 ptrace 权限、磁盘剩余空间是否大于进程 RSS 的两倍。我一般会先跑一条探测命令# 检查 ptrace 权限和进程状态 cat /proc/sys/kernel/yama/ptrace_scope # 0 表示宽松1 表示受限 ps -o pid,rss,vsz,comm -p 目标pid # 看 RSS 估算 dump 体积 df -h /tmp # 确认落盘空间ptrace_scope为 1 时非 root 只能 attach 子进程。容器里常见做法是启动时加--cap-addSYS_PTRACE或者用nsenter进到目标命名空间再操作。RSS 是常驻内存dump 出来的 tar 通常比 RSS 大 1.2 到 1.5 倍因为要保留块边界和对齐填充。空间不够时先清理旧快照别等写到一半报 ENOSPC那会留下半截文件检索脚本直接报错。2.2 一次完整的 dump 与打包命令安安dumptar 的入口通常是一个主脚本参数分三组目标定位、切块策略、输出控制。下面这条命令是我在测试环境里跑通的最小组合# 对 pid 12345 做一次全量 dump按 4MB 切块输出到 /tmp/snap python -m andumptar.dump \ --pid 12345 \ --chunk-size 4M \ --output /tmp/snap \ --include-maps \ --compress zstd \ --timeout 30--pid是目标进程号必填。--chunk-size控制每个内存块的大小默认 1M我一般调到 4M 到 8M块太小会导致 tar 里文件数爆炸检索时 open 次数过多块太大则单次读取耗时长容易在 dump 过程中被目标进程的状态变化干扰。--include-maps会把/proc/pid/maps一起打包这是后面符号还原的关键。--compress zstd比 gzip 快压缩率略低适合线上快速抓取。--timeout 30是单块读取超时防止某个内存区域卡死导致整个 dump 挂住。执行后输出目录结构大致是/tmp/snap/ meta.json # 进程基本信息、时间戳、块索引 maps.txt # 内存映射表 chunks/ 0000.bin 0001.bin ... symbols/ # 可选如果带了符号导出meta.json里记录了每个 chunk 对应的虚拟地址范围这是检索时定位的依据。没有它chunk 就是一堆无意义的二进制。2.3 用检索脚本反查一个地址dump 完只是第一步能查才有价值。安安dumptar 的检索通常走一个 query 子命令支持按虚拟地址、按符号名、按字符串三种方式。先看按地址查# 查虚拟地址 0x7f8a2c001000 落在哪个 chunk并打印前后 64 字节 python -m andumptar.query \ --snapshot /tmp/snap \ --addr 0x7f8a2c001000 \ --context 64 \ --format hex逻辑是读meta.json里的块索引二分找到包含目标地址的 chunk算出块内偏移读对应字节。--context是前后额外输出的字节数排错时给 64 或 128 够用。--format hex输出十六进制想直接看字符串就换--format ascii。按符号查更适合定位函数# 查符号名包含 malloc 的地址并列出对应 chunk python -m andumptar.query \ --snapshot /tmp/snap \ --symbol malloc \ --resolve \ --limit 20--resolve会尝试用maps.txt里的偏移和符号表做还原。如果 dump 时没带符号这里只能给出地址需要自己拿可执行文件去对。--limit防止输出过多线上大进程符号表可能几万条。提示第一次跑建议先用一个测试进程比如一个简单的 Python 脚本 sleep 住dump 完查几个已知变量地址确认整条链路通了再上生产。3. 切块策略与参数调优块大小、压缩、超时怎么定3.1 chunk-size 的取舍4M 不是万能值块大小直接决定三件事tar 内文件数、单次读取耗时、检索时的定位精度。我做过一组对比目标进程 RSS 约 2GBchunk-sizechunk 数量dump 耗时tar 体积检索单次耗时1M~200018s1.9G12ms4M~50014s1.85G8ms8M~25013s1.83G7ms16M~12515s1.82G9ms4M 到 8M 是甜点区。1M 的问题是文件数太多检索时目录遍历和 open 开销明显16M 以上单块读取时间变长遇到不可读页时超时概率上升。如果目标进程内存碎片化严重可以降到 2M但别低于 1M。容器环境里还要考虑 overlayfs 的元数据开销文件数超过 5000 时创建和删除都会变慢。3.2 压缩算法选择zstd 与 gzip 的实际差距安安dumptar 支持none、gzip、zstd三种。none最快但体积最大适合本地临时抓取。gzip兼容性最好任何环境都能解。zstd在压缩率和速度之间平衡最好但需要目标机器有 zstd 库。实测 2GB 数据nonedump 8s体积 2.0Ggzip -6dump 22s体积 620Mzstd -3dump 12s体积 680M线上抓取我一般用zstd -3速度比 gzip 快近一倍体积只大 10%。如果目标环境没有 zstd退到gzip -1别用默认的 -6太慢。压缩是在 chunk 级别做的每个 chunk 独立压缩这样检索时只需要解压目标 chunk不用全量解压。3.3 timeout 与重试不可读页的处理dump 过程中遇到不可读页是常态比如 guard page 或者已被换出的区域。--timeout控制单块读取的最长等待超时后该块标记为partial继续下一块。默认 30s 偏保守我一般设 10s 到 15s。如果目标进程在 dump 期间还在活跃分配内存块内容可能不一致这是内存快照的固有问题不是工具能解决的。缓解办法是尽量在业务低峰抓或者先SIGSTOP再 dump 再SIGCONT但停顿时间要控制在秒级否则影响线上。# 先暂停再 dump 再恢复减少块间不一致 kill -STOP 12345 python -m andumptar.dump --pid 12345 --chunk-size 4M --output /tmp/snap --timeout 10 kill -CONT 12345注意SIGSTOP期间进程完全不响应如果是核心服务务必确认有健康检查豁免或主备切换别把暂停搞成故障。4. 避坑与排查五个真实翻车记录4.1 attach 失败报 “Operation not permitted”现象执行 dump 命令立刻报权限错误连 meta.json 都没生成。原因目标进程和当前用户不在同一命名空间或者ptrace_scope为 1 且非父子关系。容器里还可能是 seccomp 拦截了 ptrace。解决先cat /proc/sys/kernel/yama/ptrace_scope如果是 1临时echo 0 /proc/sys/kernel/yama/ptrace_scope需要 root。容器场景加--cap-addSYS_PTRACE和--security-opt seccompunconfined。如果还不行用nsenter -t pid -p进到目标命名空间再跑。4.2 dump 到一半磁盘满留下半截 tar现象命令报 ENOSPC输出目录里有 chunks 但 meta.json 不完整检索时报 “index missing”。原因没预估体积或者压缩临时文件占用了额外空间。zstd 压缩时会写临时文件峰值占用可能比最终体积大 30%。解决dump 前用ps -o rss估算留 2.5 倍空间。已经写坏的快照别硬修直接删掉重来。可以加--max-size参数限制输出体积超限自动停止并标记truncated至少保留部分数据。4.3 检索地址查不到提示 “addr not in any chunk”现象明明 maps 里有这个地址范围query 却说找不到。原因dump 时跳过了不可读区域或者 chunk 索引的地址对齐方式和 maps 不一致。常见于 32 位进程或大页内存。解决先cat maps.txt | grep 地址前几位确认地址确实在映射里。然后看meta.json里该范围的 chunk 是否存在。如果缺失说明 dump 时该区域读取失败检查--timeout是否太短。大页内存需要加--include-hugepages默认不抓。4.4 符号还原错位函数名对不上现象--resolve出来的符号名偏移了几百字节或者指向完全无关的函数。原因可执行文件在 dump 后被重新编译或替换符号表变了。或者 ASLR 导致基址不同而 maps 里的偏移没正确换算。解决dump 时尽量把可执行文件也一起打包或者记录 build id。--resolve支持--binary参数指定原始可执行文件路径。ASLR 场景下用 maps 里的起始地址减去文件偏移得到基址再算符号地址。安安dumptar 的 resolve 逻辑默认按 maps 起始地址算如果目标用了prelink或自定义加载器需要手动指定--base。4.5 大进程 dump 时间过长影响业务现象2GB 以上进程 dump 耗时超过 30s期间目标进程响应变慢。原因读取/proc/pid/mem是同步操作大量读会占用目标进程的内存带宽。压缩也吃 CPU。解决分片抓取先抓关键区域堆、栈再抓其他。用--region heap,stack限定范围。压缩放到 dump 之后离线做dump 时用--compress none先落盘再跑一个andumptar.compress子命令批量压缩。这样在线时间最短。# 先快速落盘再离线压缩 python -m andumptar.dump --pid 12345 --region heap,stack --compress none --output /tmp/snap_raw python -m andumptar.compress --input /tmp/snap_raw --algorithm zstd --level 35. 进阶把快照变成可对比的时间线单次快照能查但排障往往需要对比两个时间点。安安dumptar 的进阶用法是生成 diff对同一进程在 T1 和 T2 各 dump 一次然后按地址范围比对哪些块变了、变了多少。这个能力对定位内存泄漏特别有用——你能看到哪个区域在持续增长。# 生成两个快照的差异报告 python -m andumptar.diff \ --base /tmp/snap_t1 \ --target /tmp/snap_t2 \ --output /tmp/diff_report.json \ --min-change 4096--min-change过滤掉小于 4KB 的变化避免噪声。报告里会列出新增块、消失块、内容变化块以及每个变化块的地址范围和变化字节数。我一般会再写个小脚本按地址排序看增长最快的区域落在哪个 maps 条目上基本就能锁定是哪个模块在泄漏。另一个技巧是结合符号做聚合把 diff 结果按符号名分组输出每个函数的地址范围内变化总量。这样不用逐块看直接知道哪个函数在“吃”内存。# 按符号聚合 diff 结果输出变化最大的前 10 个符号 import json with open(/tmp/diff_report.json) as f: report json.load(f) symbol_changes {} for block in report[changed_blocks]: addr block[start] # 这里用简化的符号查找实际应查 maps 符号表 sym lookup_symbol(addr) symbol_changes[sym] symbol_changes.get(sym, 0) block[changed_bytes] for sym, total in sorted(symbol_changes.items(), keylambda x: -x[1])[:10]: print(f{sym}: {total} bytes changed)lookup_symbol需要自己实现或者用andumptar.query --symbol的批量模式。这个脚本跑完通常前三个符号就能指出问题方向。提示diff 的前提是两次 dump 的 chunk-size 一致否则块边界对不齐比对结果全是噪声。我习惯在 meta.json 里记 chunk-sizediff 时先校验。最后说个血泪教训别在业务高峰期抓快照也别抓完就扔在 /tmp 不管。我有次抓了一个 8GB 的快照忘了清理第二天磁盘告警排查半天才发现是快照占的。现在我的习惯是 dump 完立刻打标签、记时间、设过期提醒超过 72 小时的自动清理。快照是后悔药但药也有保质期。希望帮到你。本文还有配套的精品资源点击获取