pwn 0.5.621 → 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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/Gemfile +1 -1
- data/README.md +7 -1
- data/bin/pwn_gqrx_scanner +78 -9
- data/documentation/Agent-Tool-Registry.md +3 -3
- data/documentation/Configuration.md +301 -47
- data/documentation/Cron.md +5 -3
- data/documentation/Diagrams.md +1 -1
- data/documentation/Extrospection.md +472 -26
- data/documentation/FFI.md +72 -8
- data/documentation/Home.md +4 -4
- data/documentation/How-PWN-Works.md +7 -3
- data/documentation/Mistakes.md +9 -0
- data/documentation/SDR.md +83 -10
- data/documentation/Skills-Memory-Learning.md +101 -11
- data/documentation/diagrams/agent-tool-registry.svg +56 -55
- data/documentation/diagrams/ai-integration-tool-calling.svg +24 -24
- data/documentation/diagrams/aws-cloud-security.svg +25 -25
- data/documentation/diagrams/burp-vs-zap-preference.svg +12 -12
- data/documentation/diagrams/code-scanning-sast.svg +25 -25
- data/documentation/diagrams/cron-scheduling.svg +23 -23
- data/documentation/diagrams/dot/agent-tool-registry.dot +2 -2
- data/documentation/diagrams/dot/extrospection-world-awareness.dot +33 -6
- data/documentation/diagrams/dot/hardware-hacking.dot +3 -1
- data/documentation/diagrams/dot/memory-skills-detailed.dot +4 -2
- data/documentation/diagrams/dot/mistakes-negative-feedback.dot +4 -2
- data/documentation/diagrams/dot/overall-pwn-architecture.dot +3 -3
- data/documentation/diagrams/dot/persistence-filesystem.dot +3 -3
- data/documentation/diagrams/dot/pwn-ai-feedback-learning-loop.dot +10 -4
- data/documentation/diagrams/dot/sdr-radio-flow.dot +39 -16
- data/documentation/diagrams/driver-framework.svg +13 -13
- data/documentation/diagrams/extrospection-world-awareness.svg +383 -177
- data/documentation/diagrams/fuzzing-workflow.svg +24 -24
- data/documentation/diagrams/hardware-hacking.svg +84 -69
- data/documentation/diagrams/history-to-drivers.svg +18 -18
- data/documentation/diagrams/memory-skills-detailed.svg +137 -120
- data/documentation/diagrams/mistakes-negative-feedback.svg +152 -137
- data/documentation/diagrams/network-infra-testing.svg +27 -27
- data/documentation/diagrams/overall-pwn-architecture.svg +97 -97
- data/documentation/diagrams/penetration-testing-workflow.svg +30 -30
- data/documentation/diagrams/persistence-filesystem.svg +100 -94
- data/documentation/diagrams/plugin-ecosystem.svg +35 -35
- data/documentation/diagrams/pwn-ai-feedback-learning-loop.svg +254 -207
- data/documentation/diagrams/pwn-repl-prototyping.svg +20 -20
- data/documentation/diagrams/reporting-pipeline.svg +18 -18
- data/documentation/diagrams/reverse-engineering-flow.svg +21 -21
- data/documentation/diagrams/sdr-radio-flow.svg +223 -91
- data/documentation/diagrams/sessions-cron-automation.svg +18 -18
- data/documentation/diagrams/swarm-multi-agent.svg +39 -39
- data/documentation/diagrams/web-application-testing.svg +26 -26
- data/documentation/diagrams/zero-day-research-flow.svg +25 -25
- data/documentation/pwn-ai-Agent.md +16 -10
- data/documentation/pwn_silent_help_learn_demo.gif +0 -0
- data/lib/pwn/ai/agent/assembly.rb +1 -1
- data/lib/pwn/ai/agent/btc.rb +1 -1
- data/lib/pwn/ai/agent/burp_suite.rb +1 -1
- data/lib/pwn/ai/agent/extrospection.rb +2562 -57
- data/lib/pwn/ai/agent/gqrx.rb +2 -2
- data/lib/pwn/ai/agent/hacker_one.rb +1 -1
- data/lib/pwn/ai/agent/learning.rb +16 -16
- data/lib/pwn/ai/agent/loop.rb +1 -1
- data/lib/pwn/ai/agent/{introspection.rb → reflect.rb} +12 -12
- data/lib/pwn/ai/agent/sast.rb +1 -1
- data/lib/pwn/ai/agent/tools/extrospection.rb +311 -8
- data/lib/pwn/ai/agent/tools/learning.rb +7 -7
- data/lib/pwn/ai/agent/transparent_browser.rb +1 -1
- data/lib/pwn/ai/agent/vuln_gen.rb +2 -2
- data/lib/pwn/ai/agent.rb +1 -1
- data/lib/pwn/ai/open_ai.rb +10 -6
- data/lib/pwn/config.rb +5 -4
- data/lib/pwn/ffi/adalm_pluto.rb +498 -0
- data/lib/pwn/ffi/fftw.rb +192 -0
- data/lib/pwn/ffi/hack_rf.rb +193 -0
- data/lib/pwn/ffi/liquid.rb +244 -0
- data/lib/pwn/ffi/rtl_sdr.rb +210 -0
- data/lib/pwn/ffi/soapy_sdr.rb +177 -0
- data/lib/pwn/ffi/stdio.rb +9 -1
- data/lib/pwn/ffi/volk.rb +241 -0
- data/lib/pwn/ffi.rb +45 -2
- data/lib/pwn/plugins/burp_suite.rb +2 -2
- data/lib/pwn/sast/pom_version.rb +2 -2
- data/lib/pwn/sast/test_case_engine.rb +1 -1
- data/lib/pwn/sdr/decoder/adsb.rb +201 -12
- data/lib/pwn/sdr/decoder/apt.rb +19 -5
- data/lib/pwn/sdr/decoder/base.rb +314 -1
- data/lib/pwn/sdr/decoder/bluetooth.rb +91 -18
- data/lib/pwn/sdr/decoder/dect.rb +81 -23
- data/lib/pwn/sdr/decoder/dsp.rb +229 -15
- data/lib/pwn/sdr/decoder/flex.rb +23 -6
- data/lib/pwn/sdr/decoder/gps.rb +80 -18
- data/lib/pwn/sdr/decoder/gsm.rb +83 -28
- data/lib/pwn/sdr/decoder/iridium.rb +80 -18
- data/lib/pwn/sdr/decoder/lora.rb +84 -28
- data/lib/pwn/sdr/decoder/lte.rb +80 -18
- data/lib/pwn/sdr/decoder/morse.rb +23 -6
- data/lib/pwn/sdr/decoder/p25.rb +90 -31
- data/lib/pwn/sdr/decoder/pager.rb +23 -6
- data/lib/pwn/sdr/decoder/pocsag.rb +23 -6
- data/lib/pwn/sdr/decoder/rds.rb +219 -52
- data/lib/pwn/sdr/decoder/rfid.rb +81 -27
- data/lib/pwn/sdr/decoder/rtl433.rb +85 -27
- data/lib/pwn/sdr/decoder/rtty.rb +23 -6
- data/lib/pwn/sdr/decoder/wifi.rb +86 -32
- data/lib/pwn/sdr/decoder/zigbee.rb +88 -35
- data/lib/pwn/sdr/decoder.rb +6 -5
- data/lib/pwn/sdr/frequency_allocation.rb +124 -81
- data/lib/pwn/sdr/gqrx.rb +1513 -165
- data/lib/pwn/version.rb +1 -1
- data/spec/lib/pwn/ai/agent/extrospection_spec.rb +96 -0
- data/spec/lib/pwn/ai/agent/{introspection_spec.rb → reflect_spec.rb} +3 -3
- data/spec/lib/pwn/ai/agent/tools/extrospection_spec.rb +8 -24
- data/spec/lib/pwn/ffi/adalm_pluto_spec.rb +37 -0
- data/spec/lib/pwn/ffi/fftw_spec.rb +26 -0
- data/spec/lib/pwn/ffi/hack_rf_spec.rb +25 -0
- data/spec/lib/pwn/ffi/liquid_spec.rb +42 -0
- data/spec/lib/pwn/ffi/rtl_sdr_spec.rb +24 -0
- data/spec/lib/pwn/ffi/soapy_sdr_spec.rb +25 -0
- data/spec/lib/pwn/ffi/volk_spec.rb +36 -0
- data/spec/lib/pwn/sdr/decoder/base_iq_spec.rb +65 -0
- data/spec/lib/pwn/sdr/decoder/dsp_spec.rb +50 -4
- data/spec/lib/pwn/sdr/decoder/rds_spec.rb +45 -4
- data/third_party/pwn_rdoc.jsonl +279 -30
- metadata +21 -5
data/lib/pwn/sdr/decoder/rtty.rb
CHANGED
|
@@ -106,11 +106,28 @@ module PWN
|
|
|
106
106
|
|
|
107
107
|
public_class_method def self.decode(opts = {})
|
|
108
108
|
freq_obj = opts[:freq_obj]
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
# Prefer true-air I/Q (FM-demod → existing audio demod) when the
|
|
110
|
+
# operator asks for a source/file or sets freq_obj[:iq_source].
|
|
111
|
+
# Otherwise keep the GQRX 48 kHz UDP audio path (run_native).
|
|
112
|
+
want_iq = opts[:source] || opts[:file] || freq_obj[:iq_source] || freq_obj[:iq_file]
|
|
113
|
+
if want_iq
|
|
114
|
+
PWN::SDR::Decoder::Base.run_iq(
|
|
115
|
+
freq_obj: freq_obj,
|
|
116
|
+
protocol: 'RTTY',
|
|
117
|
+
demod: Demod.new,
|
|
118
|
+
sample_rate: (opts[:sample_rate] || freq_obj[:iq_rate] || 48_000).to_i,
|
|
119
|
+
source: opts[:source],
|
|
120
|
+
file: opts[:file],
|
|
121
|
+
fm_demod: true,
|
|
122
|
+
note: 'RTTY true-air: FM-demod I/Q then native bit recovery; falls back to detector without SDR hardware.'
|
|
123
|
+
)
|
|
124
|
+
else
|
|
125
|
+
PWN::SDR::Decoder::Base.run_native(
|
|
126
|
+
freq_obj: freq_obj,
|
|
127
|
+
protocol: 'RTTY',
|
|
128
|
+
demod: Demod.new
|
|
129
|
+
)
|
|
130
|
+
end
|
|
114
131
|
end
|
|
115
132
|
|
|
116
133
|
# Author(s):: 0day Inc. <support@0dayinc.com>
|
|
@@ -122,7 +139,7 @@ module PWN
|
|
|
122
139
|
# Display Usage for this Module
|
|
123
140
|
|
|
124
141
|
public_class_method def self.help
|
|
125
|
-
puts "USAGE (
|
|
142
|
+
puts "USAGE (true-air I/Q + GQRX-audio native paths, no external binaries):
|
|
126
143
|
#{self}.decode(
|
|
127
144
|
freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
|
|
128
145
|
)
|
data/lib/pwn/sdr/decoder/wifi.rb
CHANGED
|
@@ -3,17 +3,68 @@
|
|
|
3
3
|
module PWN
|
|
4
4
|
module SDR
|
|
5
5
|
module Decoder
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# time. Native mode reports channel occupancy/duty and maps
|
|
10
|
-
# freq_obj to a WLAN channel number. `parse_line` retained for
|
|
11
|
-
# offline pipe-delimited tshark-fields analysis.
|
|
6
|
+
# True-air + detector-fallback decoder for WiFi.
|
|
7
|
+
# Prefers PWN::FFI I/Q (RTL-SDR / ADALM-Pluto / HackRF / capture file)
|
|
8
|
+
# via Base.run_iq; degrades to Base.run_detector with no hardware.
|
|
12
9
|
module WiFi
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
# Streaming I/Q energy/burst demod for Base.run_iq.
|
|
11
|
+
class DemodIQ
|
|
12
|
+
def initialize(rate:, protocol:, modulation:, extra: {})
|
|
13
|
+
@rate = rate.to_f
|
|
14
|
+
@protocol = protocol
|
|
15
|
+
@modulation = modulation
|
|
16
|
+
@extra = extra
|
|
17
|
+
@floor = nil
|
|
18
|
+
@in_burst = false
|
|
19
|
+
@burst_t0 = nil
|
|
20
|
+
@peak = -200.0
|
|
21
|
+
@burst_n = 0
|
|
22
|
+
@threshold = (extra[:threshold] || 8.0).to_f
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def feed_iq(samples, rate: nil, &)
|
|
26
|
+
@rate = rate.to_f if rate
|
|
27
|
+
m2 = PWN::SDR::Decoder::DSP.mag_sq(iq: samples)
|
|
28
|
+
hop = [(@rate / 1000.0).round, 1].max
|
|
29
|
+
i = 0
|
|
30
|
+
while i < m2.length
|
|
31
|
+
win = m2[i, hop]
|
|
32
|
+
break if win.nil? || win.empty?
|
|
33
|
+
|
|
34
|
+
ms = win.sum / win.length
|
|
35
|
+
lvl = ms.positive? ? (10.0 * Math.log10(ms)) : -120.0
|
|
36
|
+
@floor = @floor.nil? ? lvl : ((@floor * 0.98) + (lvl * 0.02))
|
|
37
|
+
delta = lvl - @floor
|
|
38
|
+
if delta >= @threshold
|
|
39
|
+
unless @in_burst
|
|
40
|
+
@in_burst = true
|
|
41
|
+
@burst_t0 = Time.now
|
|
42
|
+
@peak = lvl
|
|
43
|
+
end
|
|
44
|
+
@peak = lvl if lvl > @peak
|
|
45
|
+
elsif @in_burst
|
|
46
|
+
@in_burst = false
|
|
47
|
+
@burst_n += 1
|
|
48
|
+
dur_ms = ((Time.now - @burst_t0) * 1000).round
|
|
49
|
+
msg = {
|
|
50
|
+
protocol: @protocol, event: 'burst', source: 'iq',
|
|
51
|
+
burst_no: @burst_n, peak_dbfs: @peak.round(1),
|
|
52
|
+
floor_dbfs: @floor.round(1), delta_db: (@peak - @floor).round(1),
|
|
53
|
+
duration_ms: dur_ms, modulation: @modulation,
|
|
54
|
+
sample_rate: @rate.to_i
|
|
55
|
+
}.merge(@extra.except(:threshold))
|
|
56
|
+
# protocol-specific enrichment
|
|
57
|
+
msg.merge!(self.class.enrich(msg)) if self.class.respond_to?(:enrich)
|
|
58
|
+
msg[:summary] = format(
|
|
59
|
+
'%<p>s IQ-burst #%<n>d peak=%<pk>+.1f dBFS Δ=%<d>.1f dB dur=%<ms>d ms',
|
|
60
|
+
p: @protocol, n: @burst_n, pk: @peak, d: @peak - @floor, ms: dur_ms
|
|
61
|
+
)
|
|
62
|
+
yield msg if block_given?
|
|
63
|
+
end
|
|
64
|
+
i += hop
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
17
68
|
|
|
18
69
|
# Supported Method Parameters::
|
|
19
70
|
# PWN::SDR::Decoder::WiFi.decode(
|
|
@@ -22,33 +73,38 @@ module PWN
|
|
|
22
73
|
|
|
23
74
|
public_class_method def self.decode(opts = {})
|
|
24
75
|
freq_obj = opts[:freq_obj]
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
PWN::SDR::Decoder::Base.run_detector(
|
|
76
|
+
|
|
77
|
+
rate = (opts[:sample_rate] || freq_obj[:iq_rate] || 2_000_000).to_i
|
|
78
|
+
proto = 'WiFi-802.11'
|
|
79
|
+
demod = DemodIQ.new(
|
|
80
|
+
rate: rate, protocol: proto, modulation: 'OFDM',
|
|
81
|
+
extra: { threshold: 6.0 }
|
|
82
|
+
)
|
|
83
|
+
PWN::SDR::Decoder::Base.run_iq(
|
|
34
84
|
freq_obj: freq_obj,
|
|
35
|
-
protocol:
|
|
36
|
-
|
|
85
|
+
protocol: proto,
|
|
86
|
+
sample_rate: rate,
|
|
87
|
+
source: opts[:source],
|
|
88
|
+
file: opts[:file],
|
|
89
|
+
demod: demod,
|
|
37
90
|
threshold: 6.0,
|
|
38
|
-
|
|
91
|
+
note: '20+ MHz OFDM — true-air I/Q path reports channel occupancy/duty.',
|
|
92
|
+
describe: proc { |b| { modulation: 'OFDM', airtime_ms: b[:duration_ms] } }
|
|
39
93
|
)
|
|
40
94
|
end
|
|
41
95
|
|
|
42
|
-
|
|
43
|
-
|
|
96
|
+
TSHARK_FIELDS = %w[
|
|
97
|
+
frame.time_relative wlan.fc.type_subtype wlan.bssid wlan.sa
|
|
98
|
+
wlan.da wlan_radio.channel wlan_radio.signal_dbm wlan.ssid
|
|
99
|
+
].freeze
|
|
44
100
|
|
|
45
101
|
public_class_method def self.parse_line(opts = {})
|
|
46
102
|
f = opts[:line].to_s.split('|', -1)
|
|
47
103
|
out = {
|
|
48
104
|
protocol: 'WiFi', subtype: f[1], bssid: f[2], sa: f[3], da: f[4],
|
|
49
|
-
channel: f[5],
|
|
105
|
+
channel: f[5], rssi: f[6], ssid: f[7]
|
|
50
106
|
}.reject { |_, v| v.to_s.empty? }
|
|
51
|
-
out[:summary] = "WiFi ch
|
|
107
|
+
out[:summary] = "WiFi ch=#{out[:channel]} BSSID=#{out[:bssid]} SSID=#{out[:ssid]} RSSI=#{out[:rssi]}"
|
|
52
108
|
out
|
|
53
109
|
end
|
|
54
110
|
|
|
@@ -58,16 +114,14 @@ module PWN
|
|
|
58
114
|
"AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n"
|
|
59
115
|
end
|
|
60
116
|
|
|
61
|
-
# Display Usage for this Module
|
|
62
|
-
|
|
63
117
|
public_class_method def self.help
|
|
64
|
-
puts "USAGE (
|
|
118
|
+
puts "USAGE (true-air I/Q via PWN::FFI + detector fallback):
|
|
65
119
|
#{self}.decode(
|
|
66
|
-
freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
|
|
120
|
+
freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq',
|
|
121
|
+
source: 'optional - :auto|:rtlsdr|:adalm_pluto|:file',
|
|
122
|
+
file: 'optional - .cu8/.cs16 capture'
|
|
67
123
|
)
|
|
68
124
|
|
|
69
|
-
#{self}.parse_line(line: '0.1|8|aa:bb:..|aa:bb:..|ff:ff:..|6|-42|linksys')
|
|
70
|
-
|
|
71
125
|
#{self}.authors
|
|
72
126
|
"
|
|
73
127
|
end
|
|
@@ -3,17 +3,68 @@
|
|
|
3
3
|
module PWN
|
|
4
4
|
module SDR
|
|
5
5
|
module Decoder
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# per-channel packet bursts and derives channel 11–26 from
|
|
10
|
-
# freq_obj. `parse_line` retained for offline pipe-delimited
|
|
11
|
-
# analysis.
|
|
6
|
+
# True-air + detector-fallback decoder for ZigBee.
|
|
7
|
+
# Prefers PWN::FFI I/Q (RTL-SDR / ADALM-Pluto / HackRF / capture file)
|
|
8
|
+
# via Base.run_iq; degrades to Base.run_detector with no hardware.
|
|
12
9
|
module ZigBee
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
# Streaming I/Q energy/burst demod for Base.run_iq.
|
|
11
|
+
class DemodIQ
|
|
12
|
+
def initialize(rate:, protocol:, modulation:, extra: {})
|
|
13
|
+
@rate = rate.to_f
|
|
14
|
+
@protocol = protocol
|
|
15
|
+
@modulation = modulation
|
|
16
|
+
@extra = extra
|
|
17
|
+
@floor = nil
|
|
18
|
+
@in_burst = false
|
|
19
|
+
@burst_t0 = nil
|
|
20
|
+
@peak = -200.0
|
|
21
|
+
@burst_n = 0
|
|
22
|
+
@threshold = (extra[:threshold] || 8.0).to_f
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def feed_iq(samples, rate: nil, &)
|
|
26
|
+
@rate = rate.to_f if rate
|
|
27
|
+
m2 = PWN::SDR::Decoder::DSP.mag_sq(iq: samples)
|
|
28
|
+
hop = [(@rate / 1000.0).round, 1].max
|
|
29
|
+
i = 0
|
|
30
|
+
while i < m2.length
|
|
31
|
+
win = m2[i, hop]
|
|
32
|
+
break if win.nil? || win.empty?
|
|
33
|
+
|
|
34
|
+
ms = win.sum / win.length
|
|
35
|
+
lvl = ms.positive? ? (10.0 * Math.log10(ms)) : -120.0
|
|
36
|
+
@floor = @floor.nil? ? lvl : ((@floor * 0.98) + (lvl * 0.02))
|
|
37
|
+
delta = lvl - @floor
|
|
38
|
+
if delta >= @threshold
|
|
39
|
+
unless @in_burst
|
|
40
|
+
@in_burst = true
|
|
41
|
+
@burst_t0 = Time.now
|
|
42
|
+
@peak = lvl
|
|
43
|
+
end
|
|
44
|
+
@peak = lvl if lvl > @peak
|
|
45
|
+
elsif @in_burst
|
|
46
|
+
@in_burst = false
|
|
47
|
+
@burst_n += 1
|
|
48
|
+
dur_ms = ((Time.now - @burst_t0) * 1000).round
|
|
49
|
+
msg = {
|
|
50
|
+
protocol: @protocol, event: 'burst', source: 'iq',
|
|
51
|
+
burst_no: @burst_n, peak_dbfs: @peak.round(1),
|
|
52
|
+
floor_dbfs: @floor.round(1), delta_db: (@peak - @floor).round(1),
|
|
53
|
+
duration_ms: dur_ms, modulation: @modulation,
|
|
54
|
+
sample_rate: @rate.to_i
|
|
55
|
+
}.merge(@extra.except(:threshold))
|
|
56
|
+
# protocol-specific enrichment
|
|
57
|
+
msg.merge!(self.class.enrich(msg)) if self.class.respond_to?(:enrich)
|
|
58
|
+
msg[:summary] = format(
|
|
59
|
+
'%<p>s IQ-burst #%<n>d peak=%<pk>+.1f dBFS Δ=%<d>.1f dB dur=%<ms>d ms',
|
|
60
|
+
p: @protocol, n: @burst_n, pk: @peak, d: @peak - @floor, ms: dur_ms
|
|
61
|
+
)
|
|
62
|
+
yield msg if block_given?
|
|
63
|
+
end
|
|
64
|
+
i += hop
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
17
68
|
|
|
18
69
|
# Supported Method Parameters::
|
|
19
70
|
# PWN::SDR::Decoder::ZigBee.decode(
|
|
@@ -22,31 +73,35 @@ module PWN
|
|
|
22
73
|
|
|
23
74
|
public_class_method def self.decode(opts = {})
|
|
24
75
|
freq_obj = opts[:freq_obj]
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
76
|
+
|
|
77
|
+
rate = (opts[:sample_rate] || freq_obj[:iq_rate] || 2_000_000).to_i
|
|
78
|
+
proto = 'ZigBee'
|
|
79
|
+
demod = DemodIQ.new(
|
|
80
|
+
rate: rate, protocol: proto, modulation: 'O-QPSK',
|
|
81
|
+
extra: { threshold: 8.0 }
|
|
82
|
+
)
|
|
83
|
+
PWN::SDR::Decoder::Base.run_iq(
|
|
28
84
|
freq_obj: freq_obj,
|
|
29
|
-
protocol:
|
|
30
|
-
|
|
85
|
+
protocol: proto,
|
|
86
|
+
sample_rate: rate,
|
|
87
|
+
source: opts[:source],
|
|
88
|
+
file: opts[:file],
|
|
89
|
+
demod: demod,
|
|
31
90
|
threshold: 8.0,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{ modulation: 'O-QPSK', channel: ch, est_octets: octets, classification: octets < 20 ? 'ACK/beacon' : 'data-frame' }
|
|
35
|
-
}
|
|
91
|
+
note: 'O-QPSK 250 kbit/s DSSS — true-air I/Q path reports frame bursts on the tuned channel.',
|
|
92
|
+
describe: proc { |b| { modulation: 'O-QPSK', symbol_rate: 62_500, classification: b[:duration_ms] < 5 ? 'ACK' : 'MAC-frame' } }
|
|
36
93
|
)
|
|
37
94
|
end
|
|
38
95
|
|
|
39
|
-
# Supported Method Parameters::
|
|
40
|
-
# PWN::SDR::Decoder::ZigBee.parse_line(line: 't|src16|dst16|src64|dst64|pan|type|cmd|seq')
|
|
41
|
-
|
|
42
96
|
public_class_method def self.parse_line(opts = {})
|
|
43
|
-
|
|
44
|
-
out
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
out[:
|
|
49
|
-
out
|
|
97
|
+
line = opts[:line].to_s
|
|
98
|
+
out = { protocol: 'ZigBee' }
|
|
99
|
+
out[:pan] = ::Regexp.last_match(1) if line =~ /PAN[:= ]+([0-9A-Fa-f]+)/i
|
|
100
|
+
out[:src] = ::Regexp.last_match(1) if line =~ /src[:= ]+([0-9A-Fa-f:]+)/i
|
|
101
|
+
out[:dst] = ::Regexp.last_match(1) if line =~ /dst[:= ]+([0-9A-Fa-f:]+)/i
|
|
102
|
+
out[:cmd] = ::Regexp.last_match(1) if line =~ /\b(Beacon|Data|ACK|Cmd)\b/i
|
|
103
|
+
out[:summary] = "ZigBee #{out[:cmd]} PAN=#{out[:pan]} #{out[:src]}→#{out[:dst]}"
|
|
104
|
+
out.compact
|
|
50
105
|
end
|
|
51
106
|
|
|
52
107
|
# Author(s):: 0day Inc. <support@0dayinc.com>
|
|
@@ -55,16 +110,14 @@ module PWN
|
|
|
55
110
|
"AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n"
|
|
56
111
|
end
|
|
57
112
|
|
|
58
|
-
# Display Usage for this Module
|
|
59
|
-
|
|
60
113
|
public_class_method def self.help
|
|
61
|
-
puts "USAGE (
|
|
114
|
+
puts "USAGE (true-air I/Q via PWN::FFI + detector fallback):
|
|
62
115
|
#{self}.decode(
|
|
63
|
-
freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
|
|
116
|
+
freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq',
|
|
117
|
+
source: 'optional - :auto|:rtlsdr|:adalm_pluto|:file',
|
|
118
|
+
file: 'optional - .cu8/.cs16 capture'
|
|
64
119
|
)
|
|
65
120
|
|
|
66
|
-
#{self}.parse_line(line: '0.1|0x0001|0xffff||||0x01||42')
|
|
67
|
-
|
|
68
121
|
#{self}.authors
|
|
69
122
|
"
|
|
70
123
|
end
|
data/lib/pwn/sdr/decoder.rb
CHANGED
|
@@ -11,11 +11,12 @@ module PWN
|
|
|
11
11
|
# PWN::SDR::FrequencyAllocation.band_plans.
|
|
12
12
|
#
|
|
13
13
|
# 100 % ruby-native — no decoder in this namespace shells out to an
|
|
14
|
-
# external binary.
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
14
|
+
# external binary. Three pipelines (Base):
|
|
15
|
+
# run_native — GQRX 48 kHz audio tap (POCSAG/FLEX/Morse/RTTY/APT/Pager)
|
|
16
|
+
# run_iq — true-air I/Q via PWN::FFI::{RTLSdr,AdalmPluto,HackRF}
|
|
17
|
+
# or a .cu8/.cs16 capture file; all modules use this when
|
|
18
|
+
# hardware/file is present (ADS-B fully slices Mode-S PPM)
|
|
19
|
+
# run_detector — energy/burst characteriser fallback when no I/Q source
|
|
19
20
|
module Decoder
|
|
20
21
|
autoload :ADSB, 'pwn/sdr/decoder/adsb'
|
|
21
22
|
autoload :APT, 'pwn/sdr/decoder/apt'
|