pwn 0.5.622 → 0.5.626

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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -2
  3. data/README.md +7 -1
  4. data/bin/pwn_gqrx_scanner +78 -9
  5. data/documentation/Agent-Tool-Registry.md +2 -2
  6. data/documentation/Configuration.md +11 -2
  7. data/documentation/Cron.md +5 -3
  8. data/documentation/Diagrams.md +1 -1
  9. data/documentation/Extrospection.md +472 -26
  10. data/documentation/FFI.md +72 -8
  11. data/documentation/Home.md +4 -4
  12. data/documentation/How-PWN-Works.md +7 -3
  13. data/documentation/Mistakes.md +9 -0
  14. data/documentation/SDR.md +83 -10
  15. data/documentation/Skills-Memory-Learning.md +100 -10
  16. data/documentation/diagrams/agent-tool-registry.svg +56 -55
  17. data/documentation/diagrams/ai-integration-tool-calling.svg +24 -24
  18. data/documentation/diagrams/aws-cloud-security.svg +25 -25
  19. data/documentation/diagrams/burp-vs-zap-preference.svg +12 -12
  20. data/documentation/diagrams/code-scanning-sast.svg +25 -25
  21. data/documentation/diagrams/cron-scheduling.svg +23 -23
  22. data/documentation/diagrams/dot/agent-tool-registry.dot +2 -2
  23. data/documentation/diagrams/dot/extrospection-world-awareness.dot +33 -6
  24. data/documentation/diagrams/dot/hardware-hacking.dot +3 -1
  25. data/documentation/diagrams/dot/memory-skills-detailed.dot +4 -2
  26. data/documentation/diagrams/dot/mistakes-negative-feedback.dot +4 -2
  27. data/documentation/diagrams/dot/overall-pwn-architecture.dot +3 -3
  28. data/documentation/diagrams/dot/persistence-filesystem.dot +3 -3
  29. data/documentation/diagrams/dot/pwn-ai-feedback-learning-loop.dot +9 -3
  30. data/documentation/diagrams/dot/sdr-radio-flow.dot +39 -16
  31. data/documentation/diagrams/driver-framework.svg +13 -13
  32. data/documentation/diagrams/extrospection-world-awareness.svg +383 -177
  33. data/documentation/diagrams/fuzzing-workflow.svg +24 -24
  34. data/documentation/diagrams/hardware-hacking.svg +84 -69
  35. data/documentation/diagrams/history-to-drivers.svg +18 -18
  36. data/documentation/diagrams/memory-skills-detailed.svg +137 -120
  37. data/documentation/diagrams/mistakes-negative-feedback.svg +152 -137
  38. data/documentation/diagrams/network-infra-testing.svg +27 -27
  39. data/documentation/diagrams/overall-pwn-architecture.svg +97 -97
  40. data/documentation/diagrams/penetration-testing-workflow.svg +30 -30
  41. data/documentation/diagrams/persistence-filesystem.svg +100 -94
  42. data/documentation/diagrams/plugin-ecosystem.svg +35 -35
  43. data/documentation/diagrams/pwn-ai-feedback-learning-loop.svg +254 -207
  44. data/documentation/diagrams/pwn-repl-prototyping.svg +20 -20
  45. data/documentation/diagrams/reporting-pipeline.svg +18 -18
  46. data/documentation/diagrams/reverse-engineering-flow.svg +21 -21
  47. data/documentation/diagrams/sdr-radio-flow.svg +223 -91
  48. data/documentation/diagrams/sessions-cron-automation.svg +18 -18
  49. data/documentation/diagrams/swarm-multi-agent.svg +39 -39
  50. data/documentation/diagrams/web-application-testing.svg +26 -26
  51. data/documentation/diagrams/zero-day-research-flow.svg +25 -25
  52. data/documentation/pwn-ai-Agent.md +14 -8
  53. data/documentation/pwn_silent_help_learn_demo.gif +0 -0
  54. data/lib/pwn/ai/agent/extrospection.rb +2561 -56
  55. data/lib/pwn/ai/agent/tools/extrospection.rb +311 -8
  56. data/lib/pwn/ffi/adalm_pluto.rb +498 -0
  57. data/lib/pwn/ffi/fftw.rb +192 -0
  58. data/lib/pwn/ffi/hack_rf.rb +193 -0
  59. data/lib/pwn/ffi/liquid.rb +244 -0
  60. data/lib/pwn/ffi/rtl_sdr.rb +210 -0
  61. data/lib/pwn/ffi/soapy_sdr.rb +177 -0
  62. data/lib/pwn/ffi/stdio.rb +9 -1
  63. data/lib/pwn/ffi/volk.rb +241 -0
  64. data/lib/pwn/ffi.rb +45 -2
  65. data/lib/pwn/sdr/decoder/adsb.rb +201 -12
  66. data/lib/pwn/sdr/decoder/apt.rb +19 -5
  67. data/lib/pwn/sdr/decoder/base.rb +314 -1
  68. data/lib/pwn/sdr/decoder/bluetooth.rb +91 -18
  69. data/lib/pwn/sdr/decoder/dect.rb +81 -23
  70. data/lib/pwn/sdr/decoder/dsp.rb +229 -15
  71. data/lib/pwn/sdr/decoder/flex.rb +23 -6
  72. data/lib/pwn/sdr/decoder/gps.rb +80 -18
  73. data/lib/pwn/sdr/decoder/gsm.rb +83 -28
  74. data/lib/pwn/sdr/decoder/iridium.rb +80 -18
  75. data/lib/pwn/sdr/decoder/lora.rb +84 -28
  76. data/lib/pwn/sdr/decoder/lte.rb +80 -18
  77. data/lib/pwn/sdr/decoder/morse.rb +23 -6
  78. data/lib/pwn/sdr/decoder/p25.rb +90 -31
  79. data/lib/pwn/sdr/decoder/pager.rb +23 -6
  80. data/lib/pwn/sdr/decoder/pocsag.rb +23 -6
  81. data/lib/pwn/sdr/decoder/rds.rb +219 -52
  82. data/lib/pwn/sdr/decoder/rfid.rb +81 -27
  83. data/lib/pwn/sdr/decoder/rtl433.rb +85 -27
  84. data/lib/pwn/sdr/decoder/rtty.rb +23 -6
  85. data/lib/pwn/sdr/decoder/wifi.rb +86 -32
  86. data/lib/pwn/sdr/decoder/zigbee.rb +88 -35
  87. data/lib/pwn/sdr/decoder.rb +6 -5
  88. data/lib/pwn/sdr/frequency_allocation.rb +124 -81
  89. data/lib/pwn/sdr/gqrx.rb +1512 -164
  90. data/lib/pwn/version.rb +1 -1
  91. data/spec/lib/pwn/ai/agent/extrospection_spec.rb +96 -0
  92. data/spec/lib/pwn/ai/agent/tools/extrospection_spec.rb +8 -24
  93. data/spec/lib/pwn/ffi/adalm_pluto_spec.rb +37 -0
  94. data/spec/lib/pwn/ffi/fftw_spec.rb +26 -0
  95. data/spec/lib/pwn/ffi/hack_rf_spec.rb +25 -0
  96. data/spec/lib/pwn/ffi/liquid_spec.rb +42 -0
  97. data/spec/lib/pwn/ffi/rtl_sdr_spec.rb +24 -0
  98. data/spec/lib/pwn/ffi/soapy_sdr_spec.rb +25 -0
  99. data/spec/lib/pwn/ffi/volk_spec.rb +36 -0
  100. data/spec/lib/pwn/sdr/decoder/base_iq_spec.rb +65 -0
  101. data/spec/lib/pwn/sdr/decoder/dsp_spec.rb +50 -4
  102. data/spec/lib/pwn/sdr/decoder/rds_spec.rb +45 -4
  103. data/third_party/pwn_rdoc.jsonl +232 -18
  104. metadata +17 -1
