Communitygithub.com

aicodeking/leanrea

使用本地CLI工具对已发布的软件进行逆向工程,无需MCP。涵盖Electron/JS捆绑包、原生Mach-O/ELF/PE、Go/Rust/.NET/Flutter/WASM二进制文件、APK、SQLite、pcap、固件和网站。用于解释功能的工作原理、提取版本/依赖项、比较版本或重建功能。

leanrea 是什么?

leanrea是一个AI代理技能,旨在使用纯本地CLI工具对已发布的软件进行逆向工程,无需任何MCP(模型上下文协议)服务器。它支持广泛的二进制格式和捆绑包:Electron和JavaScript捆绑包、原生Mach-O/ELF/PE可执行文件、Go/Rust/.NET/Flutter/WASM二进制文件、Android APK、SQLite数据库、pcap捕获、固件映像和网站。该技能通过为你想要逆向的每种目标类型提供最快、最便宜的命令,最大限度地减少了AI代理的令牌成本和工具调用次数。它强调基于证据的推理,要求每个声明都用文件偏移量或符号字符串表示引用,并明确区分观察事实和推断事实。该技能支持macOS(lipo、otool、nm)、Linux(readelf、objdump)和跨平台工具(jadx、asar、sqlite3),如果缺少工具,则建议使用Homebrew或npm进行安装。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/aicodeking/leanrea/tree/HEAD/leanrea

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

leanrea: 快速路径

你已经了解这些工具。本文件仅列出每个目标的最快命令以及常见陷阱。

规则

  • 对副本进行操作(APFS 上 cp -c 是瞬间的)。将每次解压放入 $TMPDIR/re/<name> 下的新目录中。如果已有解压结果,则复用。
  • 首先通过 file -b T 和魔数字节进行路由。接下来运行最廉价的清单检查。之后再反编译或阅读代码。
  • rg 会跳过隐藏和忽略的文件,而打包工具会隐藏代码(Electron:.webpack/、.vite/)。始终使用 rg -uu 搜索解压后的目录树。
  • 切勿 cat 一个大文件或已压缩的文件。使用切片:rg -o '.{0,200}NEEDLE.{0,200}' F | head。将反汇编结果保存到一个文件中,然后 grep 该文件。
  • 使用 ; 和 echo '--- name' 标记将独立的探测任务批处理到一次 SHELL 调用中(注意引用:zsh 会展开裸的 ==x)。每个输出限制为 head 或 -m。
  • 基于证据作答。在回答中,将每个声明标记为 已看到(引用文件:偏移量、符号或字符串)或 推断。未找到的事实应标记为 未知,而不是“不存在”。静态阅读并非实际执行观察。
  • 缺少工具不是障碍。使用同一行的备选方案,或者在用户允许的情况下使用 brew install <x>。
  • 轮次比字节更昂贵。目标是 2–4 次工具调用:一次批量侦察调用,一两次针对性切片,然后回答。当询问的每个项目都有了证据时停止。不要重新验证你已经看到的内容。

第一步:每个目标一个调用,粘贴后运行

原生 (Mach-O):

T=./bin; D=$TMPDIR/re/$(basename $T); mkdir -p $D; file $T; lipo -info $T; otool -L $T; echo '--- EXPORTS'; nm -gU $T | head -150; echo '--- IMPORTS'; nm -u $T | rg -v '^__Z|napi_' | tr '\n' ' '; echo; echo '--- STRINGS'; strings -a -n 6 $T | rg -v '^[_$.]' | head -400; otool -tV $T > $D/dis.txt; echo '--- FUNCS'; rg '^[_a-zA-Z].*:$' $D/dis.txt | rg -v '^_?_?Z?N?St3' | head -150

然后阅读一个函数:awk '/^FUNC_LABEL:$/{p=1;next} p&&/^[_a-zA-Z].*:$/{exit} p' $D/dis.txt | rg 'bl|b\.|cmp|literal|mov\s+w2'。

Electron:

A=./app.asar; X=$TMPDIR/re/asar; rm -rf $X; npx -y @electron/asar extract $A $X && jq '{name,version,main}' $X/package.json; find $X -path '*/node_modules' -prune -o -type f \( -name '*.js' -o -name '*.node' \) -print | head -60; ls ${A}.unpacked/node_modules 2>/dev/null; rg -uu -l --glob '!node_modules' 'exposeInMainWorld' $X

然后在捆绑包中切片搜索目标字符串:rg -uu -o --glob '!node_modules' '.{0,250}NEEDLE.{0,250}' $X | head -20。

Go:通常仅 go version -m T 就能回答问题。

路由表

