pwn 0.5.618 → 0.5.620

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/README.md +14 -6
  4. data/bin/pwn_gqrx_scanner +25 -5
  5. data/documentation/Agent-Tool-Registry.md +3 -3
  6. data/documentation/Diagrams.md +6 -2
  7. data/documentation/Extrospection.md +30 -11
  8. data/documentation/Home.md +5 -4
  9. data/documentation/How-PWN-Works.md +7 -4
  10. data/documentation/Mistakes.md +96 -0
  11. data/documentation/Persistence.md +3 -1
  12. data/documentation/SDR.md +23 -5
  13. data/documentation/Skills-Memory-Learning.md +15 -5
  14. data/documentation/diagrams/agent-tool-registry.svg +94 -92
  15. data/documentation/diagrams/dot/agent-tool-registry.dot +2 -2
  16. data/documentation/diagrams/dot/extrospection-world-awareness.dot +9 -7
  17. data/documentation/diagrams/dot/memory-skills-detailed.dot +12 -5
  18. data/documentation/diagrams/dot/mistakes-negative-feedback.dot +93 -0
  19. data/documentation/diagrams/dot/overall-pwn-architecture.dot +7 -5
  20. data/documentation/diagrams/dot/persistence-filesystem.dot +3 -2
  21. data/documentation/diagrams/dot/pwn-ai-feedback-learning-loop.dot +19 -7
  22. data/documentation/diagrams/dot/sdr-radio-flow.dot +13 -11
  23. data/documentation/diagrams/extrospection-world-awareness.svg +111 -93
  24. data/documentation/diagrams/memory-skills-detailed.svg +146 -97
  25. data/documentation/diagrams/mistakes-negative-feedback.svg +266 -0
  26. data/documentation/diagrams/overall-pwn-architecture.svg +184 -173
  27. data/documentation/diagrams/persistence-filesystem.svg +77 -62
  28. data/documentation/diagrams/pwn-ai-feedback-learning-loop.svg +217 -149
  29. data/documentation/diagrams/sdr-radio-flow.svg +76 -56
  30. data/documentation/pwn-ai-Agent.md +28 -15
  31. data/lib/pwn/ai/agent/extrospection.rb +65 -6
  32. data/lib/pwn/ai/agent/learning.rb +67 -7
  33. data/lib/pwn/ai/agent/loop.rb +72 -6
  34. data/lib/pwn/ai/agent/mistakes.rb +369 -0
  35. data/lib/pwn/ai/agent/prompt_builder.rb +10 -1
  36. data/lib/pwn/ai/agent/tools/extrospection.rb +2 -2
  37. data/lib/pwn/ai/agent/tools/mistakes.rb +132 -0
  38. data/lib/pwn/ai/agent.rb +1 -0
  39. data/lib/pwn/plugins/jira_data_center.rb +2 -2
  40. data/lib/pwn/sdr/decoder/adsb.rb +101 -0
  41. data/lib/pwn/sdr/decoder/apt.rb +75 -0
  42. data/lib/pwn/sdr/decoder/bluetooth.rb +78 -0
  43. data/lib/pwn/sdr/decoder/dect.rb +84 -0
  44. data/lib/pwn/sdr/decoder/gps.rb +83 -0
  45. data/lib/pwn/sdr/decoder/gsm.rb +50 -29
  46. data/lib/pwn/sdr/decoder/iridium.rb +81 -0
  47. data/lib/pwn/sdr/decoder/lora.rb +87 -0
  48. data/lib/pwn/sdr/decoder/lte.rb +88 -0
  49. data/lib/pwn/sdr/decoder/morse.rb +64 -0
  50. data/lib/pwn/sdr/decoder/p25.rb +71 -0
  51. data/lib/pwn/sdr/decoder/pager.rb +77 -0
  52. data/lib/pwn/sdr/decoder/pocsag.rb +26 -11
  53. data/lib/pwn/sdr/decoder/rfid.rb +88 -0
  54. data/lib/pwn/sdr/decoder/rtl433.rb +95 -0
  55. data/lib/pwn/sdr/decoder/rtty.rb +66 -0
  56. data/lib/pwn/sdr/decoder/wifi.rb +98 -0
  57. data/lib/pwn/sdr/decoder/zigbee.rb +96 -0
  58. data/lib/pwn/sdr/decoder.rb +67 -7
  59. data/lib/pwn/sdr/frequency_allocation.rb +82 -41
  60. data/lib/pwn/sdr/gqrx.rb +186 -95
  61. data/lib/pwn/version.rb +1 -1
  62. data/spec/lib/pwn/ai/agent/mistakes_spec.rb +36 -0
  63. data/spec/lib/pwn/ai/agent/tools/mistakes_spec.rb +12 -0
  64. data/spec/lib/pwn/sdr/decoder/adsb_spec.rb +15 -0
  65. data/spec/lib/pwn/sdr/decoder/apt_spec.rb +15 -0
  66. data/spec/lib/pwn/sdr/decoder/bluetooth_spec.rb +15 -0
  67. data/spec/lib/pwn/sdr/decoder/dect_spec.rb +15 -0
  68. data/spec/lib/pwn/sdr/decoder/gps_spec.rb +15 -0
  69. data/spec/lib/pwn/sdr/decoder/iridium_spec.rb +15 -0
  70. data/spec/lib/pwn/sdr/decoder/lora_spec.rb +15 -0
  71. data/spec/lib/pwn/sdr/decoder/lte_spec.rb +15 -0
  72. data/spec/lib/pwn/sdr/decoder/morse_spec.rb +15 -0
  73. data/spec/lib/pwn/sdr/decoder/p25_spec.rb +15 -0
  74. data/spec/lib/pwn/sdr/decoder/pager_spec.rb +15 -0
  75. data/spec/lib/pwn/sdr/decoder/rfid_spec.rb +15 -0
  76. data/spec/lib/pwn/sdr/decoder/rtl433_spec.rb +15 -0
  77. data/spec/lib/pwn/sdr/decoder/rtty_spec.rb +15 -0
  78. data/spec/lib/pwn/sdr/decoder/wifi_spec.rb +15 -0
  79. data/spec/lib/pwn/sdr/decoder/zigbee_spec.rb +15 -0
  80. data/third_party/pwn_rdoc.jsonl +92 -3
  81. metadata +44 -6
  82. data/README.md.bak +0 -200
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+
5
+ module PWN
6
+ module SDR
7
+ module Decoder
8
+ # RFID decoder covering LF (125/134 kHz), HF (13.56 MHz) and UHF
9
+ # (860–960 MHz EPC Gen2). Near-field ASK/load-modulation is not
10
+ # recoverable from GQRX audio, so this drives a Proxmark3 (LF/HF) via
11
+ # `pm3 -c '...'` or `nfc-list` (HF), and `rtl_433` in flex/analyzer mode
12
+ # for UHF backscatter — whichever tool is present.
13
+ module RFID
14
+ # Supported Method Parameters::
15
+ # PWN::SDR::Decoder::RFID.decode(
16
+ # freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
17
+ # )
18
+
19
+ public_class_method def self.decode(opts = {})
20
+ freq_obj = opts[:freq_obj]
21
+ raise 'ERROR: :freq_obj is required' unless freq_obj.is_a?(Hash)
22
+
23
+ hz = PWN::SDR.hz_to_i(freq: freq_obj[:freq])
24
+
25
+ direct_cmd, bins, proto =
26
+ if hz < 1_000_000
27
+ ["pm3 -c 'lf search'", %w[pm3], 'RFID-LF']
28
+ elsif hz.between?(13_000_000, 14_000_000)
29
+ if PWN::SDR::Decoder::Base.bin_available?(bin: 'pm3')
30
+ ["pm3 -c 'hf search'", %w[pm3], 'RFID-HF']
31
+ else
32
+ ['nfc-list -v', %w[nfc-list], 'RFID-HF']
33
+ end
34
+ else
35
+ ["rtl_433 -f #{hz} -A -F json", %w[rtl_433], 'RFID-UHF']
36
+ end
37
+
38
+ PWN::SDR::Decoder::Base.run_pipeline(
39
+ freq_obj: freq_obj,
40
+ protocol: proto,
41
+ required_bins: bins,
42
+ direct_cmd: direct_cmd,
43
+ line_match: /(UID|EPC|TAG|ATQA|SAK|EM4|HID|ISO|Chipset|"model")/i,
44
+ parser: proc { |line| parse_line(line: line) }
45
+ )
46
+ end
47
+
48
+ # Supported Method Parameters::
49
+ # PWN::SDR::Decoder::RFID.parse_line(line: 'UID: 04 A1 B2 C3 D4 E5 F6')
50
+
51
+ public_class_method def self.parse_line(opts = {})
52
+ line = opts[:line].to_s
53
+ out = { protocol: 'RFID' }
54
+ out[:uid] = ::Regexp.last_match(1).delete(' ') if line =~ /UID[:=]?\s*((?:[0-9A-Fa-f]{2}\s*){4,10})/
55
+ out[:epc] = ::Regexp.last_match(1) if line =~ /EPC[:=]?\s*([0-9A-Fa-f]+)/
56
+ out[:atqa] = ::Regexp.last_match(1) if line =~ /ATQA[:=]?\s*([0-9A-Fa-f ]+)/
57
+ out[:sak] = ::Regexp.last_match(1) if line =~ /SAK[:=]?\s*([0-9A-Fa-f]+)/
58
+ out[:tag] = ::Regexp.last_match(1) if line =~ /(EM4\w+|HID\w*|Mifare\w*|NTAG\w*|ISO\s?\d+)/i
59
+ out[:summary] = "RFID #{out[:tag]} UID=#{out[:uid] || out[:epc]}".squeeze(' ')
60
+ out.compact
61
+ end
62
+
63
+ # Author(s):: 0day Inc. <support@0dayinc.com>
64
+
65
+ public_class_method def self.authors
66
+ "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n"
67
+ end
68
+
69
+ # Display Usage for this Module
70
+
71
+ public_class_method def self.help
72
+ puts "USAGE:
73
+ #{self}.decode(
74
+ freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
75
+ )
76
+
77
+ NOTE: LF/HF need a Proxmark3 (`pm3`) or libnfc reader; UHF uses
78
+ `rtl_433 -A`. Band chosen automatically from freq_obj[:freq].
79
+
80
+ #{self}.parse_line(line: 'UID: 04 A1 B2 C3 D4 E5 F6 SAK: 08 Mifare Classic 1K')
81
+
82
+ #{self}.authors
83
+ "
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'shellwords'
5
+
6
+ module PWN
7
+ module SDR
8
+ module Decoder
9
+ # Generic ISM/keyfob/sensor decoder backed by `rtl_433`.
10
+ #
11
+ # Covers the OOK/ASK/FSK device zoo on 300–315 / 390 / 433.92 / 868 /
12
+ # 902–928 MHz: car/garage keyfobs, TPMS, weather stations, utility
13
+ # meters, doorbells, alarm PIRs, etc. rtl_433 owns the SDR directly and
14
+ # emits one JSON object per decoded frame (`-F json`), which this module
15
+ # merges verbatim into the freq_obj log line.
16
+ module RTL433
17
+ # Supported Method Parameters::
18
+ # PWN::SDR::Decoder::RTL433.decode(
19
+ # freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
20
+ # )
21
+
22
+ public_class_method def self.decode(opts = {})
23
+ freq_obj = opts[:freq_obj]
24
+ raise 'ERROR: :freq_obj is required' unless freq_obj.is_a?(Hash)
25
+
26
+ hz = PWN::SDR.hz_to_i(freq: freq_obj[:freq])
27
+ gain = freq_obj[:rf_gain]
28
+ sdr_args = freq_obj[:sdr_args].to_s
29
+
30
+ cmd = ['rtl_433', '-f', hz.to_s, '-F', 'json', '-M', 'level', '-M', 'protocol']
31
+ cmd.push('-g', gain.to_s) if gain
32
+ cmd.push('-d', sdr_args) unless sdr_args.empty?
33
+ direct_cmd = Shellwords.join(cmd)
34
+
35
+ PWN::SDR::Decoder::Base.run_pipeline(
36
+ freq_obj: freq_obj,
37
+ protocol: 'RTL433',
38
+ required_bins: %w[rtl_433],
39
+ direct_cmd: direct_cmd,
40
+ line_match: /^\s*{/,
41
+ parser: proc { |line| parse_line(line: line) }
42
+ )
43
+ end
44
+
45
+ # Supported Method Parameters::
46
+ # PWN::SDR::Decoder::RTL433.parse_line(line: '{"time":"...","model":"..."}')
47
+
48
+ public_class_method def self.parse_line(opts = {})
49
+ line = opts[:line].to_s
50
+ h = begin
51
+ JSON.parse(line, symbolize_names: true)
52
+ rescue StandardError
53
+ { unparsed: line }
54
+ end
55
+ out = { protocol: 'RTL433' }.merge(h)
56
+
57
+ bits = []
58
+ bits << out[:model].to_s if out[:model]
59
+ bits << "id=#{out[:id]}" if out[:id]
60
+ bits << "ch=#{out[:channel]}" if out[:channel]
61
+ bits << "code=#{out[:code]}" if out[:code]
62
+ bits << "cmd=#{out[:cmd] || out[:button]}" if out[:cmd] || out[:button]
63
+ bits << "temp=#{out[:temperature_C]}C" if out[:temperature_C]
64
+ bits << "rssi=#{out[:rssi]}" if out[:rssi]
65
+ out[:summary] = bits.empty? ? line[0, 120] : bits.join(' ')
66
+ out
67
+ end
68
+
69
+ # Author(s):: 0day Inc. <support@0dayinc.com>
70
+
71
+ public_class_method def self.authors
72
+ "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n"
73
+ end
74
+
75
+ # Display Usage for this Module
76
+
77
+ public_class_method def self.help
78
+ puts "USAGE:
79
+ #{self}.decode(
80
+ freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
81
+ )
82
+
83
+ NOTE: Requires `rtl_433`. Owns the SDR directly (pass
84
+ freq_obj[:sdr_args] like ':1' or 'driver=hackrf' to select
85
+ a device other than the one GQRX is holding).
86
+
87
+ #{self}.parse_line(line: '{\"model\":\"Acurite-Tower\",\"id\":1234,...}')
88
+
89
+ #{self}.authors
90
+ "
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PWN
4
+ module SDR
5
+ module Decoder
6
+ # RTTY (Radioteletype, ITA2/Baudot) decoder for the amateur RTTY
7
+ # sub-bands (rtty20/rtty40/rtty80). GQRX supplies FM-demodulated audio
8
+ # containing the classic 170 Hz-shift 45.45-baud mark/space tones;
9
+ # `minimodem` recovers the 5-bit Baudot stream and prints ASCII.
10
+ module RTTY
11
+ # Supported Method Parameters::
12
+ # PWN::SDR::Decoder::RTTY.decode(
13
+ # freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
14
+ # )
15
+
16
+ public_class_method def self.decode(opts = {})
17
+ freq_obj = opts[:freq_obj]
18
+
19
+ # minimodem reads s16le mono from stdin at the rate we hand it; keep
20
+ # sox at 22 050 Hz (Base default) and tell minimodem the same.
21
+ PWN::SDR::Decoder::Base.run_pipeline(
22
+ freq_obj: freq_obj,
23
+ protocol: 'RTTY',
24
+ required_bins: %w[sox minimodem],
25
+ decode_cmd: 'minimodem --rx --quiet --rtty -R 22050 -f -',
26
+ line_match: /\S/,
27
+ parser: proc { |line| parse_line(line: line) }
28
+ )
29
+ end
30
+
31
+ # Supported Method Parameters::
32
+ # PWN::SDR::Decoder::RTTY.parse_line(line: 'RYRYRY DE W1AW')
33
+
34
+ public_class_method def self.parse_line(opts = {})
35
+ line = opts[:line].to_s.strip
36
+ out = { protocol: 'RTTY', text: line }
37
+ if (m = line.match(/\bDE\s+([A-Z0-9]{1,3}\d[A-Z]{1,4})\b/))
38
+ out[:callsign] = m[1]
39
+ end
40
+ out[:summary] = "RTTY #{line}"[0, 120]
41
+ out
42
+ end
43
+
44
+ # Author(s):: 0day Inc. <support@0dayinc.com>
45
+
46
+ public_class_method def self.authors
47
+ "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n"
48
+ end
49
+
50
+ # Display Usage for this Module
51
+
52
+ public_class_method def self.help
53
+ puts "USAGE:
54
+ #{self}.decode(
55
+ freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
56
+ )
57
+
58
+ #{self}.parse_line(line: 'RYRYRY CQ CQ DE W1AW')
59
+
60
+ #{self}.authors
61
+ "
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+
5
+ module PWN
6
+ module SDR
7
+ module Decoder
8
+ # 802.11 (WiFi) beacon/probe decoder for the 2.4 / 5 / 6 GHz bands.
9
+ #
10
+ # 20+ MHz OFDM cannot be recovered from a general-purpose SDR at
11
+ # bit-level in real time, so this module drives an existing monitor-mode
12
+ # NIC via `tshark` (or `airodump-ng`) instead — freq_obj[:mon_iface]
13
+ # selects the interface, freq_obj[:freq] is mapped to a WLAN channel.
14
+ module WiFi
15
+ TSHARK_FIELDS = %w[
16
+ frame.time_relative
17
+ wlan.fc.type_subtype
18
+ wlan.bssid
19
+ wlan.sa
20
+ wlan.da
21
+ wlan_radio.channel
22
+ wlan_radio.signal_dbm
23
+ wlan.ssid
24
+ ].freeze
25
+
26
+ # Supported Method Parameters::
27
+ # PWN::SDR::Decoder::WiFi.decode(
28
+ # freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
29
+ # )
30
+
31
+ public_class_method def self.decode(opts = {})
32
+ freq_obj = opts[:freq_obj]
33
+ raise 'ERROR: :freq_obj is required' unless freq_obj.is_a?(Hash)
34
+
35
+ iface = (freq_obj[:mon_iface] || 'wlan0mon').to_s
36
+ hz = PWN::SDR.hz_to_i(freq: freq_obj[:freq])
37
+ mhz = (hz / 1_000_000.0).round
38
+
39
+ tshark = ['tshark', '-i', iface, '-I', '-l', '-n',
40
+ '-Y', 'wlan.fc.type == 0',
41
+ '-T', 'fields', '-E', 'separator=|']
42
+ TSHARK_FIELDS.each { |f| tshark.push('-e', f) }
43
+
44
+ inner = "iw dev #{Shellwords.escape(iface)} set freq #{mhz} 2>/dev/null; " \
45
+ "exec #{Shellwords.join(tshark)}"
46
+ direct_cmd = "bash -c #{Shellwords.escape(inner)}"
47
+
48
+ PWN::SDR::Decoder::Base.run_pipeline(
49
+ freq_obj: freq_obj,
50
+ protocol: 'WiFi-802.11',
51
+ required_bins: %w[tshark iw],
52
+ direct_cmd: direct_cmd,
53
+ line_match: /\S/,
54
+ parser: proc { |line| parse_line(line: line) }
55
+ )
56
+ end
57
+
58
+ # Supported Method Parameters::
59
+ # PWN::SDR::Decoder::WiFi.parse_line(line: 't|subtype|bssid|sa|da|ch|rssi|ssid')
60
+
61
+ public_class_method def self.parse_line(opts = {})
62
+ f = opts[:line].to_s.split('|', -1)
63
+ out = {
64
+ protocol: 'WiFi',
65
+ subtype: f[1], bssid: f[2], sa: f[3], da: f[4],
66
+ channel: f[5], rssi_dbm: f[6], ssid: f[7]
67
+ }.reject { |_, v| v.to_s.empty? }
68
+ out[:summary] = "WiFi ch#{out[:channel]} #{out[:bssid]} '#{out[:ssid]}' #{out[:rssi_dbm]}dBm"
69
+ out
70
+ end
71
+
72
+ # Author(s):: 0day Inc. <support@0dayinc.com>
73
+
74
+ public_class_method def self.authors
75
+ "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n"
76
+ end
77
+
78
+ # Display Usage for this Module
79
+
80
+ public_class_method def self.help
81
+ puts "USAGE:
82
+ #{self}.decode(
83
+ freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
84
+ )
85
+
86
+ NOTE: Requires a monitor-mode WLAN interface (freq_obj[:mon_iface],
87
+ default 'wlan0mon'), `iw`, and `tshark`. General SDRs cannot
88
+ demodulate 802.11 OFDM in real time.
89
+
90
+ #{self}.parse_line(line: '0.1|8|aa:bb:..|aa:bb:..|ff:ff:..|6|-42|linksys')
91
+
92
+ #{self}.authors
93
+ "
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+
5
+ module PWN
6
+ module SDR
7
+ module Decoder
8
+ # IEEE 802.15.4 / ZigBee decoder for the 2.405–2.480 GHz allocation.
9
+ # 250 kbit/s O-QPSK requires raw I/Q; this drives KillerBee's `zbdump`
10
+ # (with any supported adapter — CC2531, ApiMote, RZUSB, HackRF via
11
+ # gr-ieee802-15-4) or falls back to `whsniff` piped through `tshark`
12
+ # for structured PAN/src/dst/cmd extraction.
13
+ module ZigBee
14
+ TSHARK_FIELDS = %w[
15
+ frame.time_relative
16
+ wpan.src16
17
+ wpan.dst16
18
+ wpan.src64
19
+ wpan.dst64
20
+ wpan.dst_pan
21
+ wpan.frame_type
22
+ zbee_nwk.cmd.id
23
+ wpan.seq_no
24
+ ].freeze
25
+
26
+ # Supported Method Parameters::
27
+ # PWN::SDR::Decoder::ZigBee.decode(
28
+ # freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
29
+ # )
30
+
31
+ public_class_method def self.decode(opts = {})
32
+ freq_obj = opts[:freq_obj]
33
+ raise 'ERROR: :freq_obj is required' unless freq_obj.is_a?(Hash)
34
+
35
+ hz = PWN::SDR.hz_to_i(freq: freq_obj[:freq])
36
+ # 802.15.4 ch11..26 → 2405 + 5*(ch-11) MHz
37
+ ch = (((hz - 2_405_000_000) / 5_000_000.0).round + 11).clamp(11, 26)
38
+
39
+ tshark = ['tshark', '-r', '-', '-l', '-T', 'fields', '-E', 'separator=|']
40
+ TSHARK_FIELDS.each { |f| tshark.push('-e', f) }
41
+
42
+ inner = "whsniff -c #{ch} 2>/dev/null | #{Shellwords.join(tshark)}"
43
+ direct_cmd = "bash -c #{Shellwords.escape(inner)}"
44
+
45
+ PWN::SDR::Decoder::Base.run_pipeline(
46
+ freq_obj: freq_obj,
47
+ protocol: 'ZigBee',
48
+ required_bins: %w[whsniff tshark],
49
+ direct_cmd: direct_cmd,
50
+ line_match: /\S/,
51
+ parser: proc { |line| parse_line(line: line) }
52
+ )
53
+ end
54
+
55
+ # Supported Method Parameters::
56
+ # PWN::SDR::Decoder::ZigBee.parse_line(line: 't|src16|dst16|src64|dst64|pan|type|cmd|seq')
57
+
58
+ public_class_method def self.parse_line(opts = {})
59
+ f = opts[:line].to_s.split('|', -1)
60
+ out = {
61
+ protocol: 'ZigBee',
62
+ src16: f[1], dst16: f[2],
63
+ src64: f[3], dst64: f[4],
64
+ pan_id: f[5], frame_type: f[6],
65
+ nwk_cmd: f[7], seq: f[8]
66
+ }.reject { |_, v| v.to_s.empty? }
67
+ out[:summary] = "ZigBee PAN=#{out[:pan_id]} #{out[:src16] || out[:src64]}→#{out[:dst16] || out[:dst64]} type=#{out[:frame_type]}"
68
+ out
69
+ end
70
+
71
+ # Author(s):: 0day Inc. <support@0dayinc.com>
72
+
73
+ public_class_method def self.authors
74
+ "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n"
75
+ end
76
+
77
+ # Display Usage for this Module
78
+
79
+ public_class_method def self.help
80
+ puts "USAGE:
81
+ #{self}.decode(
82
+ freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq'
83
+ )
84
+
85
+ NOTE: Requires `whsniff` (CC2531) or KillerBee, plus `tshark`.
86
+ Channel is derived from freq_obj[:freq] (11–26).
87
+
88
+ #{self}.parse_line(line: '0.1|0x0001|0xffff||||0x01||42')
89
+
90
+ #{self}.authors
91
+ "
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -5,15 +5,73 @@ module PWN
5
5
  # into memory only when they're needed. For more information, see:
6
6
  # http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
7
7
  module SDR
8
- # Deocder Module for SDR signals.
8
+ # Decoder namespace for SDR signals. Every child module exposes a
9
+ # uniform `.decode(freq_obj:)` entry point (see PWN::SDR::Decoder::Base)
10
+ # so PWN::SDR::GQRX can dispatch on a `decoder:` key from
11
+ # PWN::SDR::FrequencyAllocation.band_plans.
9
12
  module Decoder
10
- autoload :Base, 'pwn/sdr/decoder/base'
11
- autoload :Flex, 'pwn/sdr/decoder/flex'
12
- autoload :GSM, 'pwn/sdr/decoder/gsm'
13
- autoload :POCSAG, 'pwn/sdr/decoder/pocsag'
14
- autoload :RDS, 'pwn/sdr/decoder/rds'
13
+ autoload :ADSB, 'pwn/sdr/decoder/adsb'
14
+ autoload :APT, 'pwn/sdr/decoder/apt'
15
+ autoload :Base, 'pwn/sdr/decoder/base'
16
+ autoload :Bluetooth, 'pwn/sdr/decoder/bluetooth'
17
+ autoload :DECT, 'pwn/sdr/decoder/dect'
18
+ autoload :Flex, 'pwn/sdr/decoder/flex'
19
+ autoload :GPS, 'pwn/sdr/decoder/gps'
20
+ autoload :GSM, 'pwn/sdr/decoder/gsm'
21
+ autoload :Iridium, 'pwn/sdr/decoder/iridium'
22
+ autoload :LTE, 'pwn/sdr/decoder/lte'
23
+ autoload :LoRa, 'pwn/sdr/decoder/lora'
24
+ autoload :Morse, 'pwn/sdr/decoder/morse'
25
+ autoload :P25, 'pwn/sdr/decoder/p25'
26
+ autoload :POCSAG, 'pwn/sdr/decoder/pocsag'
27
+ autoload :Pager, 'pwn/sdr/decoder/pager'
28
+ autoload :RDS, 'pwn/sdr/decoder/rds'
29
+ autoload :RFID, 'pwn/sdr/decoder/rfid'
30
+ autoload :RTL433, 'pwn/sdr/decoder/rtl433'
31
+ autoload :RTTY, 'pwn/sdr/decoder/rtty'
32
+ autoload :WiFi, 'pwn/sdr/decoder/wifi'
33
+ autoload :ZigBee, 'pwn/sdr/decoder/zigbee'
15
34
 