@@ -1,33 +1,73 @@
1
- # Extrospection — World Awareness
1
+ # Extrospection — World Awareness (on-demand sensing)
2
2
 
3
3
  `PWN::AI::Agent::Extrospection` is the **outward-facing** counterpart to
4
4
  [Learning](Skills-Memory-Learning.md). Where Learning/Metrics/Mistakes ask
5
- *"how well did **I** do?"*, Extrospection asks *"what does the **world** look
6
- like right now, and has it changed under me?"*
5
+ *"how well did **I** do?"*, Extrospection asks *"what does the **outside world**
6
+ say when I need it for a better answer?"*
7
+
8
+ **Primary intent = sense tools on demand**, not ambient inventory of the local host.
9
+
10
+ | You need… | Call… |
11
+ |---|---|
12
+ | Weather / public page content | `extro_watch` / `extro_verify` / TransparentBrowser |
13
+ | Song on 101.1 FM (RDS) | **`extro_rf_tune(freq: "101.1")`** → RDS `now_playing` / `station` |
14
+ | CVE / Exploit-DB hit | `extro_intel` / `extro_verify(kind: :cve)` |
15
+ | Target DOM / TLS drift | `extro_watch(url:)` / `extro_snapshot(sections: [:web])` |
16
+ | Reverse phone / person / FCC ID / patent / IP pivot | **`extro_osint(query:)`** |
17
+ | Serial / USB-UART / AT-modem banner | **`extro_serial`** |
18
+ | SIP / VoIP / BareSIP status or dial | **`extro_telecomm`** |
19
+ | Live capture / pcap summary | **`extro_packet`** |
20
+ | OCR image / barcode-QR | **`extro_vision`** |
21
+ | Text-to-speech / speech-to-text | **`extro_voice`** |
22
+
23
+ **Secondary** — cheap ambient baseline (`snapshot` / `drift` / `correlate`) so the
24
+ agent can distinguish "I called the API wrong" from "the world moved"
25
+ (kernel upgrade, dongle unplugged). That baseline must never launch GUI / JVM /
26
+ heavy REPL binaries.
7
27
 
8
28
  ![Extrospection engine](diagrams/extrospection-world-awareness.svg)
9
29
 
10
30
  ## Snapshot sections (`extro_snapshot(sections: […])`)
11
31
 
12
- | Section | Probe | Captures |
13
- |---|---|---|
14
- | `:host` | `probe_host` | hostname · uname · distro · arch · CPU · mem |
15
- | `:net` | `probe_net` | interfaces · listening ports · default route |
16
- | `:toolchain` | `probe_toolchain` | `PROBE_BINS` nmap curl git ruby python3 gcc msfconsole sqlmap burpsuite zaproxy openssl docker **+ RF_BINS** |
17
- | `:repo` | `probe_repo` | HEAD · branch · dirty · origin |
18
- | `:env` | `probe_env` | ruby / gem / bundler / PWN version |
19
- | **`:rf`** | **`probe_rf`** | **RTL-SDR / HackRF presence · SoapySDR devices · GQRX socket (127.0.0.1:7356) · Flipper serial · `/dev/tty{USB,ACM}*` · band-plan count** |
20
- | **`:web`** *(opt-in)* | **`probe_web`** | **Renders `web_anchors` via `PWN::Plugins::TransparentBrowser` (`:headless`) — HTTP status · final URL · `<title>` · meta[generator] · `Server` · SHA-256 of *rendered* DOM text · TLS cert fp / notAfter · optional screenshot → `~/.pwn/extrospection/web/<host>.png`** |
21
-
22
- `RF_BINS = %w[rtl_sdr rtl_test rtl_433 hackrf_info gqrx dump1090 multimon-ng SoapySDRUtil]`
23
- these are also merged into `PROBE_BINS` so RF-toolchain drift shows up under
24
- `toolchain.*` diff keys.
32
+ | Section | Probe | Captures | Auto? |
33
+ |---|---|---|---|
34
+ | `:host` | `probe_host` | hostname · uname · distro · arch · CPU · mem | ✅ `AUTO_SECTIONS` |
35
+ | `:net` | `probe_net` | interfaces · listening ports · default route | ❌ on-demand |
36
+ | `:toolchain` | `probe_toolchain` | PATH presence; **safe** bins may get `timeout 2 <bin> --version` | on-demand |
37
+ | `:repo` | `probe_repo` | HEAD · branch · dirty · origin | ✅ `AUTO_SECTIONS` |
38
+ | `:env` | `probe_env` | ruby / gem / bundler / PWN version / AI engine | ✅ `AUTO_SECTIONS` |
39
+ | **`:rf`** | **`probe_rf`** | **RTL-SDR / HackRF presence · SoapySDR · GQRX sock · Flipper · serial · band-plans** | ❌ on-demand |
40
+ | **`:web`** | **`probe_web`** | **Headless render of `web_anchors` (status / title / DOM sha / TLS / screenshot)** | on-demand |
41
+ | **`:osint`** | **`probe_osint`** | **Public OSINT feed catalogue · keyed flags (Shodan/Hunter) · whois/dig bins** | ❌ on-demand |
42
+ | **`:serial`** | **`probe_serial`** | **`/dev/ttyUSB*` `/dev/ttyACM*` · by-id · minicom/picocom presence** | ❌ on-demand |
43
+ | **`:telecomm`** | **`probe_telecomm`** | **BareSIP HTTP · SIP ports · baresip/asterisk/linphone bins** | ❌ on-demand |
44
+ | **`:packet`** | **`probe_packet`** | **tshark/tcpdump/tcpreplay · ifaces · cap dir** | ❌ on-demand |
45
+ | **`:vision`** | **`probe_vision`** | **tesseract langs · zbarimg · convert/identify** | ❌ on-demand |
46
+ | **`:voice`** | **`probe_voice`** | **espeak-ng · festival · whisper · sox · spd-say** | ❌ on-demand |
47
+
48
+ ### `PROBE_BINS` split (why Burp splash used to appear)
49
+
50
+ ```ruby
51
+ SAFE_VERSION_BINS = %w[nmap curl git ruby python3 gcc openssl docker] # --version OK
52
+ PRESENCE_ONLY_BINS = %w[burpsuite zaproxy msfconsole gqrx sqlmap] # path only — NEVER spawn
53
+ RF_BINS = %w[rtl_sdr rtl_test rtl_433 hackrf_info gqrx dump1090 multimon-ng SoapySDRUtil]
54
+ OSINT_BINS = %w[whois dig host curl jq]
55
+ SERIAL_BINS = %w[minicom picocom screen cu]
56
+ TELECOMM_BINS = %w[baresip asterisk linphone sngrep]
57
+ PACKET_BINS = %w[tshark tcpdump tcpreplay dumpcap]
58
+ VISION_BINS = %w[tesseract zbarimg qrencode convert identify]
59
+ VOICE_BINS = %w[sox espeak-ng espeak festival whisper spd-say arecord aplay]
60
+ AUTO_SECTIONS = %i[host repo env] # what auto_extrospect actually runs
61
+ ```
62
+
63
+ `burpsuite` is a Java launcher (`run_java -jar …/burpsuite.jar`). Running it for
64
+ `--version` opens the real GUI → splash screen after every pwn-ai turn when
65
+ `auto_extrospect` was wiring `probe_toolchain` into the default section set.
66
+ **That was the bug.** Presence-only bins + `AUTO_SECTIONS` remove it.
25
67
 
