Imported from aicodeking/leanrea (
leanrea/SKILL.md) via skills.sh. Install upstream withnpx skills add aicodeking/leanrea --skill leanrea. Copyright stays with the author.
leanrea: fast path
You already know these tools. This file only lists the fastest command for each target, and the traps.
Rules
- Work on a copy (
cp -con APFS is instant). Put each extraction in a new dir under$TMPDIR/re/<name>. Reuse an extraction if one exists. - Route by
file -b Tand the magic bytes first. Run the cheapest inventory next. Only then decompile or read code. rgskips hidden and ignored files, and bundlers hide code (Electron:.webpack/,.vite/). Always search extracted trees withrg -uu.- Never
cata big or minified file. Slice it:rg -o '.{0,200}NEEDLE.{0,200}' F | head. Dump a disassembly once to a file, then grep that file. - Batch independent probes into ONE shell call with
;andecho '--- name'markers (quoted: zsh expands a bare==x). Cap every output withheador-m. - Answer from evidence. In the answer, mark each claim as seen (cite file:offset, or a symbol/string) or inferred. A fact you did not find is unknown, not "absent". Static reading is not observed execution.
- A missing tool is not a blocker. Use the fallback in the same row, or
brew install <x>if the user allows it. - Turns cost more than bytes. Aim for 2–4 tool calls: one batched recon call, one or two targeted slices, then answer. Stop when every item you were asked has evidence. Do not re-verify what you already saw.
First move: one call each, paste and run
Native (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
Then read one function: 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
Then slice the needle across the bundle: rg -uu -o --glob '!node_modules' '.{0,250}NEEDLE.{0,250}' $X | head -20.
Go: go version -m T alone usually answers the question.
Router
| Target (detect) | Fastest path |
|---|---|
Electron app (*.app/Contents/Resources/app.asar) |
npx -y @electron/asar extract app.asar X (also see app.asar.unpacked/ for .node addons). Entry point: jq .main X/package.json. Surface: `rg -n -o '.{0,120}(ipcMain.(handle |
| Web bundle / minified JS | Find sourceMappingURL. If the map has sourcesContent, dump the sources: jq -r '.sources[]'. For webpack, grep the module ids, then slice around the hit. For pretty-print: npx -y prettier --parser babel F > F.pretty.js, then read with line ranges. |
| Hermes / bytenode / V8 snapshot | Hermes magic is c6 1f bc 03 (index.android.bundle): use hermes-dec / hbc-decompiler. .jsc (bytenode) cannot be decompiled: use strings and runtime hooks. |
Mach-O (file: Mach-O) |
lipo -info (thin first: lipo -thin arm64 -o T1 T). Then: otool -L (libs); xcrun dyld_info -exports -imports; nm -gU (exports) / nm -u (imports = the capability map); strings -a -n 6; otool -l | rg -A3 'LC_(RPATH|BUILD_VERSION)'; codesign -d --entitlements :- T; otool -ov (ObjC classes/methods); xcrun swift-demangle --compact. |
| Native code logic | otool -tV T > $D/dis.txt (or llvm-objdump -d --no-show-raw-insn -C). Find the function: rg -n '^_?.*Name.*:$'. Read the call/string trail: awk '/^FUNC:/{p=1} p&&/^[_a-zA-Z].*:$/&&!/FUNC/{exit} p' dis.txt | rg 'bl|call|literal|cmp'. otool labels string literals and stubs inline, which is often enough. Only for real pseudocode: Ghidra analyzeHeadless $D p -import T -postScript DecompileAll.java -deleteProject or r2 -qc 'aaa; pdc @ sym.X' T. |
| ELF / PE | readelf -hlSW, readelf -dW (NEEDED), nm -D --defined-only, objdump -d -M intel. PE: objdump -x, pefile/wrestool for resources. checksec: pwn checksec. |
Go (__go_buildinfo, .go.buildinfo) |
go version -m T gives the toolchain, path, mod, deps and build flags in one call. Without Go: strings -a T | rg -m1 -A200 'Go buildinf' . Symbols survive stripping: go tool nm T | rg main\.. |
| Rust | Deps (cargo-auditable): cargo audit bin T, or extract .dep-v0 / __DATA,.dep-v0 and zlib-inflate it. Compiler: strings -a T | rg -o '/rustc/[0-9a-f]{40}' | sort -u. Crates from paths: strings -a T | rg -o '[a-z0-9_-]+-[0-9]+\.[0-9]+\.[0-9]+/src' | sort -u. |
.NET (file: Mono/.Net) |
ilspycmd -p -o X T.dll (install: dotnet tool install -g ilspycmd), then rg. P/Invoke: rg DllImport X. NativeAOT/IL2CPP are native: use the native rows. |
| Android APK | jadx -d X --show-bad-code a.apk then rg. Resources/manifest only: apktool d --no-src -o X a.apk. Quick look: unzip -l a.apk. lib/*/libflutter.so means Flutter (Dart AOT: blutter); libhermes.so means React Native (Hermes row). |
| Flutter snapshot hash | In libapp.so, find magic f5 f5 dc dc; the 32 hex chars at +20 are the snapshot hash. Map it to a Dart version. |
| WASM | wasm-objdump -x, wasm2wat, wasm-decompile (WABT). |
| SQLite | Copy the db, plus the -wal file if present. sqlite3 'file:C?mode=ro' '.schema', then SELECT * FROM t LIMIT 20. |
| plist / nib / car | plutil -p F. Info.plist holds the bundle id, URL schemes and privacy strings. Asset catalogs: assetutil --info Assets.car. |
| pcap / HAR | tshark -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 | sort -u. HAR: jq -r '.log.entries[].request | .method+" "+.url'. |
| Firmware | binwalk -e F or unblob -e X F. Then route each extracted binary through this table. |
| Website | curl -sL URL for the HTML and script srcs, then fetch the JS and slice it. For runtime behavior, use a headless browser with a network log (Playwright page.on('request')) or Chrome --remote-debugging-port. |
| Running app / runtime | macOS: lldb -p PID, dtrace/fs_usage/log stream --predicate 'process=="X"'. Electron main: --inspect (only if the RunAsNode/inspect fuses allow it). Android: adb logcat, frida -U -f pkg -l hook.js. |
Speed patterns
- Strings and imports first. On a small binary,
nm -u+strings+ the export list usually answer "what does it do" with no disassembly. - To find who uses a string: get its address from
otool -tV"literal pool for" comments, orrg -n 'literal pool for: "X'. Then find the enclosing function label above that line. - A C++ name in a
bltarget already tells you the protocol (for examplegetpeereid,write,std::hash). Read the call sequence, not every instruction. - For JS, go from the native/JS boundary inward: find the native call, then its caller class, then the IPC registration, then the renderer/preload.
- Comparing versions: diff the inventories (
nm, the module list,go version -m) before you diff any code. - Rebuilding: write the minimal reimplementation, then test it against the original (same inputs, same outputs).
Install (only on request)
brew install jadx apktool radare2 binwalk wabt wireshark ghidra · npm i -g @electron/asar · dotnet tool install -g ilspycmd · cargo install cargo-audit.