16
- # Display a List of Every PWN::AI Module
35
+ # symbol → module map. Keys are what a band_plan's :decoder value (or
36
+ # PWN::SDR::GQRX.init_freq's decoder: kwarg) may be set to.
37
+ REGISTRY = {
38
+ adsb: :ADSB,
39
+ apt: :APT,
40
+ bluetooth: :Bluetooth,
41
+ dect: :DECT,
42
+ flex: :Flex,
43
+ gprs: :GSM,
44
+ gps: :GPS,
45
+ gsm: :GSM,
46
+ iridium: :Iridium,
47
+ ism: :RTL433,
48
+ keyfob: :RTL433,
49
+ lora: :LoRa,
50
+ lte: :LTE,
51
+ morse: :Morse,
52
+ p25: :P25,
53
+ pager: :Pager,
54
+ pocsag: :POCSAG,
55
+ rds: :RDS,
56
+ rfid: :RFID,
57
+ rtl433: :RTL433,
58
+ rtty: :RTTY,
59
+ wifi: :WiFi,
60
+ zigbee: :ZigBee
61
+ }.freeze
62
+
63
+ # Supported Method Parameters::
64
+ # mod = PWN::SDR::Decoder.resolve(
65
+ # decoder: 'required - Symbol/String key from REGISTRY (e.g. :pocsag)'
66
+ # )
67
+
68
+ public_class_method def self.resolve(opts = {})
69
+ key = opts[:decoder].to_s.downcase.to_sym
70
+ const = REGISTRY[key]
71
+ raise "ERROR: Unknown decoder key #{key.inspect}. Supported: #{REGISTRY.keys.sort.join(', ')}" unless const
72
+
73
+ const_get(const)
74
+ end
17
75
 
18
76
  # Author(s):: 0day Inc. <support@0dayinc.com>
19
77
 
@@ -21,6 +79,8 @@ module PWN
21
79
  "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n"
22
80
  end
23
81
 
82
+ # Display a List of Every PWN::SDR::Decoder Module
83
+
24
84
  public_class_method def self.help
25
85
  constants.sort
26
86
  end