26
68
  `DEFAULT_WEB_ANCHORS` = NVD CVE 2.0 API, Exploit-DB, upstream `pwn/version.rb`.
27
69
  Override / extend via `PWN::Env[:ai][:agent][:extrospection][:web][:anchors]`; set
28
- `allow_targets: true` to also fingerprint `PWN::Env[:targets]`. **`:web` is never
29
- run by `auto_extrospect`** — a headless browser is ~1–3 s cold vs ~50 ms for
30
- `probe_host`, so it's opt-in (`extro_snapshot(sections: %i[web])`).
70
+ `allow_targets: true` to also fingerprint `PWN::Env[:targets]`.
31
71
 
32
72
  ## Verbs
33
73
 
@@ -35,15 +75,22 @@ run by `auto_extrospect`** — a headless browser is ~1–3 s cold vs ~50 ms for
35
75
  |---|---|---|
36
76
  | `extro_snapshot` | Fingerprint host/net/toolchain/repo/env/**rf**/*web* → hash | `extrospection.json` (`snapshot` + rotates `previous`) |
37
77
  | `extro_drift` | Diff live-vs-stored (or stored-vs-previous) | — returns `{changed, added, removed}` with dotted keys |
38
- | `extro_observe` | Record a fact — `category:` one of `recon vuln intel target network env` **`rf` `web`** `misc` | `observations[]` |
78
+ | `extro_observe` | Record a fact — `category:` one of `recon vuln intel target network env` **`rf` `web` `osint` `serial` `telecomm` `packet` `vision` `voice`** `misc` | `observations[]` |
39
79
  | `extro_observations` | Query recorded facts by source/category/target/tag | — |
40
80
  | `extro_intel` | Query NVD / CIRCL / Exploit-DB for keyword or CVE | optional `record: true` → `:intel` observations |
41
81
  | **`extro_watch`** | **Render a URL headless, hash the *rendered* DOM, screenshot, diff vs prior** | `observations[]` (`category: :web`) |
42
82
  | **`extro_verify`** | **Browser-backed self fact-check of a claim (`:cve` `:version` `:doc` `:generic`) → `:confirmed`/`:refuted`/`:unknown`** | `Mistakes.record` / `observe(:intel)` / `Learning.note_outcome` |
83
+ | **`extro_rf_tune`** | **Tune running GQRX + demod + strength + RDS sample → `now_playing` / `station`** | `observations[]` (`category: :rf`, ttl 300s) |
84
+ | **`extro_osint`** | **Aggregate public OSINT APIs (phone / IP / domain / FCC ID / patent / VIN / MAC / callsign / person / company / SEC / CourtListener / Federal Register / UK Police / OTX / URLHaus / openFDA / NPPES / Nominatim / EPSS / CISA KEV / Microlink / vital records / Shodan / Hunter / AbuseIPDB / VT / HIBP / Wayback)** | `observations[]` (`category: :osint`) |
85
+ | **`extro_serial`** | **Open serial device · optional payload · drain response · disconnect** | `observations[]` (`category: :serial`) |
86
+ | **`extro_telecomm`** | **BareSIP inventory / status / dial / hangup (never launches baresip)** | `observations[]` (`category: :telecomm`) |
87
+ | **`extro_packet`** | **Inventory · bounded live capture · pcap summarise (tshark/PacketFu)** | `observations[]` (`category: :packet`) |
88
+ | **`extro_vision`** | **OCR (tesseract/RTesseract) · barcode/QR (zbarimg)** | `observations[]` (`category: :vision`) |
89
+ | **`extro_voice`** | **TTS (espeak-ng/festival/spd-say) · STT (whisper) · inventory** | `observations[]` (`category: :voice`) |
43
90
  | `extro_correlate` | **Join** introspection ↔ extrospection | — actionable findings |
44
91
  | `extro_stats` | snapshot age · observation count · drift counts · **rf_devices** · **web_anchors** | — |
45
92
  | `extro_reset` | Wipe snapshot + observations (new engagement) | — |
46
- | `extro_auto_toggle` | Enable/disable auto-snapshot after every final answer | `PWN::Env[:ai][:agent][:auto_extrospect]` |
93
+ | `extro_auto_toggle` | Enable/disable ambient baseline (`AUTO_SECTIONS` only) after every final answer | `PWN::Env[:ai][:agent][:auto_extrospect]` |
47
94
 
48
95
  ## `extro_correlate` — the point of the whole thing
49
96
 
@@ -65,8 +112,7 @@ iterations rediscovering it.
65
112
 
66
113
  ## `extro_verify` — proactive fact-checking (the browser as a sense organ)
67
114
 
