pwn 0.5.505 → 0.5.507
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 +1 -1
- data/Gemfile +2 -2
- data/README.md +3 -3
- data/bin/pwn_gqrx_scanner +16 -16
- data/bin/pwn_serial_son_micro_sm132_rfid +11 -11
- data/lib/pwn/ai.rb +1 -1
- data/lib/pwn/aws.rb +1 -1
- data/lib/pwn/banner.rb +1 -1
- data/lib/pwn/blockchain.rb +1 -1
- data/lib/pwn/ffi.rb +1 -1
- data/lib/pwn/plugins/burp_suite.rb +38 -37
- data/lib/pwn/plugins.rb +1 -7
- data/lib/pwn/reports.rb +1 -1
- data/lib/pwn/sast.rb +2 -2
- data/lib/pwn/sdr/decoder/gsm.rb +200 -0
- data/lib/pwn/sdr/decoder.rb +19 -0
- data/lib/pwn/{plugins → sdr}/flipper_zero.rb +5 -5
- data/lib/pwn/sdr/frequency_allocation.rb +372 -0
- data/lib/pwn/sdr/gqrx.rb +656 -0
- data/lib/pwn/{plugins → sdr}/rfidler.rb +2 -2
- data/lib/pwn/{plugins → sdr}/son_micro_rfid.rb +12 -12
- data/lib/pwn/sdr.rb +21 -0
- data/lib/pwn/version.rb +1 -1
- data/lib/pwn/www.rb +1 -1
- data/lib/pwn.rb +1 -0
- data/spec/lib/pwn/sdr/decoder/gsm_spec.rb +15 -0
- data/spec/lib/pwn/sdr/decoder_spec.rb +10 -0
- data/spec/lib/pwn/{plugins → sdr}/flipper_zero_spec.rb +3 -3
- data/spec/lib/pwn/sdr/frequency_allocation_spec.rb +15 -0
- data/spec/lib/pwn/{plugins → sdr}/gqrx_spec.rb +3 -3
- data/spec/lib/pwn/{plugins → sdr}/rfidler_spec.rb +3 -3
- data/spec/lib/pwn/{plugins → sdr}/son_micro_rfid_spec.rb +3 -3
- data/spec/lib/pwn/sdr_spec.rb +10 -0
- data/third_party/pwn_rdoc.jsonl +3 -1
- metadata +21 -13
- data/lib/pwn/plugins/gqrx.rb +0 -757
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PWN
|
|
4
|
+
module SDR
|
|
5
|
+
module Decoder
|
|
6
|
+
# SDR Decoder for GSM signals.
|
|
7
|
+
module GSM
|
|
8
|
+
POWER_THRESHOLD = 0.1
|
|
9
|
+
SLEEP_INTERVAL = 0.1
|
|
10
|
+
HEADER_SIZE = 44
|
|
11
|
+
BURST_DURATION_SEC = 0.000577
|
|
12
|
+
|
|
13
|
+
# TSC 0 binary sequence (26 bits): 00100101110000101010011011
|
|
14
|
+
TSC_0 = [0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1].freeze
|
|
15
|
+
|
|
16
|
+
# Starts the live decoding thread.
|
|
17
|
+
def self.start(opts = {})
|
|
18
|
+
gqrx_obj = opts[:gqrx_obj]
|
|
19
|
+
raise ':ERROR: :gqrx_obj is required' unless gqrx_obj.is_a?(Hash)
|
|
20
|
+
|
|
21
|
+
record_path = gqrx_obj[:record_path]
|
|
22
|
+
frequency = gqrx_obj[:frequency]
|
|
23
|
+
sample_rate = gqrx_obj[:bandwidth].to_i
|
|
24
|
+
gqrx_sock = gqrx_obj[:gqrx_sock]
|
|
25
|
+
|
|
26
|
+
gqrx_obj[:decoder_stop_flag] ||= [false]
|
|
27
|
+
|
|
28
|
+
sleep 0.1 until File.exist?(record_path)
|
|
29
|
+
|
|
30
|
+
header = File.binread(record_path, HEADER_SIZE)
|
|
31
|
+
raise 'Invalid WAV header' unless header.start_with?('RIFF') && header.include?('WAVE')
|
|
32
|
+
|
|
33
|
+
bytes_read = HEADER_SIZE
|
|
34
|
+
|
|
35
|
+
puts "GSM Decoder started for frequency: #{frequency}, sample_rate: #{sample_rate}"
|
|
36
|
+
|
|
37
|
+
Thread.new do
|
|
38
|
+
loop do
|
|
39
|
+
break if gqrx_obj[:decoder_stop_flag][0]
|
|
40
|
+
|
|
41
|
+
current_size = File.size(record_path)
|
|
42
|
+
if current_size > bytes_read
|
|
43
|
+
new_bytes = current_size - bytes_read
|
|
44
|
+
# Ensure full I/Q pairs (8 bytes)
|
|
45
|
+
new_bytes -= new_bytes % 8
|
|
46
|
+
data = File.binread(record_path, new_bytes, bytes_read)
|
|
47
|
+
process_chunk(
|
|
48
|
+
data: data,
|
|
49
|
+
sample_rate: sample_rate,
|
|
50
|
+
frequency: frequency
|
|
51
|
+
)
|
|
52
|
+
bytes_read = current_size
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
sleep SLEEP_INTERVAL
|
|
56
|
+
end
|
|
57
|
+
rescue StandardError => e
|
|
58
|
+
puts "Decoder error: #{e.message}"
|
|
59
|
+
ensure
|
|
60
|
+
cleanup(record_path: record_path)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Stops the decoding thread.
|
|
65
|
+
def self.stop(opts = {})
|
|
66
|
+
thread = opts[:thread]
|
|
67
|
+
gqrx_obj = opts[:gqrx_obj]
|
|
68
|
+
raise ':ERROR: :thread and :gqrx_obj are required' unless thread && gqrx_obj.is_a?(Hash)
|
|
69
|
+
|
|
70
|
+
gqrx_obj[:decoder_stop_flag][0] = true
|
|
71
|
+
thread.join(1.0)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class << self
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def process_chunk(opts = {})
|
|
78
|
+
data = opts[:data]
|
|
79
|
+
sample_rate = opts[:sample_rate]
|
|
80
|
+
frequency = opts[:frequency]
|
|
81
|
+
raise ':ERROR: :data, :sample_rate, and :frequency are required' unless data && sample_rate && frequency
|
|
82
|
+
|
|
83
|
+
samples = data.unpack('f< *')
|
|
84
|
+
return if samples.length.odd? # Skip incomplete
|
|
85
|
+
|
|
86
|
+
complex_samples = []
|
|
87
|
+
(0...samples.length).step(2) do |i|
|
|
88
|
+
complex_samples << Complex(samples[i], samples[i + 1])
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
window_size = [(sample_rate * BURST_DURATION_SEC).round, complex_samples.length].min
|
|
92
|
+
return if window_size <= 0
|
|
93
|
+
|
|
94
|
+
# Simplified power on sliding windows
|
|
95
|
+
powers = []
|
|
96
|
+
complex_samples.each_cons(window_size) do |window|
|
|
97
|
+
power = window.map { |c| c.abs**2 }.sum / window_size
|
|
98
|
+
powers << power
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
max_power = powers.max
|
|
102
|
+
return unless max_power > POWER_THRESHOLD
|
|
103
|
+
|
|
104
|
+
# Demod the entire chunk (assume burst-aligned roughly)
|
|
105
|
+
bits = demod_gmsk(complex_samples)
|
|
106
|
+
# Synchronize via TSC correlation
|
|
107
|
+
sync_offset = find_tsc_offset(bits, TSC_0)
|
|
108
|
+
return unless sync_offset >= 0
|
|
109
|
+
|
|
110
|
+
# Extract data bits from normal burst structure
|
|
111
|
+
burst_start = sync_offset - 58 # TSC starts at symbol 58 (0-index)
|
|
112
|
+
return unless burst_start >= 0 && burst_start + 148 <= bits.length
|
|
113
|
+
|
|
114
|
+
data_bits = extract_data_bits(bits, burst_start)
|
|
115
|
+
puts "Burst synchronized at offset #{sync_offset} for #{frequency} Hz (power: #{max_power.round(4)})"
|
|
116
|
+
decode_imsi(
|
|
117
|
+
data_bits: data_bits,
|
|
118
|
+
frequency: frequency
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def demod_gmsk(complex_samples)
|
|
123
|
+
return [] if complex_samples.length < 2
|
|
124
|
+
|
|
125
|
+
bits = []
|
|
126
|
+
(1...complex_samples.length).each do |i|
|
|
127
|
+
prod = complex_samples[i] * complex_samples[i - 1].conj
|
|
128
|
+
# Sign of imaginary part for quadrature differential
|
|
129
|
+
bit = (prod.imag >= 0 ? 0 : 1) # Or adjust polarity
|
|
130
|
+
bits << bit
|
|
131
|
+
end
|
|
132
|
+
bits
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def find_tsc_offset(bits, tsc)
|
|
136
|
+
max_corr = -1
|
|
137
|
+
best_offset = -1
|
|
138
|
+
tsc_length = tsc.length # 26
|
|
139
|
+
(0...(bits.length - tsc_length + 1)).each do |offset|
|
|
140
|
+
window = bits[offset, tsc_length]
|
|
141
|
+
corr = window.zip(tsc).count { |b1, b2| b1 == b2 }
|
|
142
|
+
if corr > max_corr
|
|
143
|
+
max_corr = corr
|
|
144
|
+
best_offset = offset
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
# Threshold: e.g., >20 matches for good sync
|
|
148
|
+
max_corr > 20 ? best_offset : -1
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Extract 114 data bits from normal burst (ignoring tails/guard)
|
|
152
|
+
def extract_data_bits(bits, burst_start)
|
|
153
|
+
data1_start = burst_start + 2
|
|
154
|
+
data2_start = burst_start + 88 # After TSC 26 + data1 57 = 85, +3? Wait, structure: tail2(0-1), data(2-58), tsc(59-84), data(85-141), tail(142-143)
|
|
155
|
+
data1 = bits[data1_start, 57]
|
|
156
|
+
data2 = bits[data2_start, 57]
|
|
157
|
+
data1 + data2
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def decode_imsi(opts = {})
|
|
161
|
+
data_bits = opts[:data_bits]
|
|
162
|
+
frequency = opts[:frequency]
|
|
163
|
+
raise ':ERROR: :data_bits and :frequency are required' unless data_bits && frequency
|
|
164
|
+
|
|
165
|
+
# Simplified "IMSI extraction": Interpret first ~60 bits as packed digits (4 bits per digit, BCD-like).
|
|
166
|
+
# In reality: Deinterleave (over bursts), Viterbi decode convolutional code (polys G0=10011b, G1=11011b),
|
|
167
|
+
# CRC check, parse L3 message (e.g., Paging Req Type 1 has IMSI IE at specific offset, packed BCD).
|
|
168
|
+
# Here: Raw data bits to 15-digit IMSI (first 60 bits -> 15 nibbles).
|
|
169
|
+
return unless data_bits.length >= 60
|
|
170
|
+
|
|
171
|
+
imsi_digits = []
|
|
172
|
+
data_bits[0, 60].each_slice(4) do |nibble|
|
|
173
|
+
digit = nibble.join.to_i(2)
|
|
174
|
+
imsi_digits << (digit % 10) # Mod 10 for digit-like, or keep as is for hex
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Format as 3(MCC)+3(MNC)+9(MSIN)
|
|
178
|
+
mcc = imsi_digits[0, 3].join
|
|
179
|
+
mnc = imsi_digits[3, 3].join
|
|
180
|
+
msin = imsi_digits[6, 9].join
|
|
181
|
+
imsi = "#{mcc.ljust(3, '0')}#{mnc.ljust(3, '0')}#{msin.ljust(9, '0')}"
|
|
182
|
+
|
|
183
|
+
puts "Decoded IMSI: #{imsi} at #{frequency} Hz"
|
|
184
|
+
# TODO: Integrate full L3 parser (e.g., from ruby-gsm gem or custom).
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def cleanup(opts = {})
|
|
188
|
+
record_path = opts[:record_path]
|
|
189
|
+
raise ':ERROR: :record_path is required' unless record_path
|
|
190
|
+
|
|
191
|
+
return unless File.exist?(record_path)
|
|
192
|
+
|
|
193
|
+
File.delete(record_path)
|
|
194
|
+
puts "Cleaned up recording: #{record_path}"
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PWN
|
|
4
|
+
# This file, using the autoload directive loads SDR modules
|
|
5
|
+
# into memory only when they're needed. For more information, see:
|
|
6
|
+
# http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
|
|
7
|
+
module SDR
|
|
8
|
+
# Deocder Module for SDR signals.
|
|
9
|
+
module Decoder
|
|
10
|
+
autoload :GSM, 'pwn/sdr/decoder/gsm'
|
|
11
|
+
|
|
12
|
+
# Display a List of Every PWN::AI Module
|
|
13
|
+
|
|
14
|
+
public_class_method def self.help
|
|
15
|
+
constants.sort
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PWN
|
|
4
|
-
module
|
|
4
|
+
module SDR
|
|
5
5
|
# This plugin is used for interacting with Bus Pirate v3.6
|
|
6
6
|
# This plugin may be compatible with other versions, however,
|
|
7
7
|
# has not been tested with anything other than v3.6.
|
|
8
8
|
module FlipperZero
|
|
9
9
|
# Supported Method Parameters::
|
|
10
|
-
# PWN::
|
|
10
|
+
# PWN::SDR::FlipperZero.connect_via_screen(
|
|
11
11
|
# block_dev: 'optional - serial block device path (defaults to /dev/ttyACM0)'
|
|
12
12
|
# )
|
|
13
13
|
|
|
@@ -34,7 +34,7 @@ module PWN
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Supported Method Parameters::
|
|
37
|
-
# flipper_zero_obj = PWN::
|
|
37
|
+
# flipper_zero_obj = PWN::SDR::FlipperZero.connect(
|
|
38
38
|
# block_dev: 'optional serial block device path (defaults to /dev/ttyACM0)',
|
|
39
39
|
# baud: 'optional (defaults to 9600)',
|
|
40
40
|
# data_bits: 'optional (defaults to 8)',
|
|
@@ -50,7 +50,7 @@ module PWN
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# Supported Method Parameters::
|
|
53
|
-
# response = PWN::
|
|
53
|
+
# response = PWN::SDR::FlipperZero.request(
|
|
54
54
|
# flipper_zero_obj: 'required - flipper_zero_obj returned from #connect method',
|
|
55
55
|
# payload: 'optional - payload to send to the device (defaults to help)'
|
|
56
56
|
# )
|
|
@@ -75,7 +75,7 @@ module PWN
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
# Supported Method Parameters::
|
|
78
|
-
# PWN::
|
|
78
|
+
# PWN::SDR::FlipperZero.disconnect(
|
|
79
79
|
# flipper_zero_obj: 'required - flipper_zero_obj returned from #connect method'
|
|
80
80
|
# )
|
|
81
81
|
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open3'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
|
|
7
|
+
module PWN
|
|
8
|
+
module SDR
|
|
9
|
+
# This plugin interacts with the remote control interface of GQRX.
|
|
10
|
+
module FrequencyAllocation
|
|
11
|
+
# Supported Method Parameters::
|
|
12
|
+
# profiles = PWN::SDR::FrequencyAllocation.profiles
|
|
13
|
+
# Supported Method Parameters::
|
|
14
|
+
# profiles = PWN::SDR::FrequencyAllocation.profiles
|
|
15
|
+
public_class_method def self.profiles
|
|
16
|
+
# TODO: Wifi5 / Wifi6 profiles,
|
|
17
|
+
# migrate to a YAML file, and add
|
|
18
|
+
# rSpec test to ensure all profiles
|
|
19
|
+
# contain consistent key-value pairs
|
|
20
|
+
{
|
|
21
|
+
ads_b978: {
|
|
22
|
+
start_freq: '978.000.000',
|
|
23
|
+
target_freq: '979.000.000',
|
|
24
|
+
demodulator_mode: 'RAW',
|
|
25
|
+
bandwidth: 100_000,
|
|
26
|
+
precision: 5
|
|
27
|
+
},
|
|
28
|
+
ads_b1090: {
|
|
29
|
+
start_freq: '1.090.000.000',
|
|
30
|
+
target_freq: '1.091.000.000',
|
|
31
|
+
demodulator_mode: 'RAW',
|
|
32
|
+
bandwidth: 100_000,
|
|
33
|
+
precision: 5
|
|
34
|
+
},
|
|
35
|
+
analog_tv_vhf: {
|
|
36
|
+
start_freq: '54.000.000',
|
|
37
|
+
target_freq: '216.000.000',
|
|
38
|
+
demodulator_mode: 'WFM',
|
|
39
|
+
bandwidth: 600_000,
|
|
40
|
+
precision: 5
|
|
41
|
+
},
|
|
42
|
+
analog_tv_uhf: {
|
|
43
|
+
start_freq: '470.000.000',
|
|
44
|
+
target_freq: '890.000.000',
|
|
45
|
+
demodulator_mode: 'WFM',
|
|
46
|
+
bandwidth: 600_000,
|
|
47
|
+
precision: 5
|
|
48
|
+
},
|
|
49
|
+
am_radio: {
|
|
50
|
+
start_freq: '540.000',
|
|
51
|
+
target_freq: '1.700.000',
|
|
52
|
+
demodulator_mode: 'AM',
|
|
53
|
+
bandwidth: 10_000,
|
|
54
|
+
precision: 4
|
|
55
|
+
},
|
|
56
|
+
bluetooth: {
|
|
57
|
+
start_freq: '2.402.000.000',
|
|
58
|
+
target_freq: '2.480.000.000',
|
|
59
|
+
demodulator_mode: 'RAW',
|
|
60
|
+
bandwidth: 100_000,
|
|
61
|
+
precision: 5
|
|
62
|
+
},
|
|
63
|
+
cdma: {
|
|
64
|
+
start_freq: '824.000.000',
|
|
65
|
+
target_freq: '849.000.000',
|
|
66
|
+
demodulator_mode: 'RAW',
|
|
67
|
+
bandwidth: 125_000,
|
|
68
|
+
precision: 6
|
|
69
|
+
},
|
|
70
|
+
cw20: {
|
|
71
|
+
start_freq: '14.000.000',
|
|
72
|
+
target_freq: '14.350.000',
|
|
73
|
+
demodulator_mode: 'CW',
|
|
74
|
+
bandwidth: 150,
|
|
75
|
+
precision: 3
|
|
76
|
+
},
|
|
77
|
+
cw40: {
|
|
78
|
+
start_freq: '7.000.000',
|
|
79
|
+
target_freq: '7.300.000',
|
|
80
|
+
demodulator_mode: 'CW',
|
|
81
|
+
bandwidth: 150,
|
|
82
|
+
precision: 3
|
|
83
|
+
},
|
|
84
|
+
cw80: {
|
|
85
|
+
start_freq: '3.500.000',
|
|
86
|
+
target_freq: '3.800.000',
|
|
87
|
+
demodulator_mode: 'CW',
|
|
88
|
+
bandwidth: 150,
|
|
89
|
+
precision: 3
|
|
90
|
+
},
|
|
91
|
+
fm_radio: {
|
|
92
|
+
start_freq: '87.900.000',
|
|
93
|
+
target_freq: '108.000.000',
|
|
94
|
+
demodulator_mode: 'WFM',
|
|
95
|
+
bandwidth: 200_000,
|
|
96
|
+
precision: 6
|
|
97
|
+
},
|
|
98
|
+
frs: {
|
|
99
|
+
start_freq: '462.562.500',
|
|
100
|
+
target_freq: '467.725.000',
|
|
101
|
+
demodulator_mode: 'FM',
|
|
102
|
+
bandwidth: 200_000,
|
|
103
|
+
precision: 3
|
|
104
|
+
},
|
|
105
|
+
gmrs: {
|
|
106
|
+
start_freq: '462.550.000',
|
|
107
|
+
target_freq: '467.725.000',
|
|
108
|
+
demodulator_mode: 'FM',
|
|
109
|
+
bandwidth: 200_000,
|
|
110
|
+
precision: 3
|
|
111
|
+
},
|
|
112
|
+
gprs: {
|
|
113
|
+
start_freq: '880.000.000',
|
|
114
|
+
target_freq: '915.000.000',
|
|
115
|
+
demodulator_mode: 'RAW',
|
|
116
|
+
bandwidth: 200_000,
|
|
117
|
+
precision: 4
|
|
118
|
+
},
|
|
119
|
+
gps_l1: {
|
|
120
|
+
start_freq: '1.574.420.000',
|
|
121
|
+
target_freq: '1.576.420.000',
|
|
122
|
+
demodulator_mode: 'RAW',
|
|
123
|
+
bandwidth: 200_000,
|
|
124
|
+
precision: 6
|
|
125
|
+
},
|
|
126
|
+
gps_l2: {
|
|
127
|
+
start_freq: '1.226.600.000',
|
|
128
|
+
target_freq: '1.228.600.000',
|
|
129
|
+
demodulator_mode: 'RAW',
|
|
130
|
+
bandwidth: 200_000,
|
|
131
|
+
precision: 6
|
|
132
|
+
},
|
|
133
|
+
gsm: {
|
|
134
|
+
start_freq: '824.000.000',
|
|
135
|
+
target_freq: '894.000.000',
|
|
136
|
+
demodulator_mode: 'RAW',
|
|
137
|
+
bandwidth: 200_000,
|
|
138
|
+
precision: 4
|
|
139
|
+
},
|
|
140
|
+
high_rfid: {
|
|
141
|
+
start_freq: '13.560.000',
|
|
142
|
+
target_freq: '13.570.000',
|
|
143
|
+
demodulator_mode: 'RAW',
|
|
144
|
+
bandwidth: 200_000,
|
|
145
|
+
precision: 3
|
|
146
|
+
},
|
|
147
|
+
lora433: {
|
|
148
|
+
start_freq: '432.000.000',
|
|
149
|
+
target_freq: '434.000.000',
|
|
150
|
+
demodulator_mode: 'RAW',
|
|
151
|
+
bandwidth: 50_000,
|
|
152
|
+
precision: 3
|
|
153
|
+
},
|
|
154
|
+
lora915: {
|
|
155
|
+
start_freq: '902.000.000',
|
|
156
|
+
target_freq: '928.000.000',
|
|
157
|
+
demodulator_mode: 'RAW',
|
|
158
|
+
bandwidth: 50_000,
|
|
159
|
+
precision: 3
|
|
160
|
+
},
|
|
161
|
+
low_rfid: {
|
|
162
|
+
start_freq: '125.000',
|
|
163
|
+
target_freq: '134.000',
|
|
164
|
+
demodulator_mode: 'RAW',
|
|
165
|
+
bandwidth: 200_000,
|
|
166
|
+
precision: 1
|
|
167
|
+
},
|
|
168
|
+
keyfob300: {
|
|
169
|
+
start_freq: '300.000.000',
|
|
170
|
+
target_freq: '300.100.000',
|
|
171
|
+
demodulator_mode: 'RAW',
|
|
172
|
+
bandwidth: 50_000,
|
|
173
|
+
precision: 4
|
|
174
|
+
},
|
|
175
|
+
keyfob310: {
|
|
176
|
+
start_freq: '310.000.000',
|
|
177
|
+
target_freq: '310.100.000',
|
|
178
|
+
demodulator_mode: 'RAW',
|
|
179
|
+
bandwidth: 50_000,
|
|
180
|
+
precision: 4
|
|
181
|
+
},
|
|
182
|
+
keyfob315: {
|
|
183
|
+
start_freq: '315.000.000',
|
|
184
|
+
target_freq: '315.100.000',
|
|
185
|
+
demodulator_mode: 'RAW',
|
|
186
|
+
bandwidth: 50_000,
|
|
187
|
+
precision: 4
|
|
188
|
+
},
|
|
189
|
+
keyfob390: {
|
|
190
|
+
start_freq: '390.000.000',
|
|
191
|
+
target_freq: '390.100.000',
|
|
192
|
+
demodulator_mode: 'RAW',
|
|
193
|
+
bandwidth: 50_000,
|
|
194
|
+
precision: 4
|
|
195
|
+
},
|
|
196
|
+
keyfob433: {
|
|
197
|
+
start_freq: '433.000.000',
|
|
198
|
+
target_freq: '434.000.000',
|
|
199
|
+
demodulator_mode: 'RAW',
|
|
200
|
+
bandwidth: 50_000,
|
|
201
|
+
precision: 4
|
|
202
|
+
},
|
|
203
|
+
keyfob868: {
|
|
204
|
+
start_freq: '868.000.000',
|
|
205
|
+
target_freq: '869.000.000',
|
|
206
|
+
demodulator_mode: 'RAW',
|
|
207
|
+
bandwidth: 50_000,
|
|
208
|
+
precision: 4
|
|
209
|
+
},
|
|
210
|
+
rtty20: {
|
|
211
|
+
start_freq: '14.000.000',
|
|
212
|
+
target_freq: '14.350.000',
|
|
213
|
+
demodulator_mode: 'RTTY',
|
|
214
|
+
bandwidth: 170,
|
|
215
|
+
precision: 3
|
|
216
|
+
},
|
|
217
|
+
rtty40: {
|
|
218
|
+
start_freq: '7.000.000',
|
|
219
|
+
target_freq: '7.300.000',
|
|
220
|
+
demodulator_mode: 'RTTY',
|
|
221
|
+
bandwidth: 170,
|
|
222
|
+
precision: 3
|
|
223
|
+
},
|
|
224
|
+
rtty80: {
|
|
225
|
+
start_freq: '3.500.000',
|
|
226
|
+
target_freq: '3.800.000',
|
|
227
|
+
demodulator_mode: 'RTTY',
|
|
228
|
+
bandwidth: 170,
|
|
229
|
+
precision: 3
|
|
230
|
+
},
|
|
231
|
+
ssb10: {
|
|
232
|
+
start_freq: '28.000.000',
|
|
233
|
+
target_freq: '29.700.000',
|
|
234
|
+
demodulator_mode: 'USB',
|
|
235
|
+
bandwidth: 2_700,
|
|
236
|
+
precision: 6
|
|
237
|
+
},
|
|
238
|
+
ssb12: {
|
|
239
|
+
start_freq: '24.890.000',
|
|
240
|
+
target_freq: '24.990.000',
|
|
241
|
+
demodulator_mode: 'USB',
|
|
242
|
+
bandwidth: 2_700,
|
|
243
|
+
precision: 6
|
|
244
|
+
},
|
|
245
|
+
ssb15: {
|
|
246
|
+
start_freq: '21.000.000',
|
|
247
|
+
target_freq: '21.450.000',
|
|
248
|
+
demodulator_mode: 'USB',
|
|
249
|
+
bandwidth: 2_700,
|
|
250
|
+
precision: 6
|
|
251
|
+
},
|
|
252
|
+
ssb17: {
|
|
253
|
+
start_freq: '18.068.000',
|
|
254
|
+
target_freq: '18.168.000',
|
|
255
|
+
demodulator_mode: 'USB',
|
|
256
|
+
bandwidth: 2_700,
|
|
257
|
+
precision: 6
|
|
258
|
+
},
|
|
259
|
+
ssb20: {
|
|
260
|
+
start_freq: '14.000.000',
|
|
261
|
+
target_freq: '14.350.000',
|
|
262
|
+
demodulator_mode: 'USB',
|
|
263
|
+
bandwidth: 2_700,
|
|
264
|
+
precision: 6
|
|
265
|
+
},
|
|
266
|
+
ssb40: {
|
|
267
|
+
start_freq: '7.000.000',
|
|
268
|
+
target_freq: '7.300.000',
|
|
269
|
+
demodulator_mode: 'LSB',
|
|
270
|
+
bandwidth: 2_700,
|
|
271
|
+
precision: 6
|
|
272
|
+
},
|
|
273
|
+
ssb80: {
|
|
274
|
+
start_freq: '3.500.000',
|
|
275
|
+
target_freq: '3.800.000',
|
|
276
|
+
demodulator_mode: 'LSB',
|
|
277
|
+
bandwidth: 2_700,
|
|
278
|
+
precision: 6
|
|
279
|
+
},
|
|
280
|
+
ssb160: {
|
|
281
|
+
start_freq: '1.800.000',
|
|
282
|
+
target_freq: '2.000.000',
|
|
283
|
+
demodulator_mode: 'LSB',
|
|
284
|
+
bandwidth: 2_700,
|
|
285
|
+
precision: 6
|
|
286
|
+
},
|
|
287
|
+
tempest: {
|
|
288
|
+
start_freq: '400.000.000',
|
|
289
|
+
target_freq: '430.000.000',
|
|
290
|
+
demodulator_mode: 'WFM',
|
|
291
|
+
bandwidth: 200_000,
|
|
292
|
+
precision: 4
|
|
293
|
+
},
|
|
294
|
+
uhf_rfid: {
|
|
295
|
+
start_freq: '860.000.000',
|
|
296
|
+
target_freq: '960.000.000',
|
|
297
|
+
demodulator_mode: 'RAW',
|
|
298
|
+
bandwidth: 100_000,
|
|
299
|
+
precision: 5
|
|
300
|
+
},
|
|
301
|
+
wifi24: {
|
|
302
|
+
start_freq: '2.400.000.000',
|
|
303
|
+
target_freq: '2.500.000.000',
|
|
304
|
+
demodulator_mode: 'RAW',
|
|
305
|
+
bandwidth: 200_000,
|
|
306
|
+
precision: 7
|
|
307
|
+
},
|
|
308
|
+
wifi5: {
|
|
309
|
+
start_freq: '5.150.000.000',
|
|
310
|
+
target_freq: '5.850.000.000',
|
|
311
|
+
demodulator_mode: 'RAW',
|
|
312
|
+
bandwidth: 200_000,
|
|
313
|
+
precision: 7
|
|
314
|
+
},
|
|
315
|
+
wifi6: {
|
|
316
|
+
start_freq: '5.925.000.000',
|
|
317
|
+
target_freq: '7.125.000.000',
|
|
318
|
+
demodulator_mode: 'RAW',
|
|
319
|
+
bandwidth: 200_000,
|
|
320
|
+
precision: 7
|
|
321
|
+
},
|
|
322
|
+
zigbee: {
|
|
323
|
+
start_freq: '2.405.000.000',
|
|
324
|
+
target_freq: '2.485.000.000',
|
|
325
|
+
demodulator_mode: 'RAW',
|
|
326
|
+
bandwidth: 200_000,
|
|
327
|
+
precision: 7
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
rescue StandardError => e
|
|
331
|
+
raise e
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Supported Method Parameters::
|
|
335
|
+
# opts = PWN::SDR::FrequencyAllocation.load(
|
|
336
|
+
# profile: 'required - valid FrequencyAllocation profile name returned from #profiles method'
|
|
337
|
+
# )
|
|
338
|
+
public_class_method def self.load(opts = {})
|
|
339
|
+
profile = opts[:profile]&.to_sym
|
|
340
|
+
|
|
341
|
+
profiles_available = profiles
|
|
342
|
+
raise "ERROR: Invalid profile: #{profile}" unless profiles_available.key?(profile)
|
|
343
|
+
|
|
344
|
+
profiles_available[profile]
|
|
345
|
+
rescue StandardError => e
|
|
346
|
+
raise e
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Author(s):: 0day Inc. <support@0dayinc.com>
|
|
350
|
+
|
|
351
|
+
public_class_method def self.authors
|
|
352
|
+
"AUTHOR(S):
|
|
353
|
+
0day Inc. <support@0dayinc.com>
|
|
354
|
+
"
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# Display Usage for this Module
|
|
358
|
+
|
|
359
|
+
public_class_method def self.help
|
|
360
|
+
puts "USAGE:
|
|
361
|
+
profiles = #{self}.profiles
|
|
362
|
+
|
|
363
|
+
opts = #{self}.load(
|
|
364
|
+
profile: 'required - valid frequency allocation profile name returned from #profiles method'
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
#{self}.authors
|
|
368
|
+
"
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|