litclaude-ai 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +155 -0
- package/LICENSE +21 -0
- package/README.md +369 -0
- package/README_ko-KR.md +374 -0
- package/RELEASE_CHECKLIST.md +165 -0
- package/bin/litclaude-ai.js +643 -0
- package/cover.png +0 -0
- package/docs/agents.md +67 -0
- package/docs/hooks.md +134 -0
- package/docs/lsp.md +40 -0
- package/docs/migration.md +209 -0
- package/docs/workflow-compatibility-audit.md +119 -0
- package/generate_cover.py +123 -0
- package/package.json +48 -0
- package/plugins/litclaude/.claude-plugin/plugin.json +25 -0
- package/plugins/litclaude/.lsp.json +13 -0
- package/plugins/litclaude/.mcp.json +9 -0
- package/plugins/litclaude/agents/boulder-executor.md +12 -0
- package/plugins/litclaude/agents/librarian-researcher.md +15 -0
- package/plugins/litclaude/agents/oracle-verifier.md +16 -0
- package/plugins/litclaude/agents/prometheus-planner.md +13 -0
- package/plugins/litclaude/agents/qa-runner.md +16 -0
- package/plugins/litclaude/agents/quality-reviewer.md +17 -0
- package/plugins/litclaude/bin/litclaude-hook.js +110 -0
- package/plugins/litclaude/bin/litclaude-hud.js +271 -0
- package/plugins/litclaude/bin/litclaude-lsp-doctor.js +15 -0
- package/plugins/litclaude/bin/litclaude-mcp.js +70 -0
- package/plugins/litclaude/commands/deep-interview.md +21 -0
- package/plugins/litclaude/commands/dynamic-workflow.md +36 -0
- package/plugins/litclaude/commands/lit-loop.md +40 -0
- package/plugins/litclaude/commands/lit-plan.md +35 -0
- package/plugins/litclaude/commands/litgoal.md +30 -0
- package/plugins/litclaude/commands/review-work.md +35 -0
- package/plugins/litclaude/commands/start-work.md +36 -0
- package/plugins/litclaude/hooks/hooks.json +54 -0
- package/plugins/litclaude/lib/context-pressure.mjs +25 -0
- package/plugins/litclaude/lib/hud-accent-palette.mjs +58 -0
- package/plugins/litclaude/lib/litgoal/cli.mjs +266 -0
- package/plugins/litclaude/lib/litgoal/ledger.mjs +16 -0
- package/plugins/litclaude/lib/litgoal/paths.mjs +7 -0
- package/plugins/litclaude/lib/litgoal/state.mjs +67 -0
- package/plugins/litclaude/lib/mutated-file-paths.mjs +63 -0
- package/plugins/litclaude/lib/start-work-continuation.mjs +99 -0
- package/plugins/litclaude/lib/workflow-check.mjs +83 -0
- package/plugins/litclaude/skills/ai-slop-remover/SKILL.md +142 -0
- package/plugins/litclaude/skills/comment-checker/SKILL.md +55 -0
- package/plugins/litclaude/skills/debugging/SKILL.md +70 -0
- package/plugins/litclaude/skills/debugging/references/methodology/00-setup.md +108 -0
- package/plugins/litclaude/skills/debugging/references/methodology/02-investigate.md +126 -0
- package/plugins/litclaude/skills/debugging/references/methodology/04-oracle-triple.md +106 -0
- package/plugins/litclaude/skills/debugging/references/methodology/05-escalate.md +69 -0
- package/plugins/litclaude/skills/debugging/references/methodology/06-fix.md +116 -0
- package/plugins/litclaude/skills/debugging/references/methodology/08-qa.md +94 -0
- package/plugins/litclaude/skills/debugging/references/methodology/09-cleanup.md +164 -0
- package/plugins/litclaude/skills/debugging/references/methodology/partial-runtime-evidence.md +228 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/go.md +252 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/native-binary.md +484 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/node.md +260 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/python.md +248 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/rust.md +234 -0
- package/plugins/litclaude/skills/debugging/references/tools/ghidra.md +212 -0
- package/plugins/litclaude/skills/debugging/references/tools/playwright-cli.md +194 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwndbg.md +263 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwntools.md +265 -0
- package/plugins/litclaude/skills/deep-interview/SKILL.md +323 -0
- package/plugins/litclaude/skills/deep-interview/scripts/render_progress.py +193 -0
- package/plugins/litclaude/skills/frontend-ui-ux/SKILL.md +62 -0
- package/plugins/litclaude/skills/lit-loop/SKILL.md +144 -0
- package/plugins/litclaude/skills/lit-plan/SKILL.md +125 -0
- package/plugins/litclaude/skills/litgoal/SKILL.md +219 -0
- package/plugins/litclaude/skills/lsp/SKILL.md +63 -0
- package/plugins/litclaude/skills/programming/SKILL.md +106 -0
- package/plugins/litclaude/skills/programming/references/go/README.md +90 -0
- package/plugins/litclaude/skills/programming/references/go/backend-stack.md +641 -0
- package/plugins/litclaude/skills/programming/references/go/bootstrap.md +328 -0
- package/plugins/litclaude/skills/programming/references/go/bubbletea-v2.md +360 -0
- package/plugins/litclaude/skills/programming/references/go/cobra-stack.md +468 -0
- package/plugins/litclaude/skills/programming/references/go/concurrency.md +362 -0
- package/plugins/litclaude/skills/programming/references/go/data-modeling.md +329 -0
- package/plugins/litclaude/skills/programming/references/go/error-handling.md +359 -0
- package/plugins/litclaude/skills/programming/references/go/golangci-strict.md +236 -0
- package/plugins/litclaude/skills/programming/references/go/grpc-connect.md +375 -0
- package/plugins/litclaude/skills/programming/references/go/libraries.md +337 -0
- package/plugins/litclaude/skills/programming/references/go/one-liners.md +202 -0
- package/plugins/litclaude/skills/programming/references/go/sqlc-pgx.md +471 -0
- package/plugins/litclaude/skills/programming/references/go/testing.md +467 -0
- package/plugins/litclaude/skills/programming/references/go/type-patterns.md +298 -0
- package/plugins/litclaude/skills/programming/references/python/README.md +314 -0
- package/plugins/litclaude/skills/programming/references/python/async-anyio.md +442 -0
- package/plugins/litclaude/skills/programming/references/python/data-modeling.md +233 -0
- package/plugins/litclaude/skills/programming/references/python/data-processing.md +133 -0
- package/plugins/litclaude/skills/programming/references/python/error-handling.md +218 -0
- package/plugins/litclaude/skills/programming/references/python/fastapi-stack.md +316 -0
- package/plugins/litclaude/skills/programming/references/python/httpx2-optimization.md +360 -0
- package/plugins/litclaude/skills/programming/references/python/libraries.md +307 -0
- package/plugins/litclaude/skills/programming/references/python/one-liners.md +268 -0
- package/plugins/litclaude/skills/programming/references/python/orjson-stack.md +378 -0
- package/plugins/litclaude/skills/programming/references/python/pydantic-ai.md +285 -0
- package/plugins/litclaude/skills/programming/references/python/pyproject-strict.md +232 -0
- package/plugins/litclaude/skills/programming/references/python/textual-tui.md +201 -0
- package/plugins/litclaude/skills/programming/references/python/type-patterns.md +176 -0
- package/plugins/litclaude/skills/programming/references/rust/README.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/async-tokio.md +299 -0
- package/plugins/litclaude/skills/programming/references/rust/axum-stack.md +467 -0
- package/plugins/litclaude/skills/programming/references/rust/cargo-strict.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/clap-stack.md +409 -0
- package/plugins/litclaude/skills/programming/references/rust/concurrency.md +375 -0
- package/plugins/litclaude/skills/programming/references/rust/libraries.md +439 -0
- package/plugins/litclaude/skills/programming/references/rust/one-liners.md +291 -0
- package/plugins/litclaude/skills/programming/references/rust/proptest-insta.md +429 -0
- package/plugins/litclaude/skills/programming/references/rust/type-state.md +354 -0
- package/plugins/litclaude/skills/programming/references/rust/unsafe-discipline.md +250 -0
- package/plugins/litclaude/skills/programming/references/rust/zero-cost-safety.md +527 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/README.md +289 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/miri-sanitizers-loom.md +411 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/ub-taxonomy.md +269 -0
- package/plugins/litclaude/skills/programming/references/typescript/README.md +195 -0
- package/plugins/litclaude/skills/programming/references/typescript/backend-hono.md +672 -0
- package/plugins/litclaude/skills/programming/references/typescript/bootstrap.md +199 -0
- package/plugins/litclaude/skills/programming/references/typescript/data-modeling.md +202 -0
- package/plugins/litclaude/skills/programming/references/typescript/error-handling.md +169 -0
- package/plugins/litclaude/skills/programming/references/typescript/tsconfig-strict.md +152 -0
- package/plugins/litclaude/skills/programming/references/typescript/type-patterns.md +196 -0
- package/plugins/litclaude/skills/programming/scripts/go/check-no-excuse-rules.sh +173 -0
- package/plugins/litclaude/skills/programming/scripts/go/new-project.py +138 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.editorconfig +13 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.golangci.yml +95 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/AGENTS.md.tmpl +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/README.md.tmpl +12 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/Taskfile.yml +40 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/ci.yml +37 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/config.go +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/gitignore +15 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/main.go.tmpl +22 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/run.go +15 -0
- package/plugins/litclaude/skills/programming/scripts/python/check-no-excuse-rules.py +687 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-project.py +172 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-script.py +116 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.py +296 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.sh +158 -0
- package/plugins/litclaude/skills/programming/scripts/rust/new-project.py +175 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/check-no-excuse-rules.ts +282 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/new-project.ts +177 -0
- package/plugins/litclaude/skills/refactor/SKILL.md +73 -0
- package/plugins/litclaude/skills/remove-ai-slops/SKILL.md +52 -0
- package/plugins/litclaude/skills/review-work/SKILL.md +331 -0
- package/plugins/litclaude/skills/rules/SKILL.md +66 -0
- package/plugins/litclaude/skills/start-work/SKILL.md +132 -0
- package/scripts/audit-plan-checkboxes.mjs +37 -0
- package/scripts/doctor.mjs +41 -0
- package/scripts/inspect-agent-tools.mjs +27 -0
- package/scripts/postinstall.mjs +50 -0
- package/scripts/qa-claude-plugin-smoke.sh +60 -0
- package/scripts/qa-portable-install.sh +136 -0
- package/scripts/validate-plugin.mjs +72 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
# Native Binary Debugging (No Source / Reverse Engineering)
|
|
2
|
+
|
|
3
|
+
For binaries where you don't have trustworthy source: stripped production builds, third-party closed libs, malware, CTF challenges, firmware, vendored libs whose docs lie. The workflow is specific; doing it out of order wastes days.
|
|
4
|
+
|
|
5
|
+
This reference **coordinates** the triage and dynamic work. The heavy tools each have their own reference:
|
|
6
|
+
- **Static decompilation** → [tools/ghidra.md](../tools/ghidra.md)
|
|
7
|
+
- **Interactive debugging** → [tools/pwndbg.md](../tools/pwndbg.md)
|
|
8
|
+
- **Scripted interaction / exploitation** → [tools/pwntools.md](../tools/pwntools.md)
|
|
9
|
+
|
|
10
|
+
Read those before using them — especially Ghidra, which has a surprising amount of workflow that's not obvious.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## ⚠️ STOP — is this actually a stripped C/C++ binary?
|
|
15
|
+
|
|
16
|
+
A growing share of "binaries" are actually **bundled high-level apps** — Bun SEA, Node SEA, Deno compile, pkg, nexe, Electron, Tauri, PyInstaller. Their workflow is completely different: the high-level source is recoverable with the right per-bundler tool (often plaintext, sometimes V8 cache / `.pyc` / eszip needing extra tooling), and Ghidra against the runtime VM wastes hours.
|
|
17
|
+
|
|
18
|
+
Quick check:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
file ./target # Mach-O / ELF / PE - inconclusive
|
|
22
|
+
du -h ./target # 50 MB+ for a "simple CLI" → suspect bundled
|
|
23
|
+
strings -n 12 ./target | rg -iE 'bun|node_modules|webpack|esbuild|deno|pkg/lib|electron|pyinstaller|nexe|NODE_SEA_FUSE|tauri' | head -5
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**If any hits** → close this file, open [bundled-js-binary.md](bundled-js-binary.md) instead. Following the Ghidra/pwndbg path on a bundled-app binary wastes hours decompiling the runtime VM while the app-level bundle is recoverable with the right per-bundler tool (plaintext for Bun/pkg/nexe/Electron-asar; eszip / V8-cache / `.pyc` for Deno / Node SEA / PyInstaller).
|
|
27
|
+
|
|
28
|
+
If `file` says "Mach-O" or "ELF", `du` is < 20 MB, and the strings check is empty → continue here.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## The workflow (do these in order)
|
|
33
|
+
|
|
34
|
+
Every step's output is input to the next. Skipping steps means guessing later.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
[1] Triage → what kind of binary is this?
|
|
38
|
+
[2] Dynamic tracing → what syscalls / libcalls does it make?
|
|
39
|
+
[3] Static analysis → what does it DO, in readable form? (Ghidra)
|
|
40
|
+
[4] Dynamic debug → confirm hypotheses at runtime (pwndbg)
|
|
41
|
+
[5] Scripted repro → lock the bug with a pwntools script
|
|
42
|
+
[6] TDD + fix / report
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Steps 1 and 2 are fast (minutes). Step 3 is slow (tens of minutes to hours depending on size). Don't skip 1-2 and go straight to Ghidra — the triage output tells you what to focus on inside Ghidra.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## [1] Triage — 5-minute fingerprint
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Basic identity
|
|
53
|
+
file ./target
|
|
54
|
+
# elf, mach-o, pe? 32/64-bit? dynamically linked? stripped?
|
|
55
|
+
|
|
56
|
+
# Architecture details
|
|
57
|
+
readelf -h ./target # ELF header: entry point, arch, type
|
|
58
|
+
lipo -info ./target 2>/dev/null # macOS: universal binary?
|
|
59
|
+
|
|
60
|
+
# Interesting strings (often leaks function names, error messages, URLs, API keys)
|
|
61
|
+
strings -n 8 ./target | head -100
|
|
62
|
+
strings -n 8 ./target | grep -iE '(http|/api/|error|debug|version)'
|
|
63
|
+
|
|
64
|
+
# Imported symbols (what does it link against?)
|
|
65
|
+
nm -D ./target 2>/dev/null # dynamic symbols
|
|
66
|
+
objdump -T ./target 2>/dev/null # same, alternate tool
|
|
67
|
+
readelf -d ./target # dynamic section (NEEDED libs)
|
|
68
|
+
ldd ./target 2>/dev/null # resolved library paths
|
|
69
|
+
|
|
70
|
+
# Security posture (affects what exploits / bugs are possible)
|
|
71
|
+
checksec --file=./target # requires pwntools or installing checksec
|
|
72
|
+
# NX, PIE, RELRO, stack canary, FORTIFY
|
|
73
|
+
|
|
74
|
+
# Is it stripped?
|
|
75
|
+
nm ./target 2>/dev/null | head # empty? stripped. full? not stripped.
|
|
76
|
+
file ./target # will say "stripped" or "not stripped"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### ⚠️ `strings -n N` silently drops short content
|
|
80
|
+
|
|
81
|
+
`strings` prints runs of printable characters of length **≥ N**. With `-n 8`, **anything shorter than 8 chars sandwiched between non-printable bytes is dropped silently**. This includes:
|
|
82
|
+
|
|
83
|
+
- Short identifier interpolations in templates (`${x}`, `${i}`, `${R}`)
|
|
84
|
+
- Short embedded constants (`v3`, `null`, integer immediates as bytes)
|
|
85
|
+
- Short error codes between binary padding
|
|
86
|
+
|
|
87
|
+
Real example: a JavaScript template literal `<INSTRUCTIONS>\n${x}\n</INSTRUCTIONS>` came out of `strings -n 8` as `<INSTRUCTIONS>\n</INSTRUCTIONS>` — the `${x}` (4 chars) was dropped. A consumer reading the dump would conclude the template was empty. It is not.
|
|
88
|
+
|
|
89
|
+
**Use `strings` only for fingerprinting (Phase 1).** For any extraction whose correctness matters, **read bytes directly**:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Count occurrences of a needle
|
|
93
|
+
LC_ALL=C grep -aoc 'NEEDLE' ./target
|
|
94
|
+
|
|
95
|
+
# Find offsets
|
|
96
|
+
LC_ALL=C grep -aob 'NEEDLE' ./target | head
|
|
97
|
+
|
|
98
|
+
# Or via Python for byte-precise context
|
|
99
|
+
python3 -c "
|
|
100
|
+
import sys
|
|
101
|
+
data = open('./target','rb').read()
|
|
102
|
+
needle = b'NEEDLE'
|
|
103
|
+
pos = data.find(needle)
|
|
104
|
+
print(repr(data[max(0,pos-100):pos+200]))
|
|
105
|
+
"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
If you must keep using `strings`, lower the threshold: `strings -n 1 -t x ./target | rg ...`. The signal-to-noise drops sharply but short content is preserved.
|
|
109
|
+
|
|
110
|
+
Write the triage summary to the journal:
|
|
111
|
+
|
|
112
|
+
```markdown
|
|
113
|
+
## Binary triage
|
|
114
|
+
- Type: <ELF 64-bit, dynamically linked, stripped>
|
|
115
|
+
- Arch: <x86_64 | arm64 | ...>
|
|
116
|
+
- Libs: <libc, openssl, libcurl>
|
|
117
|
+
- Security: <NX, PIE, Partial RELRO, no canary>
|
|
118
|
+
- Interesting strings: <short list>
|
|
119
|
+
- First hypothesis surface: <which function / area looks most relevant>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## [2] Dynamic tracing — what does it actually call?
|
|
125
|
+
|
|
126
|
+
These are cheap — run them before Ghidra to orient yourself.
|
|
127
|
+
|
|
128
|
+
### Linux: strace + ltrace
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# System calls
|
|
132
|
+
strace -f -o trace.out ./target arg1 arg2
|
|
133
|
+
strace -f -e trace=network ./target # filter to network syscalls
|
|
134
|
+
strace -f -e trace=file ./target # filter to file ops
|
|
135
|
+
|
|
136
|
+
# Library calls (less useful when stripped but still informative)
|
|
137
|
+
ltrace -f -o ltrace.out ./target
|
|
138
|
+
ltrace -f -e 'str*+mem*' ./target # filter to string/mem functions
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### macOS: Mach-O specifics
|
|
142
|
+
|
|
143
|
+
**SIP block reality check.** With System Integrity Protection enabled (default on every modern macOS), `dtruss` / `dtrace` will **silently fail** to attach to:
|
|
144
|
+
- Anything in `/usr`, `/bin`, `/sbin`, `/System`
|
|
145
|
+
- Apple-signed binaries (Xcode CLT, Homebrew formulae from Apple-distributed taps)
|
|
146
|
+
- Notarized vendor binaries (Bun, Deno, Docker Desktop, etc.)
|
|
147
|
+
|
|
148
|
+
`dtruss ./target` will appear to run but produce zero events. This is not a bug; it is the SIP design. Disabling SIP requires a Recovery Mode reboot — usually not worth it. Use the alternatives below.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# dtruss — works only when SIP allows it (your own unsigned binaries)
|
|
152
|
+
sudo dtruss -f ./target 2>&1 | head -20 # equivalent to strace
|
|
153
|
+
# If output is suspiciously empty → SIP blocked it. Switch to lldb or app-level logging.
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Mach-O metadata inspection (no SIP issues, no debugger needed):**
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Architecture and slices
|
|
160
|
+
file ./target # arm64 / x86_64 / universal
|
|
161
|
+
lipo -info ./target # which architectures included
|
|
162
|
+
lipo -thin arm64 ./target -output ./target-arm64 # extract one slice for analysis
|
|
163
|
+
|
|
164
|
+
# Headers & load commands (segments, dylibs, code-signature pointer)
|
|
165
|
+
otool -h ./target # Mach header (cputype, ncmds, flags)
|
|
166
|
+
otool -l ./target | head -100 # load commands; entitlements live in code-signature blob, see codesign below
|
|
167
|
+
|
|
168
|
+
# Dynamic library dependencies (macOS equivalent of ldd)
|
|
169
|
+
otool -L ./target # linked dylibs with versions
|
|
170
|
+
dyld_info ./target # macOS 13+, more detailed than otool -L
|
|
171
|
+
|
|
172
|
+
# Disassembly
|
|
173
|
+
otool -tv ./target | head -200 # quick disassembly without Ghidra
|
|
174
|
+
otool -tV ./target # with symbol-resolved branches
|
|
175
|
+
|
|
176
|
+
# Imported / exported symbols (Apple `nm`, NOT GNU)
|
|
177
|
+
nm -u ./target # undefined references = imports
|
|
178
|
+
nm -gU ./target # external defined = exports
|
|
179
|
+
# Note: GNU `-D`/dynamic flags are not honored on Apple `nm`; use the above forms.
|
|
180
|
+
symbols -fullSourcePath -onlyWithDebugInfo ./target # if any debug info survives
|
|
181
|
+
|
|
182
|
+
# Code signature & entitlements (entitlements come from codesign, NOT otool)
|
|
183
|
+
codesign -dv --entitlements :- ./target 2>&1 # signature info + entitlements XML on stdout
|
|
184
|
+
spctl --assess --type execute -vv ./target # Gatekeeper assessment
|
|
185
|
+
|
|
186
|
+
# Cert chain — extract to a temp dir to avoid creating files named -0/-1 in cwd
|
|
187
|
+
tmp=$(mktemp -d)
|
|
188
|
+
codesign -dvv --extract-certificates="$tmp/cert" ./target 2>&1
|
|
189
|
+
ls -la "$tmp"
|
|
190
|
+
# rm -rf "$tmp" # journal first, clean up later
|
|
191
|
+
|
|
192
|
+
# Strings inside specific segments only (less noise than full-binary strings)
|
|
193
|
+
otool -s __TEXT __cstring ./target # C string section
|
|
194
|
+
otool -s __TEXT __const ./target # constants section
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Interactive debugging on macOS — use `lldb`, not `gdb`.**
|
|
198
|
+
|
|
199
|
+
GDB on macOS requires a self-signed code-signing certificate (`codesign --entitlements gdb.entitlements --sign gdb-cert /opt/homebrew/bin/gdb`) and even then is unreliable on arm64. **Use `lldb` directly** — it ships with Xcode CLT and works without configuration.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Start lldb
|
|
203
|
+
lldb ./target
|
|
204
|
+
|
|
205
|
+
# Set arguments
|
|
206
|
+
(lldb) settings set target.run-args arg1 arg2
|
|
207
|
+
|
|
208
|
+
# Run with breakpoints
|
|
209
|
+
(lldb) breakpoint set --name function_name # symbol-based
|
|
210
|
+
(lldb) breakpoint set --address 0x1000034c0 # address-based
|
|
211
|
+
(lldb) breakpoint set --regex '.*decode.*' # regex over symbols
|
|
212
|
+
|
|
213
|
+
# Run / step / inspect
|
|
214
|
+
(lldb) run
|
|
215
|
+
(lldb) bt # backtrace
|
|
216
|
+
(lldb) frame variable # locals
|
|
217
|
+
(lldb) register read # all registers
|
|
218
|
+
(lldb) memory read --size 8 --format x --count 16 $sp # 16 qwords from stack
|
|
219
|
+
(lldb) disassemble --frame # current function
|
|
220
|
+
(lldb) image list # loaded modules
|
|
221
|
+
(lldb) image lookup -a 0x1000034c0 # which module + symbol owns this address
|
|
222
|
+
|
|
223
|
+
# Process attach to running process
|
|
224
|
+
(lldb) process attach --pid 12345
|
|
225
|
+
(lldb) process attach --name target # attach by name
|
|
226
|
+
|
|
227
|
+
# Print Mach-O specific
|
|
228
|
+
(lldb) image dump sections ./target
|
|
229
|
+
(lldb) image dump symtab ./target
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Function interception via `DYLD_INSERT_LIBRARIES`** (macOS equivalent of `LD_PRELOAD`):
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Build a shim dylib that overrides specific functions
|
|
236
|
+
# Then run target with it preloaded
|
|
237
|
+
DYLD_INSERT_LIBRARIES=./shim.dylib DYLD_FORCE_FLAT_NAMESPACE=1 ./target
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
DYLD_INSERT works in the unrestricted case but is blocked in three distinct scenarios — distinguish them when diagnosing why your shim didn't load:
|
|
241
|
+
|
|
242
|
+
1. **SIP / restricted process** (target has the `__RESTRICT,__restrict` section, is setuid/setgid, or is a platform/Apple-signed binary): dyld unconditionally strips all `DYLD_*` env vars before the process starts. Nothing you set will reach the target.
|
|
243
|
+
2. **Hardened runtime + library validation** (`CS_RUNTIME` flag set, `com.apple.security.cs.disable-library-validation` entitlement absent): the process accepts `DYLD_INSERT_LIBRARIES` but **rejects** loading any dylib that isn't signed by the same Team ID or by Apple. Symptom: shim is found but not loaded; check `log show --predicate 'eventMessage CONTAINS "library validation failed"'`.
|
|
244
|
+
3. **Notarization / Gatekeeper translocation**: the binary may be running from a translocated path; relative paths in `DYLD_INSERT_LIBRARIES` won't resolve. Use absolute paths.
|
|
245
|
+
|
|
246
|
+
Check each:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Restrict segment present? (case 1)
|
|
250
|
+
otool -l ./target | grep -A2 __RESTRICT
|
|
251
|
+
# Hardened runtime flag? (case 2)
|
|
252
|
+
codesign -d --verbose=4 ./target 2>&1 | grep -iE 'flags=|CodeDirectory'
|
|
253
|
+
# Look for "0x10000(runtime)" or similar in the flags line.
|
|
254
|
+
# Disable-library-validation entitlement?
|
|
255
|
+
codesign -d --entitlements :- ./target 2>&1 | grep disable-library-validation
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**App-level debug logging (always works, ignores SIP):**
|
|
259
|
+
|
|
260
|
+
When debugger attach is blocked, fall back to maximizing the app's own logging:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# Try common patterns
|
|
264
|
+
APP_DEBUG=1 APP_LOG_LEVEL=debug APP_LOG_FILE=/tmp/trace.log ./target
|
|
265
|
+
NSDebugEnabled=YES ./target # Cocoa apps
|
|
266
|
+
OS_ACTIVITY_MODE=debug ./target # os_log subsystem
|
|
267
|
+
|
|
268
|
+
# Then read os_log unified logging stream live
|
|
269
|
+
log stream --predicate 'process == "target"' --level debug
|
|
270
|
+
|
|
271
|
+
# Or extract historical logs
|
|
272
|
+
log show --predicate 'process == "target"' --last 1h --info --debug
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
This is the **partial-runtime-evidence path** for macOS. See [methodology/partial-runtime-evidence.md](../methodology/partial-runtime-evidence.md) for how to combine app-level logs with static analysis when wire-level capture is blocked.
|
|
276
|
+
|
|
277
|
+
**Network capture on macOS (TLS-decrypted):**
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
# 1. Find the active network service (don't assume "Wi-Fi"):
|
|
281
|
+
# Map the default-route interface to the matching networksetup service name.
|
|
282
|
+
networksetup -listallnetworkservices # show options
|
|
283
|
+
DEFAULT_IF=$(route -n get default 2>/dev/null | awk '/interface:/ {print $2}')
|
|
284
|
+
echo "Default-route interface: $DEFAULT_IF"
|
|
285
|
+
# Match the interface (en0, en1, ...) back to a service name:
|
|
286
|
+
SERVICE=$(networksetup -listallhardwareports | awk -v iface="$DEFAULT_IF" '
|
|
287
|
+
/^Hardware Port:/ { hp = substr($0, index($0,$3)) }
|
|
288
|
+
/^Device:/ { if ($2 == iface) print hp }
|
|
289
|
+
')
|
|
290
|
+
if [ -z "$SERVICE" ]; then
|
|
291
|
+
echo "Could not auto-detect active service. Pick one from -listallnetworkservices manually." >&2
|
|
292
|
+
echo "Aborting proxy setup." >&2
|
|
293
|
+
false # signal failure but stay safe at top level
|
|
294
|
+
else
|
|
295
|
+
echo "Using service: $SERVICE"
|
|
296
|
+
fi
|
|
297
|
+
|
|
298
|
+
# 2. JOURNAL the original proxy state before changing it (REQUIRED for safe rollback):
|
|
299
|
+
networksetup -getwebproxy "$SERVICE" # save this output to journal
|
|
300
|
+
networksetup -getsecurewebproxy "$SERVICE" # save this too
|
|
301
|
+
|
|
302
|
+
# 3. Start mitmproxy with persistent CA at ~/.mitmproxy/
|
|
303
|
+
mitmproxy --listen-host 127.0.0.1 --listen-port 8888 &
|
|
304
|
+
|
|
305
|
+
# 4. Trust the mitmproxy CA system-wide if the target uses URLSession or any framework
|
|
306
|
+
# that ignores HTTPS_PROXY/SSL_CERT_FILE (most macOS-native apps do):
|
|
307
|
+
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ~/.mitmproxy/mitmproxy-ca-cert.pem
|
|
308
|
+
|
|
309
|
+
# 5. Two routing options. Try env-var first; fall back to system proxy:
|
|
310
|
+
# 5a. Apps that honor env vars (most CLIs):
|
|
311
|
+
HTTPS_PROXY=http://127.0.0.1:8888 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem ./target ...
|
|
312
|
+
|
|
313
|
+
# 5b. Apps that use URLSession / system network config (most GUI apps, Bun, some CLIs):
|
|
314
|
+
networksetup -setwebproxy "$SERVICE" 127.0.0.1 8888
|
|
315
|
+
networksetup -setsecurewebproxy "$SERVICE" 127.0.0.1 8888
|
|
316
|
+
|
|
317
|
+
# 6. Cleanup — RESTORE original state from journal, untrust CA:
|
|
318
|
+
networksetup -setwebproxystate "$SERVICE" off
|
|
319
|
+
networksetup -setsecurewebproxystate "$SERVICE" off
|
|
320
|
+
sudo security delete-certificate -c "mitmproxy" /Library/Keychains/System.keychain
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Critical**: forgetting step 6 leaves all your subsequent traffic mis-routed and silently MITM-able. Journal every step.
|
|
324
|
+
|
|
325
|
+
### What to look for
|
|
326
|
+
|
|
327
|
+
| Observation | Hypothesis |
|
|
328
|
+
|---|---|
|
|
329
|
+
| `open("/etc/secret-config", ...)` | Reads unexpected config; look at what it does with contents |
|
|
330
|
+
| `connect(... 1.2.3.4:443)` | Phones home or depends on an external service |
|
|
331
|
+
| `getenv("FOO")` returning NULL | Env var expected but not set |
|
|
332
|
+
| Repeated `poll`/`epoll_wait` with no progress | Stuck on I/O; check downstream |
|
|
333
|
+
| `SIGSEGV` caught by signal handler | Custom crash recovery — often hides the real bug |
|
|
334
|
+
| `dlopen("libfoo.so.42")` | Dynamic plugin loading; check plugin path |
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## [3] Static analysis with Ghidra
|
|
339
|
+
|
|
340
|
+
When triage + tracing have narrowed you to "something in function X" or "the crypto routine is weird", open Ghidra.
|
|
341
|
+
|
|
342
|
+
**Open [tools/ghidra.md](../tools/ghidra.md) before launching Ghidra** — the import / analyze / decompile workflow is not obvious and first-time users waste an hour figuring it out.
|
|
343
|
+
|
|
344
|
+
Ghidra's decompiler turns machine code into readable-ish C. That's usually what you want. Stay in the Decompiler view; drop to Listing (disassembly) only when the decompiler punts.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## [4] Dynamic debugging with pwndbg
|
|
349
|
+
|
|
350
|
+
Once static analysis gives you a hypothesis ("this branch at 0x401234 is where the validation fails"), confirm it at runtime with pwndbg.
|
|
351
|
+
|
|
352
|
+
**Open [tools/pwndbg.md](../tools/pwndbg.md) before launching gdb.** Pwndbg gives you the context view (registers / stack / disasm / code all visible at once) which is essential for binary debugging.
|
|
353
|
+
|
|
354
|
+
Typical pwndbg flow:
|
|
355
|
+
|
|
356
|
+
```
|
|
357
|
+
$ gdb ./target # pwndbg loads automatically if installed
|
|
358
|
+
pwndbg> break *0x401234 # break at the address static analysis flagged
|
|
359
|
+
pwndbg> run arg1 arg2
|
|
360
|
+
# At the breakpoint:
|
|
361
|
+
pwndbg> context # registers + stack + disasm
|
|
362
|
+
pwndbg> telescope $rdi # walk pointers at $rdi
|
|
363
|
+
pwndbg> x/20xw $rsp # raw dump of stack
|
|
364
|
+
pwndbg> ni / si # step next / step instruction
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## [5] Scripted reproduction with pwntools
|
|
370
|
+
|
|
371
|
+
Once you have a hypothesis with a concrete repro input, lock it down with pwntools. This is the "failing test" equivalent for binaries.
|
|
372
|
+
|
|
373
|
+
**Open [tools/pwntools.md](../tools/pwntools.md)** — the Process/Remote/ELF/context APIs are the foundation.
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
from pwn import *
|
|
377
|
+
|
|
378
|
+
context.binary = elf = ELF('./target')
|
|
379
|
+
|
|
380
|
+
p = process('./target')
|
|
381
|
+
p.sendlineafter(b'> ', b'<trigger input that reproduces the bug>')
|
|
382
|
+
result = p.recvall(timeout=3)
|
|
383
|
+
assert b'expected-output-when-fixed' in result, f'bug repro: {result}'
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
This script is now your "red test". When the fix is applied, the script should pass (or the assertion should be inverted for negative tests — e.g. "the crash string should NOT appear").
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## [6] Fixing a binary bug you can't recompile
|
|
391
|
+
|
|
392
|
+
Three options, in preference order:
|
|
393
|
+
|
|
394
|
+
### Option A: Patch at the source (if you have it)
|
|
395
|
+
|
|
396
|
+
If the bug is in your own code and source is available, fix it there and rebuild. Standard TDD path.
|
|
397
|
+
|
|
398
|
+
### Option B: Binary patch
|
|
399
|
+
|
|
400
|
+
For tiny fixes (one byte, one branch inversion):
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
# Identify the exact byte offset
|
|
404
|
+
# e.g. Ghidra says the bug is at 0x401234 = file offset 0x1234
|
|
405
|
+
printf '\x90\x90' | dd of=./target bs=1 seek=$((0x1234)) conv=notrunc
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Journal the exact `dd` command and the original bytes so you can revert.
|
|
409
|
+
|
|
410
|
+
### Option C: Wrap / shim
|
|
411
|
+
|
|
412
|
+
If you can't patch the binary, write a shim library (LD_PRELOAD on Linux, DYLD_INSERT_LIBRARIES on macOS) that overrides the buggy function. pwntools has examples.
|
|
413
|
+
|
|
414
|
+
### Option D: Report upstream
|
|
415
|
+
|
|
416
|
+
If it's a third-party binary and none of the above are feasible, the "fix" is a high-quality bug report with:
|
|
417
|
+
- Full triage summary
|
|
418
|
+
- Reproducible pwntools script
|
|
419
|
+
- Ghidra decompilation of the buggy function
|
|
420
|
+
- Hypothesis about the root cause
|
|
421
|
+
- Recommended patch sketch (in C or pseudocode)
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Silent-failure patterns in native binaries
|
|
426
|
+
|
|
427
|
+
| Pattern | Why it's silent |
|
|
428
|
+
|---|---|
|
|
429
|
+
| Ignored libc return codes (`read`, `write`, `malloc`) | Bug continues with garbage data; no check |
|
|
430
|
+
| Signal handler swallows SIGSEGV | Crash converted to "something didn't work"; no log |
|
|
431
|
+
| `setjmp`/`longjmp` unwinding over cleanup | Resources leak silently |
|
|
432
|
+
| Thread-local error state never read (`errno`, `GetLastError`) | Error happened, nobody asked |
|
|
433
|
+
| Recovered assertion failure in release build | `assert` compiled out; precondition violations silently corrupt |
|
|
434
|
+
| Dangling pointer reads after free | Often looks like valid data until it doesn't |
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Phase 9 cleanup specifics
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
# Kill debugger sessions
|
|
442
|
+
pkill -f 'gdb' || true
|
|
443
|
+
pkill -f 'lldb' || true
|
|
444
|
+
|
|
445
|
+
# Ghidra scratch projects (if made just for this session)
|
|
446
|
+
# Named something like ~/ghidra-projects/debug-<timestamp>:
|
|
447
|
+
ls -la ~/ghidra-projects/ 2>/dev/null
|
|
448
|
+
# rm -rf ~/ghidra-projects/debug-scratch # only if the journal says to
|
|
449
|
+
|
|
450
|
+
# Core dumps left from crashes
|
|
451
|
+
rm -f ./core ./core.* ~/core.*
|
|
452
|
+
|
|
453
|
+
# strace/ltrace output files
|
|
454
|
+
rm -f trace.out ltrace.out
|
|
455
|
+
|
|
456
|
+
# If you made a binary patch (Option B above), confirm revert
|
|
457
|
+
# The journal should have the original bytes — restore them:
|
|
458
|
+
# printf '<original-bytes>' | dd of=./target bs=1 seek=<offset> conv=notrunc
|
|
459
|
+
|
|
460
|
+
# Trace-output files
|
|
461
|
+
rm -f /tmp/debug-*.bin /tmp/debug-*.strace /tmp/debug-*.ltrace
|
|
462
|
+
|
|
463
|
+
# macOS-specific:
|
|
464
|
+
# Restore proxy settings if you set them (CRITICAL — leaves system traffic mis-routed otherwise)
|
|
465
|
+
# Use the SAME $SERVICE you used when enabling the proxy (read it from the journal).
|
|
466
|
+
# Do NOT hardcode "Wi-Fi" — many machines route traffic over Ethernet, USB tether, or a VPN service.
|
|
467
|
+
[ -n "$SERVICE" ] && {
|
|
468
|
+
networksetup -setwebproxystate "$SERVICE" off 2>/dev/null
|
|
469
|
+
networksetup -setsecurewebproxystate "$SERVICE" off 2>/dev/null
|
|
470
|
+
}
|
|
471
|
+
# Or restore explicitly from the journaled original state — see the proxy section above.
|
|
472
|
+
|
|
473
|
+
# Stop mitmproxy
|
|
474
|
+
pkill -f 'mitmproxy' 2>/dev/null
|
|
475
|
+
|
|
476
|
+
# Remove DYLD shim libraries you built
|
|
477
|
+
rm -f /tmp/*-shim.dylib
|
|
478
|
+
|
|
479
|
+
# Clear extracted strings dumps (these can be huge and may contain secrets)
|
|
480
|
+
rm -f /tmp/*-strings*.txt
|
|
481
|
+
|
|
482
|
+
# Verify hostname resolution returns to normal (mitmproxy can leave entries)
|
|
483
|
+
scutil --dns | head -20
|
|
484
|
+
```
|