目标(检测方式)最快路径
Electron 应用 (*.app/Contents/Resources/app.asar)npx -y @electron/asar extract app.asar X(另见 app.asar.unpacked/ 中的 .node 插件)。入口点:jq .main X/package.json。表面API:`rg -n -o '.{0,120}(ipcMain.(handle
Web 捆绑包 / 压缩的 JS查找 sourceMappingURL。如果 source map 有 sourcesContent,则提取源代码:jq -r '.sources[]'。对于 webpack,grep 模块 ID,然后在命中位置周围切片。对于美化打印:npx -y prettier --parser babel F > F.pretty.js,然后按行范围读取。
Hermes / bytenode / V8 快照Hermes 魔数为 c6 1f bc 03(index.android.bundle):使用 hermes-dec / hbc-decompiler。.jsc(bytenode)无法反编译:使用字符串和运行时钩子。
Mach-O(file:Mach-O)lipo -info(先瘦身:lipo -thin arm64 -o T1 T)。然后:otool -L(库);xcrun dyld_info -exports -imports;nm -gU(导出)/ nm -u(导入 = 能力映射);strings -a -n 6;`otool -l
原生代码逻辑otool -tV T > $D/dis.txt(或 llvm-objdump -d --no-show-raw-insn -C)。找到函数:rg -n '^_?.*Name.*:$'。读取调用/字符串线索:`awk '/^FUNC:/{p=1} p&&/^[_a-zA-Z].*:$/&&!/FUNC/{exit} p' dis.txt
ELF / PEreadelf -hlSW、readelf -dW(NEEDED)、nm -D --defined-only、objdump -d -M intel。PE:objdump -x、pefile/wrestool(用于资源)。checksec:pwn checksec。
Go(__go_buildinfo、.go.buildinfo)go version -m T 在一次调用中提供工具链、路径、模块、依赖项和构建标志。如果没有 Go:`strings -a T
Rust依赖项(cargo-auditable):cargo audit bin T,或提取 .dep-v0 / __DATA,.dep-v0 并执行 zlib-inflate。编译器:`strings -a T
.NET(file:Mono/.Net)ilspycmd -p -o X T.dll(安装:dotnet tool install -g ilspycmd),然后 rg。P/Invoke:rg DllImport X。NativeAOT/IL2CPP 是原生代码:使用原生代码的行。
Android APKjadx -d X --show-bad-code a.apk 然后 rg。仅需资源/清单:apktool d --no-src -o X a.apk。快速查看:unzip -l a.apk。lib/*/libflutter.so 表示 Flutter(Dart AOT:blutter);libhermes.so 表示 React Native(见 Hermes 行)。
Flutter 快照哈希在 libapp.so 中,找到魔数 f5 f5 dc dc;+20 偏移处的 32 个十六进制字符是快照哈希。将其映射到 Dart 版本。
WASMwasm-objdump -x、wasm2wat、wasm-decompile(WABT)。
SQLite复制数据库,如果存在 -wal 文件也一并复制。sqlite3 'file:C?mode=ro' '.schema',然后 SELECT * FROM t LIMIT 20。
plist / nib / carplutil -p F。Info.plist 保存捆绑包 ID、URL 方案和隐私字符串。资源目录:assetutil --info Assets.car。
pcap / HARtshark -r C -q -z conv,tcp;`tshark -r C -T fields -e dns.qry.name -e tls.handshake.extensions_server_name -e http.request.full_uri
固件binwalk -e F 或 unblob -e X F。然后通过此表路由每个提取的二进制文件。
网站curl -sL URL 获取 HTML 和脚本 src,然后获取 JS 并进行切片。对于运行时行为,使用带有网络日志的无头浏览器(Playwright page.on('request'))或 Chrome --remote-debugging-port。
运行中的应用 / 运行时macOS:lldb -p PID、dtrace/fs_usage/log stream --predicate 'process=="X"'。Electron main:--inspect(仅在 RunAsNode/inspect fuse 允许的情况下)。Android:adb logcat、frida -U -f pkg -l hook.js。

速度模式

  • 优先检查字符串和导入。对于小型二进制文件,nm -u + strings + 导出列表通常无需反汇编即可回答“它做了什么”。
  • 要查找谁使用了某个字符串:从 otool -tV 的“literal pool for”注释中获取其地址,或使用 rg -n 'literal pool for: "X'。然后在该行上方找到包围函数标签。
  • bl 目标中的 C++ 名称已经告诉你协议(例如 getpeereid、write、std::hash)。读取调用序列,而不是每条指令。
  • 对于 JS,从原生/JS 边界向内进行:找到原生调用,然后找到其调用者类,接着找到 IPC 注册,最后找到渲染器/preload。
  • 版本比较:在比较任何代码之前,先比较清单(nm、模块列表、go version -m)。
  • 功能重建:编写最小的重新实现,然后针对原始版本进行测试(相同的输入,相同的输出)。

安装(仅按需)

brew install jadx apktool radare2 binwalk wabt wireshark ghidra · npm i -g @electron/asar · dotnet tool install -g ilspycmd · cargo install cargo-audit。

相关技能