pwn 0.5.518 → 0.5.520
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/Gemfile +1 -1
- data/README.md +3 -3
- data/bin/pwn_gqrx_scanner +2 -6
- data/lib/pwn/sdr/decoder/rds.rb +52 -74
- data/lib/pwn/sdr/frequency_allocation.rb +2 -2
- data/lib/pwn/sdr/gqrx.rb +63 -67
- data/lib/pwn/version.rb +1 -1
- data/third_party/pwn_rdoc.jsonl +11 -6
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6d0dc923ff782c8b1fcd7ad1ec99996f9d2a07770e6c877c217104f889b5c1c
|
|
4
|
+
data.tar.gz: 700f54af4422a4e36080057cfeb221dc51f78e9bb66f0b2f6041ab4878e6c19e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c65607836b13bdd1c45fd0d2e845855cba2dd8d75180026f71238c2e9ae8a74bca150adf988b641dadbb156772cb1db96cb1e5fc9c9bee3319c3b08279fc6cd1
|
|
7
|
+
data.tar.gz: 54146ccf00a69c62116c448f9c143a7e1d14192cf055e41bd5fb272238e8242f734ae57fc9cc37e54798c4bcdc6986eb51e5c11fc9d6feef83345bd064c3cf5d
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -37,7 +37,7 @@ $ cd /opt/pwn
|
|
|
37
37
|
$ ./install.sh
|
|
38
38
|
$ ./install.sh ruby-gem
|
|
39
39
|
$ pwn
|
|
40
|
-
pwn[v0.5.
|
|
40
|
+
pwn[v0.5.520]:001 >>> PWN.help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
[](https://youtu.be/G7iLUY4FzsI)
|
|
@@ -52,7 +52,7 @@ $ rvm use ruby-3.4.7@pwn
|
|
|
52
52
|
$ gem uninstall --all --executables pwn
|
|
53
53
|
$ gem install --verbose pwn
|
|
54
54
|
$ pwn
|
|
55
|
-
pwn[v0.5.
|
|
55
|
+
pwn[v0.5.520]:001 >>> PWN.help
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
If you're using a multi-user install of RVM do:
|
|
@@ -62,7 +62,7 @@ $ rvm use ruby-3.4.7@pwn
|
|
|
62
62
|
$ rvmsudo gem uninstall --all --executables pwn
|
|
63
63
|
$ rvmsudo gem install --verbose pwn
|
|
64
64
|
$ pwn
|
|
65
|
-
pwn[v0.5.
|
|
65
|
+
pwn[v0.5.520]:001 >>> PWN.help
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script:
|
data/bin/pwn_gqrx_scanner
CHANGED
|
@@ -45,10 +45,6 @@ PWN::Driver::Parser.new do |options|
|
|
|
45
45
|
opts[:demodulator_mode] = d
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
options.on('-rRDS', '--rds', '<Optional - Enable RDS Decoding when in WFM or WFM_ST Demodulator Mode (Defaults to false)>') do |r|
|
|
49
|
-
opts[:rds] = r
|
|
50
|
-
end
|
|
51
|
-
|
|
52
48
|
options.on('-PINT', '--precision=INT', '<Optional - Precision of Frequency 1-12 (Defaults to 5)>') do |p|
|
|
53
49
|
opts[:precision] = p
|
|
54
50
|
end
|
|
@@ -116,7 +112,6 @@ begin
|
|
|
116
112
|
gqrx_sock = PWN::SDR::GQRX.connect(target: host, port: port)
|
|
117
113
|
|
|
118
114
|
demodulator_mode = opts[:demodulator_mode]
|
|
119
|
-
rds = opts[:rds]
|
|
120
115
|
bandwidth = opts[:bandwidth]
|
|
121
116
|
overlap_protection = opts[:overlap_protection]
|
|
122
117
|
|
|
@@ -144,6 +139,7 @@ begin
|
|
|
144
139
|
baseband_gain = baseband_gain.to_f unless baseband_gain.nil?
|
|
145
140
|
|
|
146
141
|
scan_log = opts[:scan_log]
|
|
142
|
+
decoder = opts[:decoder]
|
|
147
143
|
location = opts[:location]
|
|
148
144
|
|
|
149
145
|
# Merge opts again to ensure we override profile values with CLI values
|
|
@@ -152,7 +148,6 @@ begin
|
|
|
152
148
|
start_freq: start_freq,
|
|
153
149
|
target_freq: target_freq,
|
|
154
150
|
demodulator_mode: demodulator_mode,
|
|
155
|
-
rds: rds,
|
|
156
151
|
bandwidth: bandwidth,
|
|
157
152
|
overlap_protection: overlap_protection,
|
|
158
153
|
audio_gain_db: audio_gain_db,
|
|
@@ -164,6 +159,7 @@ begin
|
|
|
164
159
|
intermediate_gain: intermediate_gain,
|
|
165
160
|
baseband_gain: baseband_gain,
|
|
166
161
|
scan_log: scan_log,
|
|
162
|
+
decoder: decoder,
|
|
167
163
|
location: location
|
|
168
164
|
)
|
|
169
165
|
puts 'Scan Complete.'
|
data/lib/pwn/sdr/decoder/rds.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'tty-spinner'
|
|
5
|
+
|
|
3
6
|
module PWN
|
|
4
7
|
module SDR
|
|
5
8
|
module Decoder
|
|
@@ -7,90 +10,70 @@ module PWN
|
|
|
7
10
|
module RDS
|
|
8
11
|
# Supported Method Parameters::
|
|
9
12
|
# rds_resp = PWN::SDR::GQRX.decode_rds(
|
|
10
|
-
#
|
|
13
|
+
# freq_obj: 'required - GQRX socket object returned from #connect method'
|
|
11
14
|
# )
|
|
12
|
-
|
|
13
|
-
# private_class_method def self.rds(opts = {})
|
|
14
15
|
public_class_method def self.decode(opts = {})
|
|
15
|
-
|
|
16
|
+
freq_obj = opts[:freq_obj]
|
|
17
|
+
gqrx_sock = freq_obj[:gqrx_sock]
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
freq_obj = freq_obj.dup
|
|
20
|
+
freq_obj.delete(:gqrx_sock)
|
|
21
|
+
skip_rds = "\n"
|
|
22
|
+
puts JSON.pretty_generate(freq_obj)
|
|
23
|
+
puts "\n*** FM Radio RDS Decoder ***"
|
|
24
|
+
puts '(Press [ENTER] to continue)...'
|
|
25
|
+
|
|
26
|
+
# Toggle RDS off and on to reset the decoder
|
|
27
|
+
PWN::SDR::GQRX.gqrx_cmd(
|
|
19
28
|
gqrx_sock: gqrx_sock,
|
|
20
29
|
cmd: 'U RDS 0',
|
|
21
30
|
resp_ok: 'RPRT 0'
|
|
22
31
|
)
|
|
23
32
|
|
|
24
|
-
|
|
33
|
+
PWN::SDR::GQRX.gqrx_cmd(
|
|
25
34
|
gqrx_sock: gqrx_sock,
|
|
26
35
|
cmd: 'U RDS 1',
|
|
27
36
|
resp_ok: 'RPRT 0'
|
|
28
37
|
)
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
rds_resp
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
sleep 0.1
|
|
50
|
-
end
|
|
51
|
-
puts 'complete.'
|
|
52
|
-
rds_resp
|
|
53
|
-
rescue StandardError => e
|
|
54
|
-
raise e
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Starts the live decoding thread.
|
|
58
|
-
def self.start(opts = {})
|
|
59
|
-
freq_obj = opts[:freq_obj]
|
|
60
|
-
raise ':ERROR: :freq_obj is required' unless freq_obj.is_a?(Hash)
|
|
61
|
-
|
|
62
|
-
gqrx_sock = freq_obj[:gqrx_sock]
|
|
63
|
-
freq = freq_obj[:freq]
|
|
64
|
-
bandwidth = freq_obj[:bandwidth].to_i
|
|
65
|
-
record_path = freq_obj[:record_path]
|
|
66
|
-
|
|
67
|
-
sleep 0.1 until File.exist?(record_path)
|
|
68
|
-
|
|
69
|
-
header = File.binread(record_path, HEADER_SIZE)
|
|
70
|
-
raise 'Invalid WAV header' unless header.start_with?('RIFF') && header.include?('WAVE')
|
|
71
|
-
|
|
72
|
-
bytes_read = HEADER_SIZE
|
|
73
|
-
|
|
74
|
-
puts "GSM Decoder started for freq: #{freq}, bandwidth: #{bandwidth}"
|
|
75
|
-
|
|
76
|
-
Thread.new do
|
|
77
|
-
loop do
|
|
78
|
-
sleep 1
|
|
39
|
+
# Use TTY::Spinner correctly with a dynamic :title token
|
|
40
|
+
spinner = TTY::Spinner.new('[:spinner] :title', format: :arrow_pulse)
|
|
41
|
+
spinner.update(title: 'INFO: Decoding FM radio RDS data...')
|
|
42
|
+
spinner.auto_spin # Background thread handles smooth animation
|
|
43
|
+
|
|
44
|
+
last_resp = {}
|
|
45
|
+
|
|
46
|
+
loop do
|
|
47
|
+
rds_resp = {
|
|
48
|
+
rds_pi: PWN::SDR::GQRX.gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'p RDS_PI').to_s.strip.chomp,
|
|
49
|
+
rds_ps_name: PWN::SDR::GQRX.gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'p RDS_PS_NAME').to_s.strip.chomp,
|
|
50
|
+
rds_radiotext: PWN::SDR::GQRX.gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'p RDS_RADIOTEXT').to_s.strip.chomp
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Only update the displayed message when we have new, complete, valid RDS data
|
|
54
|
+
if rds_resp[:rds_pi] != '0000' && rds_resp != last_resp
|
|
55
|
+
status = "Program ID: #{rds_resp[:rds_pi]} | Station Name: #{rds_resp[:rds_ps_name].ljust(8)} | Radio Text: #{rds_resp[:rds_radiotext]}"
|
|
56
|
+
spinner.update(title: status)
|
|
57
|
+
last_resp = rds_resp.dup
|
|
79
58
|
end
|
|
80
|
-
rescue StandardError => e
|
|
81
|
-
puts "Decoder error: #{e.message}"
|
|
82
|
-
ensure
|
|
83
|
-
cleanup(record_path: record_path)
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
59
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
60
|
+
# Non-blocking check for ENTER key
|
|
61
|
+
if $stdin.wait_readable(0)
|
|
62
|
+
begin
|
|
63
|
+
char = $stdin.read_nonblock(1)
|
|
64
|
+
break if char == skip_rds
|
|
65
|
+
rescue IO::WaitReadable, EOFError
|
|
66
|
+
# No-op
|
|
67
|
+
end
|
|
68
|
+
end
|
|
91
69
|
|
|
92
|
-
|
|
93
|
-
|
|
70
|
+
sleep 0.01
|
|
71
|
+
end
|
|
72
|
+
rescue StandardError => e
|
|
73
|
+
spinner.error('Decoding failed') if defined?(spinner)
|
|
74
|
+
raise e
|
|
75
|
+
ensure
|
|
76
|
+
spinner.stop if spinner
|
|
94
77
|
end
|
|
95
78
|
|
|
96
79
|
# Author(s):: 0day Inc. <support@0dayinc.com>
|
|
@@ -105,12 +88,7 @@ module PWN
|
|
|
105
88
|
|
|
106
89
|
public_class_method def self.help
|
|
107
90
|
puts "USAGE:
|
|
108
|
-
|
|
109
|
-
freq_obj: 'required - freq_obj returned from PWN::SDR::Receiver::GQRX.init_freq method'
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
# To stop the decoder thread:
|
|
113
|
-
#{self}.stop(
|
|
91
|
+
#{self}.decode(
|
|
114
92
|
freq_obj: 'required - freq_obj returned from PWN::SDR::Receiver::GQRX.init_freq method'
|
|
115
93
|
)
|
|
116
94
|
|
|
@@ -180,9 +180,9 @@ module PWN
|
|
|
180
180
|
start_freq: '87.900.000',
|
|
181
181
|
target_freq: '108.000.000',
|
|
182
182
|
demodulator_mode: :WFM_ST,
|
|
183
|
-
rds: true,
|
|
184
183
|
bandwidth: '200.000',
|
|
185
|
-
precision: 6
|
|
184
|
+
precision: 6,
|
|
185
|
+
decoder: :rds
|
|
186
186
|
},
|
|
187
187
|
frs: {
|
|
188
188
|
start_freq: '462.562.500',
|
data/lib/pwn/sdr/gqrx.rb
CHANGED
|
@@ -178,7 +178,7 @@ module PWN
|
|
|
178
178
|
distance_between_unique_samples = (strength_db - prev_strength_db).abs.round(2)
|
|
179
179
|
strength_measured = true if distance_between_unique_samples.positive? && strength_lock > strength_db
|
|
180
180
|
end
|
|
181
|
-
strength_measured = true if distance_between_unique_samples.positive? && distance_between_unique_samples < 5
|
|
181
|
+
strength_measured = true if (distance_between_unique_samples.positive? && distance_between_unique_samples < 5) || attempts >= 500
|
|
182
182
|
|
|
183
183
|
break if strength_measured
|
|
184
184
|
|
|
@@ -187,7 +187,8 @@ module PWN
|
|
|
187
187
|
end
|
|
188
188
|
# Uncomment for debugging strength measurement attempts
|
|
189
189
|
# which translates to speed and accuracy refinement
|
|
190
|
-
puts "\tStrength Measurement Attempts: #{attempts} | Freq: #{freq} | Phase: #{phase}
|
|
190
|
+
puts "\tStrength Measurement Attempts: #{attempts} | Freq: #{freq} | Phase: #{phase}"
|
|
191
|
+
puts "\tUnique Samples: #{unique_samples} | dbFS Distance Unique Samples: #{distance_between_unique_samples}"
|
|
191
192
|
|
|
192
193
|
strength_db.round(1)
|
|
193
194
|
rescue StandardError => e
|
|
@@ -469,7 +470,6 @@ module PWN
|
|
|
469
470
|
# gqrx_sock: 'required - GQRX socket object returned from #connect method',
|
|
470
471
|
# freq: 'required - Frequency to set',
|
|
471
472
|
# demodulator_mode: 'optional - Demodulator mode (defaults to WFM)',
|
|
472
|
-
# rds: 'optional - Boolean to enable/disable RDS decoding (defaults to false)',
|
|
473
473
|
# bandwidth: 'optional - Bandwidth (defaults to "200.000")',
|
|
474
474
|
# squelch: 'optional - Squelch level to set (Defaults to current value)',
|
|
475
475
|
# decoder: 'optional - Decoder key (e.g., :gsm) to start live decoding (starts recording if provided)',
|
|
@@ -498,8 +498,6 @@ module PWN
|
|
|
498
498
|
demodulator_mode = opts[:demodulator_mode] ||= :WFM
|
|
499
499
|
raise "ERROR: Invalid demodulator_mode '#{demodulator_mode}'. Valid modes: #{valid_demodulator_modes.join(', ')}" unless valid_demodulator_modes.include?(demodulator_mode.to_sym)
|
|
500
500
|
|
|
501
|
-
rds = opts[:rds] ||= false
|
|
502
|
-
|
|
503
501
|
bandwidth = opts[:bandwidth] ||= '200.000'
|
|
504
502
|
squelch = opts[:squelch]
|
|
505
503
|
decoder = opts[:decoder]
|
|
@@ -537,8 +535,8 @@ module PWN
|
|
|
537
535
|
freq: freq,
|
|
538
536
|
demodulator_mode: demodulator_mode,
|
|
539
537
|
bandwidth: bandwidth,
|
|
540
|
-
|
|
541
|
-
|
|
538
|
+
strength_db: strength_db,
|
|
539
|
+
decoder: decoder
|
|
542
540
|
}
|
|
543
541
|
|
|
544
542
|
unless suppress_details
|
|
@@ -572,58 +570,56 @@ module PWN
|
|
|
572
570
|
cmd: 'l BB_GAIN'
|
|
573
571
|
)
|
|
574
572
|
|
|
575
|
-
rds_resp = nil
|
|
576
|
-
rds_resp = PWN::SDR::Decoder::RDS.decode(gqrx_sock: gqrx_sock) if rds
|
|
577
|
-
|
|
578
573
|
freq_obj[:audio_gain_db] = audio_gain_db
|
|
579
574
|
freq_obj[:demod_mode_n_passband] = demod_n_passband
|
|
580
575
|
freq_obj[:bb_gain] = bb_gain
|
|
581
576
|
freq_obj[:if_gain] = if_gain
|
|
582
577
|
freq_obj[:rf_gain] = rf_gain
|
|
583
578
|
freq_obj[:squelch] = squelch
|
|
584
|
-
freq_obj[:rds] = rds_resp
|
|
585
|
-
end
|
|
586
|
-
|
|
587
|
-
# Start recording and decoding if decoder provided
|
|
588
|
-
decoder_module = nil
|
|
589
|
-
decoder_thread = nil
|
|
590
|
-
record_path = nil
|
|
591
|
-
if decoder
|
|
592
|
-
# Resolve decoder module via case statement for extensibility
|
|
593
|
-
case decoder
|
|
594
|
-
when :gsm
|
|
595
|
-
decoder_module = PWN::SDR::Decoder::GSM
|
|
596
|
-
when :rds
|
|
597
|
-
decoder_module = PWN::SDR::Decoder::RDS
|
|
598
|
-
else
|
|
599
|
-
raise "ERROR: Unknown decoder key: #{decoder}. Supported: :gsm"
|
|
600
|
-
end
|
|
601
|
-
|
|
602
|
-
# Ensure recording is off before starting
|
|
603
|
-
record_status = gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'u RECORD')
|
|
604
|
-
gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'U RECORD 0', resp_ok: 'RPRT 0') if record_status == '1'
|
|
605
|
-
|
|
606
|
-
# Start recording
|
|
607
|
-
gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'U RECORD 1', resp_ok: 'RPRT 0')
|
|
608
|
-
|
|
609
|
-
# Prepare for decoder
|
|
610
|
-
start_time = Time.now
|
|
611
|
-
expected_filename = "gqrx_#{start_time.strftime('%Y%m%d_%H%M%S')}_#{current_freq_raw}.wav"
|
|
612
|
-
record_path = File.join(record_dir, expected_filename)
|
|
613
|
-
|
|
614
|
-
# Build partial gqrx_obj for decoder start
|
|
615
|
-
freq_obj[:record_path] = record_path
|
|
616
579
|
|
|
617
|
-
#
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
580
|
+
# Start recording and decoding if decoder provided
|
|
581
|
+
if decoder
|
|
582
|
+
decoder = decoder.to_s.to_sym
|
|
583
|
+
decoder_module = nil
|
|
584
|
+
decoder_thread = nil
|
|
585
|
+
record_path = nil
|
|
586
|
+
|
|
587
|
+
# Resolve decoder module via case statement for extensibility
|
|
588
|
+
case decoder
|
|
589
|
+
when :gsm
|
|
590
|
+
decoder_module = PWN::SDR::Decoder::GSM
|
|
591
|
+
when :rds
|
|
592
|
+
decoder_module = PWN::SDR::Decoder::RDS
|
|
593
|
+
else
|
|
594
|
+
raise "ERROR: Unknown decoder key: #{decoder}. Supported: :gsm"
|
|
595
|
+
end
|
|
621
596
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
597
|
+
# Ensure recording is off before starting
|
|
598
|
+
# unless decoder == :rds
|
|
599
|
+
# puts decoder.inspect
|
|
600
|
+
# record_status = gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'u RECORD')
|
|
601
|
+
# gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'U RECORD 0', resp_ok: 'RPRT 0') if record_status == '1'
|
|
602
|
+
|
|
603
|
+
# # Start recording
|
|
604
|
+
# gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'U RECORD 1', resp_ok: 'RPRT 0')
|
|
605
|
+
|
|
606
|
+
# # Prepare for decoder
|
|
607
|
+
# start_time = Time.now
|
|
608
|
+
# expected_filename = "gqrx_#{start_time.strftime('%Y%m%d_%H%M%S')}_#{current_freq_raw}.wav"
|
|
609
|
+
# record_path = File.join(record_dir, expected_filename)
|
|
610
|
+
|
|
611
|
+
# # Build partial gqrx_obj for decoder start
|
|
612
|
+
# freq_obj[:record_path] = record_path
|
|
613
|
+
# end
|
|
614
|
+
|
|
615
|
+
# Initialize and start decoder (module style: .start returns thread)
|
|
616
|
+
freq_obj[:gqrx_sock] = gqrx_sock
|
|
617
|
+
freq_obj[:decoder_module] = decoder_module
|
|
618
|
+
freq_obj[:record_path] = record_path
|
|
619
|
+
decoder_module.decode(freq_obj: freq_obj)
|
|
620
|
+
# decoder_thread = decoder_module.start(freq_obj: freq_obj)
|
|
621
|
+
# freq_obj[:decoder_thread] = decoder_thread
|
|
622
|
+
end
|
|
627
623
|
end
|
|
628
624
|
|
|
629
625
|
freq_obj
|
|
@@ -631,14 +627,13 @@ module PWN
|
|
|
631
627
|
raise e
|
|
632
628
|
ensure
|
|
633
629
|
# Ensure decoder recording stops
|
|
634
|
-
if decoder
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
end
|
|
630
|
+
# if decoder && decoder != :rds
|
|
631
|
+
# gqrx_cmd(
|
|
632
|
+
# gqrx_sock: gqrx_sock,
|
|
633
|
+
# cmd: 'U RECORD 0',
|
|
634
|
+
# resp_ok: 'RPRT 0'
|
|
635
|
+
# )
|
|
636
|
+
# end
|
|
642
637
|
disconnect(gqrx_sock: gqrx_sock) if gqrx_sock.is_a?(TCPSocket) && !keep_alive
|
|
643
638
|
end
|
|
644
639
|
|
|
@@ -648,7 +643,6 @@ module PWN
|
|
|
648
643
|
# start_freq: 'required - Start frequency of scan range',
|
|
649
644
|
# target_freq: 'required - Target frequency of scan range',
|
|
650
645
|
# demodulator_mode: 'optional - Demodulator mode (e.g. WFM, AM, FM, USB, LSB, RAW, CW, RTTY / defaults to WFM)',
|
|
651
|
-
# rds: 'optional - Boolean to enable/disable RDS decoding (defaults to false)',
|
|
652
646
|
# bandwidth: 'optional - Bandwidth in Hz (Defaults to "200.000")',
|
|
653
647
|
# precision: 'optional - Frequency step precision (number of digits; defaults to 1)',
|
|
654
648
|
# strength_lock: 'optional - Strength lock in dBFS (defaults to -70.0)',
|
|
@@ -674,7 +668,6 @@ module PWN
|
|
|
674
668
|
hz_target = PWN::SDR.hz_to_i(target_freq)
|
|
675
669
|
|
|
676
670
|
demodulator_mode = opts[:demodulator_mode]
|
|
677
|
-
rds = opts[:rds] ||= false
|
|
678
671
|
|
|
679
672
|
bandwidth = opts[:bandwidth] ||= '200.000'
|
|
680
673
|
|
|
@@ -682,6 +675,7 @@ module PWN
|
|
|
682
675
|
strength_lock = opts[:strength_lock] ||= -70.0
|
|
683
676
|
squelch = opts[:squelch] ||= (strength_lock - 3.0)
|
|
684
677
|
scan_log = opts[:scan_log] ||= "/tmp/pwn_sdr_gqrx_scan_#{PWN::SDR.hz_to_s(hz_start)}-#{PWN::SDR.hz_to_s(hz_target)}_#{log_timestamp}.json"
|
|
678
|
+
decoder = opts[:decoder]
|
|
685
679
|
location = opts[:location] ||= 'United States'
|
|
686
680
|
|
|
687
681
|
step_hz = 10**(precision - 1)
|
|
@@ -750,9 +744,9 @@ module PWN
|
|
|
750
744
|
gqrx_sock: gqrx_sock,
|
|
751
745
|
freq: hz_start,
|
|
752
746
|
demodulator_mode: demodulator_mode,
|
|
753
|
-
rds: rds,
|
|
754
747
|
bandwidth: bandwidth,
|
|
755
748
|
squelch: squelch,
|
|
749
|
+
decoder: decoder,
|
|
756
750
|
suppress_details: true,
|
|
757
751
|
keep_alive: true
|
|
758
752
|
)
|
|
@@ -811,8 +805,8 @@ module PWN
|
|
|
811
805
|
prev_freq_obj = init_freq(
|
|
812
806
|
gqrx_sock: gqrx_sock,
|
|
813
807
|
freq: best_freq,
|
|
814
|
-
rds: rds,
|
|
815
808
|
suppress_details: true,
|
|
809
|
+
decoder: decoder,
|
|
816
810
|
keep_alive: true
|
|
817
811
|
)
|
|
818
812
|
prev_freq_obj[:strength_lock] = strength_lock
|
|
@@ -874,6 +868,7 @@ module PWN
|
|
|
874
868
|
)
|
|
875
869
|
|
|
876
870
|
scan_resp[:signals].each do |signal|
|
|
871
|
+
# puts JSON.pretty_generate(signal)
|
|
877
872
|
signal[:gqrx_sock] = gqrx_sock
|
|
878
873
|
# This is required to keep connection alive during analysis
|
|
879
874
|
signal[:keep_alive] = true
|
|
@@ -881,9 +876,11 @@ module PWN
|
|
|
881
876
|
freq_obj = signal.merge(freq_obj)
|
|
882
877
|
# Redact gqrx_sock from output
|
|
883
878
|
freq_obj.delete(:gqrx_sock)
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
879
|
+
unless freq_obj[:decoder]
|
|
880
|
+
puts JSON.pretty_generate(freq_obj)
|
|
881
|
+
print 'Press [ENTER] to continue...'
|
|
882
|
+
gets
|
|
883
|
+
end
|
|
887
884
|
puts "\n" * 3
|
|
888
885
|
end
|
|
889
886
|
rescue Interrupt
|
|
@@ -958,7 +955,6 @@ module PWN
|
|
|
958
955
|
gqrx_sock: 'required - GQRX socket object returned from #connect method',
|
|
959
956
|
freq: 'required - Frequency to set',
|
|
960
957
|
demodulator_mode: 'optional - Demodulator mode (defaults to WFM)',
|
|
961
|
-
rds: 'optional - Boolean to enable/disable RDS decoding (defaults to false)',
|
|
962
958
|
bandwidth: 'optional - Bandwidth (defaults to \"200.000\")',
|
|
963
959
|
decoder: 'optional - Decoder key (e.g., :gsm) to start live decoding (starts recording if provided)',
|
|
964
960
|
record_dir: 'optional - Directory where GQRX saves recordings (required if decoder provided; defaults to /tmp/gqrx_recordings)',
|
data/lib/pwn/version.rb
CHANGED
data/third_party/pwn_rdoc.jsonl
CHANGED
|
@@ -1274,11 +1274,18 @@
|
|
|
1274
1274
|
{"messages":[{"role":"user","content":"PWN::SAST::WindowLocationHash.scan Usage"},{"role":"assistant","content":"`PWN::SAST::WindowLocationHash.scan`: Supported Method Parameters\n\nPWN::SAST::WindowLocationHash.scan(\n\ndir_path: 'optional path to dir defaults to .'\ngit_repo_root_uri: 'optional http uri of git repo scanned'\n\n)\n"}]}
|
|
1275
1275
|
{"messages":[{"role":"user","content":"PWN::SAST::WindowLocationHash.security_references Usage"},{"role":"assistant","content":"`PWN::SAST::WindowLocationHash.security_references`: "}]}
|
|
1276
1276
|
{"messages":[{"role":"user","content":"PWN::SDR.help Usage"},{"role":"assistant","content":"`PWN::SDR.help`: "}]}
|
|
1277
|
+
{"messages":[{"role":"user","content":"PWN::SDR.hz_to_i Usage"},{"role":"assistant","content":"`PWN::SDR.hz_to_i`: "}]}
|
|
1278
|
+
{"messages":[{"role":"user","content":"PWN::SDR.hz_to_s Usage"},{"role":"assistant","content":"`PWN::SDR.hz_to_s`: "}]}
|
|
1277
1279
|
{"messages":[{"role":"user","content":"PWN::SDR::Decoder.help Usage"},{"role":"assistant","content":"`PWN::SDR::Decoder.help`: "}]}
|
|
1278
1280
|
{"messages":[{"role":"user","content":"PWN::SDR::Decoder::GSM.authors Usage"},{"role":"assistant","content":"`PWN::SDR::Decoder::GSM.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
1279
1281
|
{"messages":[{"role":"user","content":"PWN::SDR::Decoder::GSM.help Usage"},{"role":"assistant","content":"`PWN::SDR::Decoder::GSM.help`: "}]}
|
|
1280
1282
|
{"messages":[{"role":"user","content":"PWN::SDR::Decoder::GSM.start Usage"},{"role":"assistant","content":"`PWN::SDR::Decoder::GSM.start`: "}]}
|
|
1281
1283
|
{"messages":[{"role":"user","content":"PWN::SDR::Decoder::GSM.stop Usage"},{"role":"assistant","content":"`PWN::SDR::Decoder::GSM.stop`: "}]}
|
|
1284
|
+
{"messages":[{"role":"user","content":"PWN::SDR::Decoder::RDS.authors Usage"},{"role":"assistant","content":"`PWN::SDR::Decoder::RDS.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
1285
|
+
{"messages":[{"role":"user","content":"PWN::SDR::Decoder::RDS.decode Usage"},{"role":"assistant","content":"`PWN::SDR::Decoder::RDS.decode`: "}]}
|
|
1286
|
+
{"messages":[{"role":"user","content":"PWN::SDR::Decoder::RDS.help Usage"},{"role":"assistant","content":"`PWN::SDR::Decoder::RDS.help`: "}]}
|
|
1287
|
+
{"messages":[{"role":"user","content":"PWN::SDR::Decoder::RDS.start Usage"},{"role":"assistant","content":"`PWN::SDR::Decoder::RDS.start`: "}]}
|
|
1288
|
+
{"messages":[{"role":"user","content":"PWN::SDR::Decoder::RDS.stop Usage"},{"role":"assistant","content":"`PWN::SDR::Decoder::RDS.stop`: "}]}
|
|
1282
1289
|
{"messages":[{"role":"user","content":"PWN::SDR::FlipperZero.authors Usage"},{"role":"assistant","content":"`PWN::SDR::FlipperZero.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
1283
1290
|
{"messages":[{"role":"user","content":"PWN::SDR::FlipperZero.connect Usage"},{"role":"assistant","content":"`PWN::SDR::FlipperZero.connect`: Supported Method Parameters\n\nflipper_zero_obj = PWN::SDR::FlipperZero.connect(\n\nblock_dev: 'optional serial block device path (defaults to /dev/ttyACM0)',\nbaud: 'optional (defaults to 9600)',\ndata_bits: 'optional (defaults to 8)',\nstop_bits: 'optional (defaults to 1)',\nparity: 'optional - :even||:odd|:none (defaults to :none)'\n\n)\n"}]}
|
|
1284
1291
|
{"messages":[{"role":"user","content":"PWN::SDR::FlipperZero.connect_via_screen Usage"},{"role":"assistant","content":"`PWN::SDR::FlipperZero.connect_via_screen`: Supported Method Parameters\n\nPWN::SDR::FlipperZero.connect_via_screen(\n\nblock_dev: 'optional - serial block device path (defaults to /dev/ttyACM0)'\n\n)\n"}]}
|
|
@@ -1292,18 +1299,16 @@
|
|
|
1292
1299
|
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.analyze_scan Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.analyze_scan`: Supported Method Parameters\n\nPWN::SDR::GQRX.analyze_scan(\n\nscan_resp: 'required - Scan response hash returned from #scan_range method',\ntarget: 'optional - GQRX target IP address (defaults to 127.0.0.1)',\nport: 'optional - GQRX target port (defaults to 7356)'\n\n)\n"}]}
|
|
1293
1300
|
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.authors Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
1294
1301
|
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.connect Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.connect`: Supported Method Parameters\n\ngqrx_sock = PWN::SDR::GQRX.connect(\n\ntarget: 'optional - GQRX target IP address (defaults to 127.0.0.1)',\nport: 'optional - GQRX target port (defaults to 7356)'\n\n)\n"}]}
|
|
1295
|
-
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.decode_rds Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.decode_rds`: Supported Method Parameters\n\nrds_resp = PWN::SDR::GQRX.decode_rds(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method'\n\n)\n"}]}
|
|
1296
1302
|
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.disconnect Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.disconnect`: Supported Method Parameters\n\nPWN::SDR::GQRX.disconnect(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method'\n\n)\n"}]}
|
|
1297
1303
|
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.edge_detection Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.edge_detection`: Supported Method Parameters\n\ncandidate_signals = PWN::SDR::GQRX.edge_detection(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method',\nhz: 'required - Frequency to start edge detection from',\nstep_hz: 'required - Frequency step in Hz for edge detection',\nstrength_lock: 'required - Strength lock in dBFS to determine signal edges'\n\n)\n"}]}
|
|
1304
|
+
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.find_best_peak Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.find_best_peak`: Supported Method Parameters\n\nbest_peak = PWN::SDR::GQRX.find_best_peak(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method',\ncandidate_signals: 'required - Array of candidate signals from edge_detection',\nstep_hz: 'required - Frequency step in Hz for peak finding',\nstrength_lock: 'required - Strength lock in dBFS to determine signal edges'\n\n)\n"}]}
|
|
1298
1305
|
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.gqrx_cmd Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.gqrx_cmd`: Supported Method Parameters\n\ngqrx_resp = PWN::SDR::GQRX.gqrx_cmd(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method',\ncmd: 'required - GQRX command to execute',\nresp_ok: 'optional - Expected response from GQRX to indicate success'\n\n)\n"}]}
|
|
1299
1306
|
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.help Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.help`: "}]}
|
|
1300
|
-
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.init_freq Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.init_freq`: Supported Method Parameters\n\nfreq_obj = PWN::SDR::GQRX.init_freq(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method',\nfreq: 'required - Frequency to set',\ndemodulator_mode: 'optional - Demodulator mode (defaults to WFM)',\nrds: 'optional - Boolean to enable/disable RDS decoding (defaults to false)',\nbandwidth: 'optional - Bandwidth (defaults to
|
|
1307
|
+
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.init_freq Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.init_freq`: Supported Method Parameters\n\nfreq_obj = PWN::SDR::GQRX.init_freq(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method',\nfreq: 'required - Frequency to set',\ndemodulator_mode: 'optional - Demodulator mode (defaults to WFM)',\nrds: 'optional - Boolean to enable/disable RDS decoding (defaults to false)',\nbandwidth: 'optional - Bandwidth (defaults to \"200.000\")',\nsquelch: 'optional - Squelch level to set (Defaults to current value)',\ndecoder: 'optional - Decoder key (e.g., :gsm) to start live decoding (starts recording if provided)',\nrecord_dir: 'optional - Directory where GQRX saves recordings (required if decoder provided; defaults to /tmp/gqrx_recordings)',\nsuppress_details: 'optional - Boolean to include extra frequency details in return hash (defaults to false)',\nkeep_alive: 'optional - Boolean to keep GQRX connection alive after method completion (defaults to false)'\n\n)\n"}]}
|
|
1301
1308
|
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.log_signals Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.log_signals`: Supported Method Parameters\n\nscan_resp = PWN::SDR::GQRX.log_signals(\n\nsignals_arr: 'required - Array of detected signals',\ntimestamp_start: 'required - Scan start timestamp',\nscan_log: 'required - Path to save detected signals log'\n\n)\n"}]}
|
|
1302
|
-
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.measure_signal_strength Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.measure_signal_strength`: Supported Method Parameters\n\nstrength_db = PWN::SDR::GQRX.measure_signal_strength(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method',\nstrength_lock: 'optional - Strength lock in dBFS to determine signal edges (defaults to -70.0)'\n\n)\n"}]}
|
|
1303
|
-
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.scan_range Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.scan_range`: Supported Method Parameters\n\nscan_resp = PWN::SDR::GQRX.scan_range(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method',\nstart_freq: 'required - Start frequency of scan range',\ntarget_freq: 'required - Target frequency of scan range',\ndemodulator_mode: 'optional - Demodulator mode (e.g. WFM, AM, FM, USB, LSB, RAW, CW, RTTY / defaults to WFM)',\nrds: 'optional - Boolean to enable/disable RDS decoding (defaults to false)',\nbandwidth: 'optional - Bandwidth in Hz (Defaults to
|
|
1309
|
+
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.measure_signal_strength Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.measure_signal_strength`: Supported Method Parameters\n\nstrength_db = PWN::SDR::GQRX.measure_signal_strength(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method',\nfreq: 'required - Frequency to measure signal strength',\nstrength_lock: 'optional - Strength lock in dBFS to determine signal edges (defaults to -70.0)',\nphase: 'optional - Phase of measurement for logging purposes (defaults to :find_candidates)'\n\n)\n"}]}
|
|
1310
|
+
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.scan_range Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.scan_range`: Supported Method Parameters\n\nscan_resp = PWN::SDR::GQRX.scan_range(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method',\nstart_freq: 'required - Start frequency of scan range',\ntarget_freq: 'required - Target frequency of scan range',\ndemodulator_mode: 'optional - Demodulator mode (e.g. WFM, AM, FM, USB, LSB, RAW, CW, RTTY / defaults to WFM)',\nrds: 'optional - Boolean to enable/disable RDS decoding (defaults to false)',\nbandwidth: 'optional - Bandwidth in Hz (Defaults to \"200.000\")',\nprecision: 'optional - Frequency step precision (number of digits; defaults to 1)',\nstrength_lock: 'optional - Strength lock in dBFS (defaults to -70.0)',\nsquelch: 'optional - Squelch level in dBFS (defaults to strength_lock - 3.0)',\naudio_gain_db: 'optional - Audio gain in dB (defaults to 6.0)',\nrf_gain: 'optional - RF gain (defaults to 0.0)',\nintermediate_gain: 'optional - Intermediate gain (defaults to 32.0)',\nbaseband_gain: 'optional - Baseband gain (defaults to 10.0)',\nscan_log: 'optional - Path to save detected signals log (defaults to /tmp/pwn_sdr_gqrx_scan_<start_freq>-<target_freq>_<timestamp>.json)',\nlocation: 'optional - Location string to include in AI analysis (e.g., \"New York, NY\", 90210, GPS coords, etc.)'\n\n)\n"}]}
|
|
1304
1311
|
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.tune_to Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.tune_to`: Supported Method Parameters\n\ntune_resp = PWN::SDR::GQRX.tune_to(\n\ngqrx_sock: 'required - GQRX socket object returned from #connect method',\nhz: 'required - Frequency to tune to'\n\n)\n"}]}
|
|
1305
|
-
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.cast_to_pretty_hz Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.cast_to_pretty_hz`: "}]}
|
|
1306
|
-
{"messages":[{"role":"user","content":"PWN::SDR::GQRX.cast_to_raw_hz Usage"},{"role":"assistant","content":"`PWN::SDR::GQRX.cast_to_raw_hz`: "}]}
|
|
1307
1312
|
{"messages":[{"role":"user","content":"PWN::SDR::RFIDler.authors Usage"},{"role":"assistant","content":"`PWN::SDR::RFIDler.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
1308
1313
|
{"messages":[{"role":"user","content":"PWN::SDR::RFIDler.connect_via_screen Usage"},{"role":"assistant","content":"`PWN::SDR::RFIDler.connect_via_screen`: Supported Method Parameters\n\nPWN::SDR::RFIDler.connect_via_screen(\n\nblock_dev: 'optional - serial block device path (defaults to /dev/ttyUSB0)'\n\n)\n"}]}
|
|
1309
1314
|
{"messages":[{"role":"user","content":"PWN::SDR::RFIDler.help Usage"},{"role":"assistant","content":"`PWN::SDR::RFIDler.help`: "}]}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pwn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.520
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 0day Inc.
|
|
@@ -141,14 +141,14 @@ dependencies:
|
|
|
141
141
|
requirements:
|
|
142
142
|
- - ">="
|
|
143
143
|
- !ruby/object:Gem::Version
|
|
144
|
-
version: 4.0.
|
|
144
|
+
version: 4.0.3
|
|
145
145
|
type: :development
|
|
146
146
|
prerelease: false
|
|
147
147
|
version_requirements: !ruby/object:Gem::Requirement
|
|
148
148
|
requirements:
|
|
149
149
|
- - ">="
|
|
150
150
|
- !ruby/object:Gem::Version
|
|
151
|
-
version: 4.0.
|
|
151
|
+
version: 4.0.3
|
|
152
152
|
- !ruby/object:Gem::Dependency
|
|
153
153
|
name: bundler-audit
|
|
154
154
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -2447,7 +2447,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
2447
2447
|
- !ruby/object:Gem::Version
|
|
2448
2448
|
version: '0'
|
|
2449
2449
|
requirements: []
|
|
2450
|
-
rubygems_version: 4.0.
|
|
2450
|
+
rubygems_version: 4.0.3
|
|
2451
2451
|
specification_version: 4
|
|
2452
2452
|
summary: Automated Security Testing for CI/CD Pipelines & Beyond
|
|
2453
2453
|
test_files: []
|