68
- `probe_rf` gave Extrospection **ears**; `PWN::Plugins::TransparentBrowser` gives
69
- it **eyes**. `extro_verify(claim:)` renders a canonical source *with JavaScript
115
+ `probe_rf` is a passive **ear inventory**; `extro_rf_tune` actually **listens** (tune + RDS). `PWN::Plugins::TransparentBrowser` gives Extrospection **eyes**. `extro_verify(claim:)` renders a canonical source *with JavaScript
70
116
  executed* and returns a verdict:
71
117
 
72
118
  | `kind:` | Canonical source (rendered headless) | Verdict logic |
@@ -88,7 +134,261 @@ This adds a **proactive** trigger to the negative-feedback loop: today
88
134
  the world **before** a human does. `commit: false` returns the verdict without
89
135
  side-effects.
90
136
 
91
- ### `revalidate_memory` — the browser as GC for `PWN::Memory`
137
+ ## `extro_rf_tune` — the radio as a sense organ
138
+
139
+ `probe_rf` is a **passive inventory** (what radios are attached / is GQRX
140
+ listening?). `extro_rf_tune` is the **active RF sense organ** — the radio
141
+ analogue of `extro_watch` / `extro_verify`. It never launches GQRX (presence-
142
+ only bin); it connects to an already-running remote-control socket, tunes,
143
+ demodulates, measures strength, and — for FM broadcast — samples RDS:
144
+
145
+ ```text
146
+ extro_rf_tune(freq: "101.1")
147
+ → connect GQRX :7356
148
+ → M WFM_ST 200000 ; F 101100000
149
+ → sample RDS for settle_secs (default 8s)
150
+ → { ok, freq, strength_dbfs, station, now_playing, rds:{pi,ps_name,radiotext}, summary }
151
+ → observe(category: :rf, ttl: 300) # ephemeral — songs change
152
+ ```
153
+
154
+ RDS sampling is **`PWN::SDR::Decoder::RDS.sample`** (non-interactive Hash:
155
+ `pi` / `ps_name` / `radiotext` / `station`). The TTY spinner
156
+ (`Decoder::RDS.decode`) stays the human path used by
157
+ `GQRX.init_freq(decoder: :rds)` (default `interactive: true`). Agents and
158
+ cron pass `interactive: false` (or just call `.sample` / `extro_rf_tune`).
159
+
160
+
161
+ Frequency parsing is free-form: `"101.1"`, `"101.1 FM"`, `"101.1 MHz"`,
162
+ `"101.100.000"`, `101100000`, `"433.92"` all work. Band plan is matched via
163
+ `PWN::SDR::FrequencyAllocation` (`fm_radio` → `WFM_ST` + RDS decoder; AM → AM;
164
+ etc.). Override with `demodulator_mode:` / `bandwidth:` / `rds:`.
165
+
166
+ Requires GQRX remote control listening and an SDR attached; returns
167
+ `{ok:false, advice:…}` with a concrete recovery hint otherwise. Configure:
168
+
169
+ ```ruby
170
+ PWN::Env[:ai][:agent][:extrospection][:rf] = {
171
+ host: '127.0.0.1', port: 7356, settle_secs: 8, ttl: 300
172
+ }
173
+ ```
174
+
175
+ ## `extro_osint` — OSINT sense organ
176
+
177
+ Aggregates as many **public / free** OSINT APIs as practical into one verb.
178
+ Optional keyed feeds (Shodan / Hunter) unlock when `SHODAN_API_KEY` /
179
+ `HUNTER_API_KEY` or `PWN::Env` keys are present. Every feed is best-effort —
180
+ unreachable endpoints return `{error:…}` instead of raising.
181
+
182
+ ### Auto-detected kinds
183
+
184
+ | Kind | Trigger | Default feeds |
185
+ |---|---|---|
186
+ | `:ip` | IPv4 / IPv6 | ip · geo · ipapi_is · iplocate · ipwhois · dns · rdap · bgpview · otx · abuseipdb · greynoise · shodan · hackertarget |
187
+ | `:geo` | street-like address / geo query | geo · nominatim · ip |
188
+ | `:domain` / `:dns` / `:whois` / `:rdap` | FQDN | dns · whois · rdap · crtsh · certspotter · wayback · otx · urlhaus · urlscan · shodan · securitytrails · hackertarget |
189
+ | `:url` | `http(s)://…` | urlscan · otx · urlhaus · wayback · microlink · virustotal |
190
+ | `:email` | `a@b.c` | hunter · person · github · haveibeenpwned |
191
+ | `:phone` | E.164 / NANP | phone · person |
192
+ | `:fcc_id` | `2ABIP-ESP32` style | fcc_id |
193
+ | `:patent` | `US10123456` / `patent …` | patent |
194
+ | `:person` | `Jane Doe` | person · username · github · open_sanctions · agify · genderize · nationalize · vital_records |
195
+ | `:username` / `:github` | `@handle` / `gh:` | username · github |
196
+ | `:company` | `… Inc.` / `LLC` / `Ltd` | opencorporates · sec_edgar · federal_register · person · courtlistener |
197
+ | `:cik` | 10-digit CIK | sec_edgar · opencorporates |
198
+ | `:vital_records` | birth/death/marriage keywords | vital_records · person |
199
+ | `:threat` | forced | otx · urlhaus · threatfox · abuseipdb · greynoise · virustotal · epss · cisa_kev |
200
+ | `:openfda` | forced | openfda |
201
+ | `:vin` | 17-char ISO 3779 VIN | nhtsa (NHTSA vPIC / `PWN::Plugins::VIN`) |
202
+ | `:mac` | `00:11:22:33:44:55` / bare hex / Cisco form | mac_vendor |
203
+ | `:callsign` | amateur radio e.g. `W1AW` | callook |
204
+ | `:npi` | `NPI 1679576722` (prefixed) | nppes |
205
+ | `:cve` | `CVE-YYYY-NNNN` | epss · cisa_kev |
206
+
207
+ ### Public feed catalogue (no key unless noted)
208
+
209
+ | Feed | Sources | Notes |
210
+ |---|---|---|
211
+ | `:ip` / `:geo` | ip-api.com + `PWN::Plugins::IPInfo` · ipwho.is | ISP/ASN/geo/proxy flags |
212
+ | `:dns` | dig + dns.google DoH | A/AAAA/MX/NS/TXT |
213
+ | `:whois` | system `whois` | first 4 kB |
214
+ | `:rdap` | rdap.org domain/IP bootstrap | JSON |
215
+ | `:crtsh` | crt.sh Certificate Transparency | name + issuer + validity |
216
+ | `:bgpview` | api.bgpview.io | IP/ASN/search |
217
+ | `:shodan` | `PWN::Plugins::Shodan` | **needs** `SHODAN_API_KEY` |
218
+ | `:hunter` | `PWN::Plugins::Hunter` | **needs** `HUNTER_API_KEY` |
219
+ | `:phone` | NANP/E.164 heuristic + reverse-lookup search targets | country, area-code structure, people-search URLs |
220
+ | `:fcc_id` | fccid.io · device.report · fcc.gov OET | grantee/product, MHz excerpts |
221
+ | `:patent` | Google Patents HTML + PatentsView API | title, number, assignee, date |
222
+ | `:person` | Wikipedia · Wikidata · OpenSanctions + NamUs/FBI/Charley targets | missing-person pivot plan |
223
+ | `:username` / `:github` | GitHub · GitLab · Reddit public APIs | profile pivots |
224
+ | `:wayback` | archive.org availability + CDX | snapshots |
225
+ | `:otx` | AlienVault OTX indicators + passive DNS | IP/domain/url |
226
+ | `:urlhaus` | abuse.ch URLHaus host/url API | malware distribution URLs |
227
+ | `:threatfox` | abuse.ch ThreatFox IOC search | malware C2 IOCs |
228
+ | `:urlscan` | urlscan.io public search | recent scans / screenshots |
229
+ | `:hackertarget` | api.hackertarget.com | whois/dns/geoip/headers (rate-limited) |
230
+ | `:openfda` | api.fda.gov device 510(k) · drug label · enforcement | FDA public datasets |
231
+ | `:nominatim` | OpenStreetMap Nominatim | geocode / reverse address |
232
+ | `:opencorporates` | api.opencorporates.com | company search (rate-limited free tier) |
233
+ | `:courtlistener` | CourtListener v4 search / people | RECAP opinions + judges |
234
+ | `:sec_edgar` | sec.gov company_tickers + EFTS | CIK/ticker/filings |
235
+ | `:vital_records` | structured public-record plan | FamilySearch · FindAGrave · CDC W2W · NamUs (live certificates are state-restricted) |
236
+ | `:ipapi_is` | api.ipapi.is | IP company/ASN/hosting/proxy/crawler flags (free) |
237
+ | `:iplocate` | iplocate.io | IP geo + threat (proxy/VPN/hosting) |
238
+ | `:ipwhois` | ipwho.is | Free IP geo / connection / timezone |
239
+ | `:abuseipdb` | api.abuseipdb.com | IP reputation — **needs** `ABUSEIPDB_API_KEY` |
240
+ | `:virustotal` | virustotal.com API v3 | URL/domain/IP analysis — **needs** `VIRUSTOTAL_API_KEY` |
241
+ | `:greynoise` | greynoise.io community / v2 | Internet scanner noise (community free; key upgrades) |
242
+ | `:certspotter` | api.certspotter.com | Certificate Transparency issuances |
243
+ | `:epss` | api.first.org/data/v1/epss | Exploit Prediction Scoring System for a CVE |
244
+ | `:cisa_kev` | CISA Known Exploited Vulnerabilities catalog | KEV membership / ransomware flag |
245
+ | `:nhtsa` | vpic.nhtsa.dot.gov + `PWN::Plugins::VIN` | VIN decode / make-model |
246
+ | `:nppes` | npiregistry.cms.hhs.gov | US healthcare NPI provider lookup |
247
+ | `:federal_register` | federalregister.gov API | US Federal Register document search |
248
+ | `:uk_police` | data.police.uk | UK street-level crime / force catalogue |
249
+ | `:callook` | callook.info | US amateur-radio callsign (FCC ULS) |
250
+ | `:mac_vendor` | maclookup.app · macvendors.com | MAC OUI → vendor |
251
+ | `:universities` | universities.hipolabs.com | University name / domain / country |
252
+ | `:microlink` | api.microlink.io | Link unfurl (OG title/desc/image) |
253
+ | `:agify` / `:genderize` / `:nationalize` | agify.io · genderize.io · nationalize.io | First-name age/gender/nationality estimates |
254
+ | `:haveibeenpwned` | haveibeenpwned.com v3 | Breach membership — **needs** `HIBP_API_KEY` |
255
+ | `:securitytrails` | api.securitytrails.com | Domain DNS history — **needs** `SECURITYTRAILS_API_KEY` |
256
+
257
+ > Feed selection inspired by [public-api-lists/public-api-lists](https://github.com/public-api-lists/public-api-lists) (Anti-Malware, Security, Geocoding, Government, Health, Open Data, Vehicle, Development).
258
+
259
+ ### Examples
260
+
261
+ ```
262
+ extro_osint(query: "+13125551212")
263
+ → kind=:phone · feeds.phone = {country_guess, nanp, reverse_lookup_targets}
264
+
265
+ extro_osint(query: "2ABIP-ESP32", kind: :fcc_id)
266
+ → fccid.io / device.report excerpts + MHz list
267
+
268
+ extro_osint(query: "US10123456", kind: :patent)
269
+ → google patents titles + patentsview JSON
270
+
271
+ extro_osint(query: "8.8.8.8")
272
+ → ip-api geo + RDAP + BGPView + OTX pulses
273
+
274
+ extro_osint(query: "Ada Lovelace", kind: :person)
275
+ → Wikipedia / Wikidata / OpenSanctions + missing-person targets
276
+
277
+ extro_osint(query: "Acme Robotics LLC", kind: :company)
278
+ → OpenCorporates + SEC EDGAR tickers + CourtListener
279
+
280
+ extro_osint(query: "birth record Jane Doe", kind: :vital_records)
281
+ → public genealogy + state vital-records index (no closed B2B scrape)
282
+
283
+ extro_osint(query: "1HGCM82633A004352")
284
+ → kind=:vin · NHTSA vPIC decode (make/model/year/plant)
285
+
286
+ extro_osint(query: "00:11:22:33:44:55")
287
+ → kind=:mac · OUI vendor (maclookup / macvendors)
288
+
289
+ extro_osint(query: "W1AW")
290
+ → kind=:callsign · Callook FCC ULS (trustee, class, grid)
291
+
292
+ extro_osint(query: "CVE-2021-44228")
293
+ → kind=:cve · FIRST EPSS score + CISA KEV membership
294
+
295
+ extro_osint(query: "8.8.8.8", feeds: ["ipapi_is", "iplocate", "greynoise", "abuseipdb"])
296
+ → multi-source IP threat/geo (keyed feeds skip cleanly when no key)
297
+ ```
298
+
299
+ Configure:
300
+
301
+ ```ruby
302
+ PWN::Env[:ai][:agent][:extrospection][:osint] = {
303
+ ttl: 86_400,
304
+ api_keys: {
305
+ shodan: '…',
306
+ hunter: '…',
307
+ abuseipdb: '…',
308
+ virustotal: '…',
309
+ greynoise: '…',
310
+ haveibeenpwned: '…',
311
+ securitytrails: '…'
312
+ }
313
+ }
314
+ # ENV fallbacks also accepted:
315
+ # SHODAN_API_KEY, HUNTER_API_KEY, ABUSEIPDB_API_KEY, VIRUSTOTAL_API_KEY,
316
+ # GREYNOISE_API_KEY, HIBP_API_KEY, SECURITYTRAILS_API_KEY
317
+ ```
318
+
319
+
320
+ ## `extro_serial` — Serial sense organ
321
+
322
+ Passive inventory via `snapshot(sections: [:serial])` (`/dev/ttyUSB*`,
323
+ `/dev/ttyACM*`, `/dev/serial/by-id/*`). The active verb opens a device through
324
+ `PWN::Plugins::Serial`, optionally writes a payload (AT command or bytes),
325
+ drains the response for `settle_secs`, and **always disconnects** so other
326
+ tools can reclaim the bus:
327
+
328
+ ```text
329
+ extro_serial(block_dev: "/dev/ttyUSB0", baud: 115200, payload: "ATI\r")
330
+ → { ok, text, hex, line_state, modem_params, bytes }
331
+ → observe(category: :serial)
332
+ ```
333
+
334
+ ## `extro_telecomm` — SIP / VoIP / PSTN sense organ
335
+
336
+ Telecomm analogue of `extro_rf_tune`. Talks to a **running** BareSIP HTTP
337
+ control socket (never launches it). Actions:
338
+
339
+ | action | Effect |
340
+ |---|---|
341
+ | `:inventory` / `:status` | bins · SIP listen ports · BareSIP HTTP reachability · status text |
342
+ | `:dial` | require `target:` SIP URI or E.164 — **OPSEC: real call** |
343
+ | `:hangup` | hang up active call |
344
+
345
+ ```ruby
346
+ PWN::Env[:ai][:agent][:extrospection][:telecomm] = {
347
+ host: '127.0.0.1', port: 8000, ttl: 600
348
+ }
349
+ ```
350
+
351
+ ## `extro_packet` — Packet sense organ
352
+
353
+ Bounded L2/L3 sensing via `tshark` / `tcpdump` + pcap summarisation through
354
+ `PWN::Plugins::Packet` / tshark hierarchy & conversations:
355
+
356
+ | action | Effect |
357
+ |---|---|
358
+ | `:inventory` | ifaces + PACKET_BINS presence |
359
+ | `:capture` | short capture → `~/.pwn/extrospection/packet/*.pcap` + summary |
360
+ | `:summarize_pcap` | parse `path:` pcap (protocol hierarchy, IP conversations) |
361
+
362
+ Capture is hard-capped (`count` ≤ 200, `timeout` ≤ 60s) so the agent never
363
+ hangs mid-turn.
364
+
365
+ ## `extro_vision` — Vision / OCR sense organ
366
+
367
+ Eyes on the host:
368
+
369
+ | action | Backend |
370
+ |---|---|
371
+ | `:ocr` | `PWN::Plugins::OCR` (RTesseract) → `tesseract` CLI fallback |
372
+ | `:barcode` | `zbarimg` for barcodes / QR |
373
+ | `:inventory` | tesseract langs + vision bins |
374
+
375
+ ```text
376
+ extro_vision(file: "/tmp/shot.png", action: :ocr)
377
+ → { text, chars, preview } → observe(category: :vision)
378
+ ```
379
+
380
+ ## `extro_voice` — Voice (TTS / STT) sense organ
381
+
382
+ | action | Backend |
383
+ |---|---|
384
+ | `:tts` | espeak-ng → spd-say → festival (`PWN::Plugins::Voice`) |
385
+ | `:stt` | OpenAI whisper binary / `PWN::Plugins::Voice.speech_to_text` |
386
+ | `:inventory` | VOICE_BINS presence |
387
+
388
+ Artefacts land under `~/.pwn/extrospection/voice/`.
389
+
390
+ ## `revalidate_memory` — the browser as GC for `PWN::Memory`
391
+
92
392
 
93
393
  `learning_consolidate` only dedupes/truncates; it never asks *"is this still
94
394
  true?"*. `PWN::AI::Agent::Extrospection.revalidate_memory` walks every
@@ -101,7 +401,7 @@ cron_create(name: 'memory_revalidate', schedule: '0 4 * * 0',
101
401
  ruby: 'PWN::AI::Agent::Extrospection.revalidate_memory')
102
402
  ```
103
403
 
104
- ### Configuration
404
+ ## Configuration
105
405
 
106
406
  ```ruby
107
407
  PWN::Env[:ai][:agent][:extrospection][:web] = {
@@ -119,12 +419,19 @@ PWN::Env[:ai][:agent][:extrospection][:web] = {
119
419
 
120
420
  ```ruby
121
421
  extro_reset(confirm: true) # clean slate for new scope
122
- extro_snapshot # baseline (six local sections; add sections:%i[web] for anchors)
422
+ extro_snapshot # baseline (host/net/toolchain/repo/env/rf; add sections:%i[web] for anchors)
123
423
  # … recon …
124
424
  extro_observe(source: 'nmap', target: '10.0.0.5',
125
425
  category: 'recon', data: 'OpenSSH 8.2p1 Ubuntu')
126
426
  extro_observe(source: 'gqrx', target: '433.920MHz',
127
427
  category: 'rf', data: 'peak -34.2 dBFS bw=200k FSK — likely garage remote')
428
+ extro_rf_tune(freq: '101.1') # live RDS → now_playing / station (category: :rf)
429
+ extro_osint(query: '10.0.0.5', kind: :ip)
430
+ extro_osint(query: '2ABIP-ESP32', kind: :fcc_id)
431
+ extro_serial(payload: "ATI\r")
432
+ extro_packet(action: :capture, filter: 'tcp port 22', count: 20)
433
+ extro_vision(file: '/tmp/badge.png', action: :ocr)
434
+ extro_voice(action: :tts, text: 'recon complete')
128
435
  extro_intel(query: 'OpenSSH 8.2p1', record: true)
129
436
  extro_watch(url: 'https://target.acme/api/version') # DOM-hash + screenshot; changed:true on next run
130
437
  extro_verify(claim: 'CVE-2026-12345 affects OpenSSL 3.2.1') # → Mistakes/Memory/observe on verdict
@@ -133,6 +440,145 @@ extro_drift # what moved?
133
440
  extro_correlate # why did it break?
134
441
  ```
135
442
 
443
+ ## Example questions that trigger Extrospection
444
+
445
+ Natural-language prompts that should fire **outward** sensing (grouped by sense organ).
446
+ Location-specific details use **Chicago** as the canonical city so the catalog stays
447
+ portable across deployments.
448
+
449
+ ### RF sense organ (`extro_rf_tune` + RF observations)
450
+
451
+ - “What’s playing on 101.1 right now?”
452
+ - “What’s on Q101 / 101.1 FM in Chicago?”
453
+ - “Is NOAA weather radio for the Chicago area broadcasting an alert?”
454
+ - “Tune 433.92 MHz and report signal strength.”
455
+ - “Any ADS-B traffic near Chicago O’Hare (ORD) right now?”
456
+ - “Sample RDS on 93.1 for 15 seconds — station name and radiotext?”
457
+ - “Is the local Chicago 2 m repeater on 146.x active?”
458
+
459
+ ### Web sense organ (`extro_watch`, `extro_verify`, `snapshot(sections: [:web])`)
460
+
461
+ - “Did the vendor status page change since last check?”
462
+ - “Watch https://status.example.com for DOM changes.”
463
+ - “Has the bug-bounty scope page been updated?”
464
+ - “What’s the weather in Chicago today?” *(live external page)*
465
+ - “Fingerprint that login portal’s generator / title / tech stack.”
466
+ - “Screenshot + hash https://target/app/version for drift tracking.”
467
+
468
+ ### OSINT sense organ (`extro_osint`)
469
+
470
+ - “Reverse lookup +1 312 555 1212.”
471
+ - “Who is the registrant for example.com?”
472
+ - “What device is FCC ID 2ABIP-ESP32?”
473
+ - “Find patents related to software-defined radio receivers.”
474
+ - “Pivot on username @octocat across GitHub/GitLab/Reddit.”
475
+ - “Is Jane Doe listed on OpenSanctions / Wikipedia?”
476
+ - “CT certs for target.acme via crt.sh.”
477
+ - “ASN / BGP neighbours for 1.1.1.1.”
478
+ - “Any Wayback snapshots of https://target.acme/login?”
479
+
480
+ ### Serial sense organ (`extro_serial`)
481
+
482
+ - “What USB serial devices are attached?”
483
+ - “Send ATI to the modem on /dev/ttyUSB0 and show the banner.”
484
+ - “Talk to the Flipper / RFID reader on ttyACM and dump response hex.”
485
+ - “Probe the Arduino console at 115200 8N1.”
486
+
487
+ ### Telecomm sense organ (`extro_telecomm`)
488
+
489
+ - “Is BareSIP running and what is its status?”
490
+ - “List SIP ports listening on this host.”
491
+ - “Dial sip:echo@example.com via BareSIP.” *(OPSEC: real call)*
492
+ - “Hang up the active VoIP call.”
493
+
494
+ ### Packet sense organ (`extro_packet`)
495
+
496
+ - “Inventory capture-capable interfaces and tshark/tcpdump.”
497
+ - “Capture 20 packets on eth0 for tcp/443 and summarise.”
498
+ - “Summarise this pcap: /tmp/eng.pcap.”
499
+ - “What IP conversations dominate that capture?”
500
+
501
+ ### Vision / OCR sense organ (`extro_vision`)
502
+
503
+ - “OCR this screenshot of the login page.”
504
+ - “Decode the QR code on /tmp/badge.png.”
505
+ - “What languages does tesseract have installed?”
506
+
507
+ ### Voice sense organ (`extro_voice`)
508
+
509
+ - “Speak ‘engagement complete’ via TTS.”
510
+ - “Transcribe /tmp/voicemail.wav.”
511
+ - “What TTS/STT engines are available on this host?”
512
+
513
+ ### Fact-check / claim verification (`extro_verify`)
514
+
515
+ - “Is CVE-2024-3094 still rated Critical on NVD?”
516
+ - “Confirm the latest `pwn` gem on RubyGems is 0.5.x.”
517
+ - “Is it true that OpenSSH 9.8p1 is the current stable?”
518
+ - “Verify this claim from the advisory against the official page.”
519
+ - “Does that docs page actually say what we quoted?”
520
+
521
+ ### Threat intel (`extro_intel` ± `record:true`)
522
+
523
+ - “Any known exploits for Apache 2.4.58?”
524
+ - “Look up CVE-2025-XXXX across NVD / CIRCL / Exploit-DB.”
525
+ - “What CVEs hit `libssl` in the last year?”
526
+ - “Is there a public PoC for that JetBrains CVE?”
527
+ - “Correlate installed nmap version against known CVEs.”
528
+
529
+ ### Host / env / toolchain drift (`extro_snapshot`, `extro_drift`, `extro_stats`)
530
+
531
+ - “Did the environment change under us since last session?”
532
+ - “Take a full extrospection snapshot and show drift.”
533
+ - “Is nmap / gqrx / SoapySDR still the version we expect?”
534
+ - “What’s listening on this host right now?”
535
+ - “Show toolchain + repo HEAD fingerprint.”
536
+ - “Why did that tool start failing — world drift or operator error?” → pairs with `extro_correlate`
537
+
538
+ ### Recon memory / observations (`extro_observe`, `extro_observations`)
539
+
540
+ - “Remember that target X bannered as nginx/1.25.3 on 443.”
541
+ - “What recon findings do we have for 10.0.0.5?”
542
+ - “List RF observations from today.”
543
+ - “Any :web watches that changed this week?”
544
+ - “Show intel hits we recorded against this product.”
545
+
546
+ ### Correlation / “am I wrong or did the world move?” (`extro_correlate`)
547
+
548
+ - “Cross-check recent tool failures against host/toolchain drift.”
549
+ - “Did any of our recorded CVEs match installed package versions?”
550
+ - “Is that failed shell call explained by missing binaries?”
551
+ - “Any refuted claims that should invalidate old memory facts?”
552
+
553
+ ### Operational / lifecycle
554
+
555
+ - “Enable ambient auto-extrospect after every answer.”
556
+ - “Reset extrospection for a new engagement.” *(destructive)*
557
+ - “How stale is the current world snapshot?”
558
+ - “Toggle auto-extrospect off while we fuzz.”
559
+
560
+ ### Short “trigger” patterns the agent should recognize
561
+
562
+ | Pattern | Likely tools |
563
+ |--------|----------------|
564
+ | “What’s on … MHz / FM / station” | `extro_rf_tune` |
565
+ | “Reverse phone / FCC ID / patent / whois / person” | `extro_osint` |
566
+ | “Serial / AT modem / ttyUSB / Flipper console” | `extro_serial` |
567
+ | “SIP / VoIP / BareSIP / dial” | `extro_telecomm` |
568
+ | “Capture packets / summarise pcap” | `extro_packet` |
569
+ | “OCR / barcode / QR this image” | `extro_vision` |
570
+ | “Speak this / transcribe that audio” | `extro_voice` |
571
+ | “Did … page change / watch URL” | `extro_watch` |
572
+ | “Is it true that… / confirm CVE / latest version” | `extro_verify` |
573
+ | “Known vulns / CVE / exploit for…” | `extro_intel` |
574
+ | “What changed on this host / drift” | `extro_snapshot` + `extro_drift` |
575
+ | “Remember this finding about…” | `extro_observe` |
576
+ | “Why did X start failing?” | `extro_correlate` + metrics/mistakes |
577
+ | “Weather in Chicago / public page content” | `extro_watch` / TransparentBrowser |
578
+
579
+ The **inward** half of the loop (Memory · Skills · Learning · Mistakes · Metrics)
580
+ has a matching example catalog in [Skills, Memory & Learning](Skills-Memory-Learning.md#example-questions-that-trigger-introspection).
581
+
136
582
  **See also:** [Skills, Memory & Learning](Skills-Memory-Learning.md) ·
137
583
  [Mistakes](Mistakes.md) · [SDR](SDR.md) · [Transparent Browser](Transparent-Browser.md) · [Cron](Cron.md) · [pwn-ai Agent](pwn-ai-Agent.md)
138
584
 
data/documentation/FFI.md CHANGED
@@ -1,14 +1,78 @@
1
- # `PWN::FFI` — Native Calls
1
+ # `PWN::FFI` — Native Calls for DSP & RF
2
2
 
3
- `PWN::FFI::Stdio` (`lib/pwn/ffi/stdio.rb`) exposes libc functions to Ruby via
4
- the `ffi` gem useful for shellcode testing, format-string research and
5
- low-level RE without leaving the REPL.
3
+ Thin `ffi`-gem bindings to **already-installed** system shared objects. Nothing
4
+ is compiled at `gem install` time, nothing shells out. If a `.so` is missing the
5
+ module still loads and `.available?` returns `false` so callers fall back to
6
+ pure Ruby.
6
7
 
7
8
  ```ruby
8
- PWN::FFI::Stdio.printf(fmt: "leak: %p\n", args: [0x41414141])
9
+ PWN::FFI.backends
10
+ # => {FFTW: true, HackRF: true, Liquid: true, RTLSdr: true, SoapySDR: true, Volk: true}
11
+
12
+ PWN::FFI.available?(mod: :Volk) # => true/false
9
13
  ```
10
14
 
11
- Combine with `PWN::Plugins::Assembly` (opcodes ↔ asm) and `pwn-asm` for a
12
- minimal in-process exploit-dev workbench.
15
+ ## Modules (`lib/pwn/ffi/*.rb`)
16
+
17
+ | Module | Shared object | Role |
18
+ |---|---|---|
19
+ | **`Volk`** | `libvolk` | SIMD kernels: s16→f32 convert, accumulate, \|z\|², scale, dot-product. Backs `Decoder::DSP.unpack_s16le` / `rms_dbfs`. |
20
+ | **`Liquid`** | `libliquid` | liquid-dsp: `freqdem` (FM demod), `msresamp_rrrf` (arbitrary resample), Kaiser FIR, DC blocker. Backs `Decoder::DSP.resample` / `dc_block`. |
21
+ | **`FFTW`** | `libfftw3f` | Single-precision FFTW3: real & complex 1-D FFTs, magnitude / power-dB helpers for spectrum work. |
22
+ | **`HackRF`** | `libhackrf` | Control plane (init/open/tune/rate/gains) + device info for Extrospection `probe_rf` and wideband I/Q capture. |
23
+ | **`RTLSdr`** | `librtlsdr` | Device list, tune, gain, blocking `read_sync` of raw u8 I/Q. |
24
+ | **`SoapySDR`** | `libSoapySDR` | Enumerate every Soapy-backed front-end (RTL-SDR, HackRF, Airspy, Pluto, UHD, …); API/ABI/lib version. |
25
+ | `Stdio` | `libc` | Classic `puts` / `printf` / `scanf` for shellcode / format-string research. |
26
+
27
+ ## Design rules
28
+
29
+ 1. **Thin & optional.** Attach a minimal surface; prefer high-level Ruby wrappers
30
+ (`Liquid.freq_demod`, `Volk.unpack_s16le`) over raw C pointers in call sites.
31
+ 2. **No install-time compile.** Use the system package (`libvolk-dev`,
32
+ `libliquid-dev`, `libfftw3-dev`, `libhackrf-dev`, …). On hosts without them
33
+ every `.available?` is `false` and pure-Ruby paths keep working.
34
+ 3. **Namespace hygiene.** `PubFFI = ::FFI` (defined once in `lib/pwn/ffi.rb`)
35
+ avoids the `PWN::FFI` ↔ `::FFI` collision when modules `extend PubFFI::Library`.
36
+ 4. **Fallbacks in DSP.** `PWN::SDR::Decoder::DSP` probes `PWN::FFI.available?`
37
+ and rescues into pure Ruby. Toggle globally with
38
+ `PWN::SDR::Decoder::DSP.native = false`.
39
+
40
+ ## REPL examples
41
+
42
+ ```ruby
43
+ # SIMD-unpack GQRX audio
44
+ raw = udp_chunk # s16le bytes
45
+ samples = PWN::FFI::Volk.unpack_s16le(data: raw)
46
+
47
+ # FM demod of complex baseband
48
+ audio = PWN::FFI::Liquid.freq_demod(iq: interleaved_iq, kf: 0.5)
49
+
50
+ # Real FFT power spectrum
51
+ db = PWN::FFI::FFTW.rfft_power_db(samples: audio, n: 4096)
52
+
53
+ # RF inventory
54
+ PWN::FFI::SoapySDR.list_devices
55
+ PWN::FFI::HackRF.info
56
+ PWN::FFI::RTLSdr.list_devices
57
+ ```
58
+
59
+ ## How this accelerates `PWN::SDR::Decoder::*`
60
+
61
+ Audio-rate protocols (POCSAG, FLEX, Morse, RTTY, APT, …) stay on the GQRX
62
+ 48 kHz UDP tap and now automatically pick up VOLK/liquid for unpack + resample
63
+ hot loops.
64
+
65
+ Wideband protocols (ADS-B, GSM, LTE, LoRa, WiFi, …) still characterise bursts
66
+ from the audio tap via `Base.run_detector`, but can graduate to true
67
+ demodulation by:
68
+
69
+ 1. Opening a front-end via `PWN::FFI::RTLSdr` / `HackRF` / `SoapySDR`
70
+ 2. Reading raw I/Q
71
+ 3. Running native DSP (`Liquid.freq_demod`, `FFTW.rfft`, VOLK magnitude)
72
+ 4. Emitting the same Hash frames decoders already JSONL-log
73
+
74
+ No decoder shells out; Ruby remains the orchestrator.
75
+
76
+ **See also:** [SDR](SDR.md) · [Hardware](Hardware.md)
13
77
 
14
- [← Home](Home.md) · [Hardware](Hardware.md)
78
+ [← Home](Home.md)