pwn 0.5.75 → 0.5.76
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 +11 -287
- data/lib/pwn/plugins/gqrx.rb +361 -0
- data/lib/pwn/plugins.rb +1 -0
- data/lib/pwn/version.rb +1 -1
- data/spec/lib/pwn/plugins/gqrx_spec.rb +15 -0
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6341c999a95a8ef1867905f07a60fd054d0ad217912b5b50dbafa1d42c8c1aa9
|
|
4
|
+
data.tar.gz: dc476c26b6f833f7c78850e8a32f9ad5b68cb36f0110ca5b739b2d5e8a690a55
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 30126968532893656b7d1db6bd1afb32a46b59831d9156fe7d1b4eaa317609b5c8e5a268e468ab4c7bc922b0731049485db14a703d24f958c600c6da564d77d3
|
|
7
|
+
data.tar.gz: a6a8d6b2d84a71bf9730885b1a7b1c2d746d1e6ac8c22183e089f03260cc57eeed8ed79f619093e11e087c4267dd3ca113fd9175bc9837d685c4b6795ea099e0
|
data/Gemfile
CHANGED
|
@@ -81,7 +81,7 @@ gem 'ruby-nmap', '1.0.3'
|
|
|
81
81
|
gem 'ruby-saml', '1.16.0'
|
|
82
82
|
gem 'rvm', '1.11.3.9'
|
|
83
83
|
gem 'savon', '2.15.0'
|
|
84
|
-
gem 'selenium-devtools', '0.
|
|
84
|
+
gem 'selenium-devtools', '0.123.0'
|
|
85
85
|
gem 'serialport', '1.3.2'
|
|
86
86
|
# gem 'sinatra', '4.0.0'
|
|
87
87
|
gem 'slack-ruby-client', '2.3.0'
|
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.76]:001 >>> PWN.help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
[](https://youtu.be/G7iLUY4FzsI)
|
|
@@ -52,7 +52,7 @@ $ rvm use ruby-3.3.0@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.76]: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.3.0@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.76]: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
|
@@ -73,282 +73,6 @@ if opts.empty?
|
|
|
73
73
|
exit 1
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
def gqrx_cmd(opts = {})
|
|
77
|
-
gqrx_sock = opts[:gqrx_sock]
|
|
78
|
-
cmd = opts[:cmd]
|
|
79
|
-
resp_ok = opts[:resp_ok]
|
|
80
|
-
|
|
81
|
-
# Most Recent GQRX Command Set:
|
|
82
|
-
# https://raw.githubusercontent.com/gqrx-sdr/gqrx/master/resources/remote-control.txt
|
|
83
|
-
# Supported commands:
|
|
84
|
-
# f Get frequency [Hz]
|
|
85
|
-
# F <frequency> Set frequency [Hz]
|
|
86
|
-
# m Get demodulator mode and passband
|
|
87
|
-
# M <mode> [passband]
|
|
88
|
-
# Set demodulator mode and passband [Hz]
|
|
89
|
-
# Passing a '?' as the first argument instead of 'mode' will return
|
|
90
|
-
# a space separated list of radio backend supported modes.
|
|
91
|
-
# l|L ?
|
|
92
|
-
# Get a space separated list of settings available for reading (l) or writing (L).
|
|
93
|
-
# l STRENGTH
|
|
94
|
-
# Get signal strength [dBFS]
|
|
95
|
-
# l SQL
|
|
96
|
-
# Get squelch threshold [dBFS]
|
|
97
|
-
# L SQL <sql>
|
|
98
|
-
# Set squelch threshold to <sql> [dBFS]
|
|
99
|
-
# l AF
|
|
100
|
-
# Get audio gain [dB]
|
|
101
|
-
# L AF <gain>
|
|
102
|
-
# Set audio gain to <gain> [dB]
|
|
103
|
-
# l <gain_name>_GAIN
|
|
104
|
-
# Get the value of the gain setting with the name <gain_name>
|
|
105
|
-
# L <gain_name>_GAIN <value>
|
|
106
|
-
# Set the value of the gain setting with the name <gain_name> to <value>
|
|
107
|
-
# p RDS_PI
|
|
108
|
-
# Get the RDS PI code (in hexadecimal). Returns 0000 if not applicable.
|
|
109
|
-
# u RECORD
|
|
110
|
-
# Get status of audio recorder
|
|
111
|
-
# U RECORD <status>
|
|
112
|
-
# Set status of audio recorder to <status>
|
|
113
|
-
# u DSP
|
|
114
|
-
# Get DSP (SDR receiver) status
|
|
115
|
-
# U DSP <status>
|
|
116
|
-
# Set DSP (SDR receiver) status to <status>
|
|
117
|
-
# u RDS
|
|
118
|
-
# Get RDS decoder to <status>. Only functions in WFM mode.
|
|
119
|
-
# U RDS <status>
|
|
120
|
-
# Set RDS decoder to <status>. Only functions in WFM mode.
|
|
121
|
-
# q|Q
|
|
122
|
-
# Close connection
|
|
123
|
-
# AOS
|
|
124
|
-
# Acquisition of signal (AOS) event, start audio recording
|
|
125
|
-
# LOS
|
|
126
|
-
# Loss of signal (LOS) event, stop audio recording
|
|
127
|
-
# LNB_LO [frequency]
|
|
128
|
-
# If frequency [Hz] is specified set the LNB LO frequency used for
|
|
129
|
-
# display. Otherwise print the current LNB LO frequency [Hz].
|
|
130
|
-
# \chk_vfo
|
|
131
|
-
# Get VFO option status (only usable for hamlib compatibility)
|
|
132
|
-
# \dump_state
|
|
133
|
-
# Dump state (only usable for hamlib compatibility)
|
|
134
|
-
# \get_powerstat
|
|
135
|
-
# Get power status (only usable for hamlib compatibility)
|
|
136
|
-
# v
|
|
137
|
-
# Get 'VFO' (only usable for hamlib compatibility)
|
|
138
|
-
# V
|
|
139
|
-
# Set 'VFO' (only usable for hamlib compatibility)
|
|
140
|
-
# s
|
|
141
|
-
# Get 'Split' mode (only usable for hamlib compatibility)
|
|
142
|
-
# S
|
|
143
|
-
# Set 'Split' mode (only usable for hamlib compatibility)
|
|
144
|
-
# _
|
|
145
|
-
# Get version
|
|
146
|
-
#
|
|
147
|
-
# Reply:
|
|
148
|
-
# RPRT 0
|
|
149
|
-
# Command successful
|
|
150
|
-
# RPRT 1
|
|
151
|
-
# Command failed
|
|
152
|
-
|
|
153
|
-
gqrx_sock.write("#{cmd}\n")
|
|
154
|
-
response = []
|
|
155
|
-
got_freq = false
|
|
156
|
-
# Read all responses from gqrx_sock.write
|
|
157
|
-
timeout = 0.001 if timeout.nil?
|
|
158
|
-
|
|
159
|
-
begin
|
|
160
|
-
response.push(gqrx_sock.readline.chomp) while gqrx_sock.wait_readable(timeout)
|
|
161
|
-
raise IOError if response.empty?
|
|
162
|
-
rescue IOError
|
|
163
|
-
timeout += 0.001
|
|
164
|
-
retry
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
got_int_value_in_resp = true if response.first.to_i.positive?
|
|
168
|
-
response = response.first if response.length == 1
|
|
169
|
-
|
|
170
|
-
raise "ERROR!!! Command: #{cmd} Expected Resp: #{resp_ok}, Got: #{response}" if resp_ok && response != resp_ok
|
|
171
|
-
|
|
172
|
-
if got_int_value_in_resp
|
|
173
|
-
fixed_len_freq = format('%0.12d', response.to_i)
|
|
174
|
-
freq_segments = fixed_len_freq.scan(/.{3}/)
|
|
175
|
-
first_non_zero_index = freq_segments.index { |s| s.to_i.positive? }
|
|
176
|
-
freq_segments = freq_segments[first_non_zero_index..-1]
|
|
177
|
-
freq_segments[0] = freq_segments.first.to_i.to_s
|
|
178
|
-
response = freq_segments.join('.')
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
# DEBUG
|
|
182
|
-
# puts response.inspect
|
|
183
|
-
# puts response.length
|
|
184
|
-
|
|
185
|
-
response
|
|
186
|
-
rescue RuntimeError => e
|
|
187
|
-
puts 'WARNING: RF Gain is not supported by the radio backend.' if e.message.include?('Command: L RF_GAIN')
|
|
188
|
-
puts 'WARNING: Intermediate Gain is not supported by the radio backend.' if e.message.include?('Command: L IF_GAIN')
|
|
189
|
-
puts 'WARNING: Baseband Gain is not supported by the radio backend.' if e.message.include?('Command: L BB_GAIN')
|
|
190
|
-
|
|
191
|
-
raise e unless e.message.include?('Command: L RF_GAIN') ||
|
|
192
|
-
e.message.include?('Command: L IF_GAIN') ||
|
|
193
|
-
e.message.include?('Command: L BB_GAIN')
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def init_freq(opts = {})
|
|
197
|
-
gqrx_sock = opts[:gqrx_sock]
|
|
198
|
-
demodulator_mode = opts[:demodulator_mode]
|
|
199
|
-
bandwidth = opts[:bandwidth]
|
|
200
|
-
this_freq = opts[:this_freq]
|
|
201
|
-
lock_freq_duration = opts[:lock_freq_duration]
|
|
202
|
-
strength_lock = opts[:strength_lock]
|
|
203
|
-
|
|
204
|
-
demod_n_passband = gqrx_cmd(
|
|
205
|
-
gqrx_sock: gqrx_sock,
|
|
206
|
-
cmd: 'm'
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
change_freq_resp = gqrx_cmd(
|
|
210
|
-
gqrx_sock: gqrx_sock,
|
|
211
|
-
cmd: "F #{this_freq}",
|
|
212
|
-
resp_ok: 'RPRT 0'
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
current_freq = gqrx_cmd(
|
|
216
|
-
gqrx_sock: gqrx_sock,
|
|
217
|
-
cmd: 'f'
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
audio_gain_db = gqrx_cmd(
|
|
221
|
-
gqrx_sock: gqrx_sock,
|
|
222
|
-
cmd: 'l AF'
|
|
223
|
-
).to_f
|
|
224
|
-
|
|
225
|
-
current_strength = gqrx_cmd(
|
|
226
|
-
gqrx_sock: gqrx_sock,
|
|
227
|
-
cmd: 'l STRENGTH'
|
|
228
|
-
).to_f
|
|
229
|
-
|
|
230
|
-
current_squelch = gqrx_cmd(
|
|
231
|
-
gqrx_sock: gqrx_sock,
|
|
232
|
-
cmd: 'l SQL'
|
|
233
|
-
).to_f
|
|
234
|
-
|
|
235
|
-
rf_gain = gqrx_cmd(
|
|
236
|
-
gqrx_sock: gqrx_sock,
|
|
237
|
-
cmd: 'l RF_GAIN'
|
|
238
|
-
).to_f
|
|
239
|
-
|
|
240
|
-
if_gain = gqrx_cmd(
|
|
241
|
-
gqrx_sock: gqrx_sock,
|
|
242
|
-
cmd: 'l IF_GAIN'
|
|
243
|
-
).to_f
|
|
244
|
-
|
|
245
|
-
bb_gain = gqrx_cmd(
|
|
246
|
-
gqrx_sock: gqrx_sock,
|
|
247
|
-
cmd: 'l BB_GAIN'
|
|
248
|
-
).to_f
|
|
249
|
-
|
|
250
|
-
init_freq_hash = {
|
|
251
|
-
demod_mode_n_passband: demod_n_passband,
|
|
252
|
-
frequency: current_freq,
|
|
253
|
-
bandwidth: bandwidth,
|
|
254
|
-
audio_gain_db: audio_gain_db,
|
|
255
|
-
squelch: current_squelch,
|
|
256
|
-
rf_gain: rf_gain,
|
|
257
|
-
if_gain: if_gain,
|
|
258
|
-
bb_gain: bb_gain,
|
|
259
|
-
strength: current_strength,
|
|
260
|
-
strength_lock: strength_lock,
|
|
261
|
-
lock_freq_duration: lock_freq_duration
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
print '.'
|
|
265
|
-
sleep lock_freq_duration if current_strength > strength_lock
|
|
266
|
-
|
|
267
|
-
init_freq_hash
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
def scan_range(opts = {})
|
|
271
|
-
gqrx_sock = opts[:gqrx_sock]
|
|
272
|
-
demodulator_mode = opts[:demodulator_mode]
|
|
273
|
-
bandwidth = opts[:bandwidth]
|
|
274
|
-
start_freq = opts[:start_freq]
|
|
275
|
-
target_freq = opts[:target_freq]
|
|
276
|
-
precision = opts[:precision]
|
|
277
|
-
lock_freq_duration = opts[:lock_freq_duration]
|
|
278
|
-
strength_lock = opts[:strength_lock]
|
|
279
|
-
|
|
280
|
-
multiplier = 10**(precision - 1)
|
|
281
|
-
prev_freq_hash = {
|
|
282
|
-
demod_mode_n_passband: demodulator_mode,
|
|
283
|
-
frequency: start_freq,
|
|
284
|
-
bandwidth: bandwidth,
|
|
285
|
-
audio_gain_db: 0.0,
|
|
286
|
-
squelch: 0.0,
|
|
287
|
-
rf_gain: 0.0,
|
|
288
|
-
if_gain: 0.0,
|
|
289
|
-
bb_gain: 0.0,
|
|
290
|
-
strength: 0.0,
|
|
291
|
-
strength_lock: strength_lock,
|
|
292
|
-
lock_freq_duration: lock_freq_duration
|
|
293
|
-
}
|
|
294
|
-
if start_freq > target_freq
|
|
295
|
-
start_freq.downto(target_freq) do |this_freq|
|
|
296
|
-
next unless (this_freq % multiplier).zero?
|
|
297
|
-
|
|
298
|
-
init_freq_hash = init_freq(
|
|
299
|
-
gqrx_sock: gqrx_sock,
|
|
300
|
-
demodulator_mode: demodulator_mode,
|
|
301
|
-
bandwidth: bandwidth,
|
|
302
|
-
this_freq: this_freq,
|
|
303
|
-
lock_freq_duration: lock_freq_duration,
|
|
304
|
-
strength_lock: strength_lock
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
current_strength = init_freq_hash[:strength]
|
|
308
|
-
prev_strength = prev_freq_hash[:strength]
|
|
309
|
-
prev_freq = prev_freq_hash[:frequency]
|
|
310
|
-
|
|
311
|
-
approaching_detection = true if current_strength > prev_strength &&
|
|
312
|
-
current_strength > strength_lock
|
|
313
|
-
if approaching_detection && current_strength <= prev_strength
|
|
314
|
-
puts "\n**** Found a signal ~ #{prev_freq} Hz ****"
|
|
315
|
-
puts JSON.pretty_generate(prev_freq_hash)
|
|
316
|
-
approaching_detection = false
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
prev_freq_hash = init_freq_hash
|
|
320
|
-
end
|
|
321
|
-
else
|
|
322
|
-
this_freq = start_freq
|
|
323
|
-
while this_freq <= target_freq
|
|
324
|
-
init_freq_hash = init_freq(
|
|
325
|
-
gqrx_sock: gqrx_sock,
|
|
326
|
-
demodulator_mode: demodulator_mode,
|
|
327
|
-
bandwidth: bandwidth,
|
|
328
|
-
this_freq: this_freq,
|
|
329
|
-
lock_freq_duration: lock_freq_duration,
|
|
330
|
-
strength_lock: strength_lock
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
current_strength = init_freq_hash[:strength]
|
|
334
|
-
prev_strength = prev_freq_hash[:strength]
|
|
335
|
-
prev_freq = prev_freq_hash[:frequency]
|
|
336
|
-
|
|
337
|
-
approaching_detection = true if current_strength > prev_strength &&
|
|
338
|
-
current_strength > strength_lock
|
|
339
|
-
if approaching_detection && current_strength < prev_strength
|
|
340
|
-
puts "\n**** Discovered a signal ~ #{prev_freq} Hz ****"
|
|
341
|
-
puts JSON.pretty_generate(prev_freq_hash)
|
|
342
|
-
approaching_detection = false
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
prev_freq_hash = init_freq_hash
|
|
346
|
-
|
|
347
|
-
this_freq += multiplier
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
end
|
|
351
|
-
|
|
352
76
|
begin
|
|
353
77
|
pwn_provider = 'ruby-gem'
|
|
354
78
|
pwn_provider = ENV.fetch('PWN_PROVIDER') if ENV.keys.any? { |s| s == 'PWN_PROVIDER' }
|
|
@@ -358,11 +82,11 @@ begin
|
|
|
358
82
|
target_freq = target_freq.to_i
|
|
359
83
|
raise "ERROR: Invalid target frequency #{target_freq}" if target_freq.zero?
|
|
360
84
|
|
|
361
|
-
host = opts[:host]
|
|
362
|
-
port = opts[:port]
|
|
85
|
+
host = opts[:host]
|
|
86
|
+
port = opts[:port]
|
|
363
87
|
|
|
364
88
|
puts "Connecting to GQRX at #{host}:#{port}..."
|
|
365
|
-
gqrx_sock = PWN::Plugins::
|
|
89
|
+
gqrx_sock = PWN::Plugins::GQRX.connect(target: host, port: port)
|
|
366
90
|
|
|
367
91
|
start_freq = opts[:start_freq]
|
|
368
92
|
start_freq = start_freq.to_s.delete('.') unless start_freq.nil?
|
|
@@ -377,7 +101,7 @@ begin
|
|
|
377
101
|
|
|
378
102
|
puts "Setting demodulator mode to #{demodulator_mode} and bandwidth to #{bandwidth}..."
|
|
379
103
|
bandwidth = bandwidth.to_s.delete('.').to_i unless bandwidth.nil?
|
|
380
|
-
demod_resp = gqrx_cmd(
|
|
104
|
+
demod_resp = PWN::Plugins::GQRX.gqrx_cmd(
|
|
381
105
|
gqrx_sock: gqrx_sock,
|
|
382
106
|
cmd: "M #{demodulator_mode} #{bandwidth}",
|
|
383
107
|
resp_ok: 'RPRT 0'
|
|
@@ -385,7 +109,7 @@ begin
|
|
|
385
109
|
|
|
386
110
|
audio_gain_db = opts[:audio_gain_db] ||= 1.0
|
|
387
111
|
audio_gain_db = audio_gain_db.to_f
|
|
388
|
-
audio_gain_db_resp = gqrx_cmd(
|
|
112
|
+
audio_gain_db_resp = PWN::Plugins::GQRX.gqrx_cmd(
|
|
389
113
|
gqrx_sock: gqrx_sock,
|
|
390
114
|
cmd: "L AF #{audio_gain_db}",
|
|
391
115
|
resp_ok: 'RPRT 0'
|
|
@@ -393,7 +117,7 @@ begin
|
|
|
393
117
|
|
|
394
118
|
squelch = opts[:squelch] ||= -63.0
|
|
395
119
|
squelch = squelch.to_f
|
|
396
|
-
squelch_resp = gqrx_cmd(
|
|
120
|
+
squelch_resp = PWN::Plugins::GQRX.gqrx_cmd(
|
|
397
121
|
gqrx_sock: gqrx_sock,
|
|
398
122
|
cmd: "L SQL #{squelch}",
|
|
399
123
|
resp_ok: 'RPRT 0'
|
|
@@ -411,7 +135,7 @@ begin
|
|
|
411
135
|
|
|
412
136
|
rf_gain = opts[:rf_gain] ||= 0.0
|
|
413
137
|
rf_gain = rf_gain.to_f
|
|
414
|
-
rf_gain_resp = gqrx_cmd(
|
|
138
|
+
rf_gain_resp = PWN::Plugins::GQRX.gqrx_cmd(
|
|
415
139
|
gqrx_sock: gqrx_sock,
|
|
416
140
|
cmd: "L RF_GAIN #{rf_gain}",
|
|
417
141
|
resp_ok: 'RPRT 0'
|
|
@@ -419,7 +143,7 @@ begin
|
|
|
419
143
|
|
|
420
144
|
intermediate_gain = opts[:intermediate_gain] ||= 32.0
|
|
421
145
|
intermediate_gain = intermediate_gain.to_f
|
|
422
|
-
intermediate_resp = gqrx_cmd(
|
|
146
|
+
intermediate_resp = PWN::Plugins::GQRX.gqrx_cmd(
|
|
423
147
|
gqrx_sock: gqrx_sock,
|
|
424
148
|
cmd: "L IF_GAIN #{intermediate_gain}",
|
|
425
149
|
resp_ok: 'RPRT 0'
|
|
@@ -427,7 +151,7 @@ begin
|
|
|
427
151
|
|
|
428
152
|
baseband_gain = opts[:baseband_gain] ||= 10.0
|
|
429
153
|
baseband_gain = baseband_gain.to_f
|
|
430
|
-
baseband_resp = gqrx_cmd(
|
|
154
|
+
baseband_resp = PWN::Plugins::GQRX.gqrx_cmd(
|
|
431
155
|
gqrx_sock: gqrx_sock,
|
|
432
156
|
cmd: "L BB_GAIN #{baseband_gain}",
|
|
433
157
|
resp_ok: 'RPRT 0'
|
|
@@ -437,7 +161,7 @@ begin
|
|
|
437
161
|
t_freq_pretty = target_freq.to_s.chars.insert(-4, '.').insert(-8, '.').join
|
|
438
162
|
puts "*** Scanning from #{s_freq_pretty} to #{t_freq_pretty}\n\n\n"
|
|
439
163
|
|
|
440
|
-
scan_range(
|
|
164
|
+
PWN::Plugins::GQRX.scan_range(
|
|
441
165
|
gqrx_sock: gqrx_sock,
|
|
442
166
|
demodulator_mode: demodulator_mode,
|
|
443
167
|
bandwidth: bandwidth,
|
|
@@ -454,5 +178,5 @@ rescue StandardError => e
|
|
|
454
178
|
rescue Interrupt, SystemExit
|
|
455
179
|
puts "\nGoodbye."
|
|
456
180
|
ensure
|
|
457
|
-
gqrx_sock = PWN::Plugins::
|
|
181
|
+
gqrx_sock = PWN::Plugins::GQRX.disconnect(gqrx_sock: gqrx_sock)
|
|
458
182
|
end
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open3'
|
|
4
|
+
|
|
5
|
+
module PWN
|
|
6
|
+
module Plugins
|
|
7
|
+
# This plugin interacts with the remote control interface of GQRX.
|
|
8
|
+
module GQRX
|
|
9
|
+
# Supported Method Parameters::
|
|
10
|
+
# gqrx_sock = PWN::Plugins::GQRX.connect(
|
|
11
|
+
# target: 'optional - GQRX target IP address (defaults to 127.0.0.1)',
|
|
12
|
+
# port: 'optional - GQRX target port (defaults to 7356)'
|
|
13
|
+
# )
|
|
14
|
+
public_class_method def self.connect(opts = {})
|
|
15
|
+
target = opts[:target] ||= '127.0.0.1'
|
|
16
|
+
port = opts[:port] ||= 7356
|
|
17
|
+
|
|
18
|
+
PWN::Plugins::Sock.connect(target: target, port: port)
|
|
19
|
+
rescue StandardError => e
|
|
20
|
+
raise e
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Supported Method Parameters::
|
|
24
|
+
# gqrx_resp = PWN::Plugins::GQRX.gqrx_cmd(
|
|
25
|
+
# gqrx_sock: 'required - GQRX socket object returned from #connect method',
|
|
26
|
+
# cmd: 'required - GQRX command to execute'
|
|
27
|
+
# resp_ok: 'optional - Expected response from GQRX to indicate success'
|
|
28
|
+
# )
|
|
29
|
+
|
|
30
|
+
public_class_method def self.gqrx_cmd(opts = {})
|
|
31
|
+
gqrx_sock = opts[:gqrx_sock]
|
|
32
|
+
cmd = opts[:cmd]
|
|
33
|
+
resp_ok = opts[:resp_ok]
|
|
34
|
+
|
|
35
|
+
# Most Recent GQRX Command Set:
|
|
36
|
+
# https://raw.githubusercontent.com/gqrx-sdr/gqrx/master/resources/remote-control.txt
|
|
37
|
+
# Supported commands:
|
|
38
|
+
# f Get frequency [Hz]
|
|
39
|
+
# F <frequency> Set frequency [Hz]
|
|
40
|
+
# m Get demodulator mode and passband
|
|
41
|
+
# M <mode> [passband]
|
|
42
|
+
# Set demodulator mode and passband [Hz]
|
|
43
|
+
# Passing a '?' as the first argument instead of 'mode' will return
|
|
44
|
+
# a space separated list of radio backend supported modes.
|
|
45
|
+
# l|L ?
|
|
46
|
+
# Get a space separated list of settings available for reading (l) or writing (L).
|
|
47
|
+
# l STRENGTH
|
|
48
|
+
# Get signal strength [dBFS]
|
|
49
|
+
# l SQL
|
|
50
|
+
# Get squelch threshold [dBFS]
|
|
51
|
+
# L SQL <sql>
|
|
52
|
+
# Set squelch threshold to <sql> [dBFS]
|
|
53
|
+
# l AF
|
|
54
|
+
# Get audio gain [dB]
|
|
55
|
+
# L AF <gain>
|
|
56
|
+
# Set audio gain to <gain> [dB]
|
|
57
|
+
# l <gain_name>_GAIN
|
|
58
|
+
# Get the value of the gain setting with the name <gain_name>
|
|
59
|
+
# L <gain_name>_GAIN <value>
|
|
60
|
+
# Set the value of the gain setting with the name <gain_name> to <value>
|
|
61
|
+
# p RDS_PI
|
|
62
|
+
# Get the RDS PI code (in hexadecimal). Returns 0000 if not applicable.
|
|
63
|
+
# u RECORD
|
|
64
|
+
# Get status of audio recorder
|
|
65
|
+
# U RECORD <status>
|
|
66
|
+
# Set status of audio recorder to <status>
|
|
67
|
+
# u DSP
|
|
68
|
+
# Get DSP (SDR receiver) status
|
|
69
|
+
# U DSP <status>
|
|
70
|
+
# Set DSP (SDR receiver) status to <status>
|
|
71
|
+
# u RDS
|
|
72
|
+
# Get RDS decoder to <status>. Only functions in WFM mode.
|
|
73
|
+
# U RDS <status>
|
|
74
|
+
# Set RDS decoder to <status>. Only functions in WFM mode.
|
|
75
|
+
# q|Q
|
|
76
|
+
# Close connection
|
|
77
|
+
# AOS
|
|
78
|
+
# Acquisition of signal (AOS) event, start audio recording
|
|
79
|
+
# LOS
|
|
80
|
+
# Loss of signal (LOS) event, stop audio recording
|
|
81
|
+
# LNB_LO [frequency]
|
|
82
|
+
# If frequency [Hz] is specified set the LNB LO frequency used for
|
|
83
|
+
# display. Otherwise print the current LNB LO frequency [Hz].
|
|
84
|
+
# \chk_vfo
|
|
85
|
+
# Get VFO option status (only usable for hamlib compatibility)
|
|
86
|
+
# \dump_state
|
|
87
|
+
# Dump state (only usable for hamlib compatibility)
|
|
88
|
+
# \get_powerstat
|
|
89
|
+
# Get power status (only usable for hamlib compatibility)
|
|
90
|
+
# v
|
|
91
|
+
# Get 'VFO' (only usable for hamlib compatibility)
|
|
92
|
+
# V
|
|
93
|
+
# Set 'VFO' (only usable for hamlib compatibility)
|
|
94
|
+
# s
|
|
95
|
+
# Get 'Split' mode (only usable for hamlib compatibility)
|
|
96
|
+
# S
|
|
97
|
+
# Set 'Split' mode (only usable for hamlib compatibility)
|
|
98
|
+
# _
|
|
99
|
+
# Get version
|
|
100
|
+
#
|
|
101
|
+
# Reply:
|
|
102
|
+
# RPRT 0
|
|
103
|
+
# Command successful
|
|
104
|
+
# RPRT 1
|
|
105
|
+
# Command failed
|
|
106
|
+
|
|
107
|
+
gqrx_sock.write("#{cmd}\n")
|
|
108
|
+
response = []
|
|
109
|
+
got_freq = false
|
|
110
|
+
# Read all responses from gqrx_sock.write
|
|
111
|
+
timeout = 0.001 if timeout.nil?
|
|
112
|
+
|
|
113
|
+
begin
|
|
114
|
+
response.push(gqrx_sock.readline.chomp) while gqrx_sock.wait_readable(timeout)
|
|
115
|
+
raise IOError if response.empty?
|
|
116
|
+
rescue IOError
|
|
117
|
+
timeout += 0.001
|
|
118
|
+
retry
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
got_int_value_in_resp = true if response.first.to_i.positive?
|
|
122
|
+
response = response.first if response.length == 1
|
|
123
|
+
|
|
124
|
+
raise "ERROR!!! Command: #{cmd} Expected Resp: #{resp_ok}, Got: #{response}" if resp_ok && response != resp_ok
|
|
125
|
+
|
|
126
|
+
if got_int_value_in_resp
|
|
127
|
+
fixed_len_freq = format('%0.12d', response.to_i)
|
|
128
|
+
freq_segments = fixed_len_freq.scan(/.{3}/)
|
|
129
|
+
first_non_zero_index = freq_segments.index { |s| s.to_i.positive? }
|
|
130
|
+
freq_segments = freq_segments[first_non_zero_index..-1]
|
|
131
|
+
freq_segments[0] = freq_segments.first.to_i.to_s
|
|
132
|
+
response = freq_segments.join('.')
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# DEBUG
|
|
136
|
+
# puts response.inspect
|
|
137
|
+
# puts response.length
|
|
138
|
+
|
|
139
|
+
response
|
|
140
|
+
rescue RuntimeError => e
|
|
141
|
+
puts 'WARNING: RF Gain is not supported by the radio backend.' if e.message.include?('Command: L RF_GAIN')
|
|
142
|
+
puts 'WARNING: Intermediate Gain is not supported by the radio backend.' if e.message.include?('Command: L IF_GAIN')
|
|
143
|
+
puts 'WARNING: Baseband Gain is not supported by the radio backend.' if e.message.include?('Command: L BB_GAIN')
|
|
144
|
+
|
|
145
|
+
raise e unless e.message.include?('Command: L RF_GAIN') ||
|
|
146
|
+
e.message.include?('Command: L IF_GAIN') ||
|
|
147
|
+
e.message.include?('Command: L BB_GAIN')
|
|
148
|
+
rescue StandardError => e
|
|
149
|
+
raise e
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Supported Method Parameters::
|
|
153
|
+
# PWN::Plugins::GQRX.init_freq(
|
|
154
|
+
# gqrx_sock: 'required - GQRX socket object returned from #connect method',
|
|
155
|
+
# freq: 'required - Frequency to set',
|
|
156
|
+
# demodulator_mode: 'optional - Demodulator mode (defaults to WFM)',
|
|
157
|
+
# bandwidth: 'optional - Bandwidth (defaults to 200000)',
|
|
158
|
+
# lock_freq_duration: 'optional - Lock frequency duration (defaults to 0.5)',
|
|
159
|
+
# strength_lock: 'optional - Strength lock (defaults to -60.0)'
|
|
160
|
+
# )
|
|
161
|
+
public_class_method def self.init_freq(opts = {})
|
|
162
|
+
gqrx_sock = opts[:gqrx_sock]
|
|
163
|
+
freq = opts[:freq]
|
|
164
|
+
demodulator_mode = opts[:demodulator_mode]
|
|
165
|
+
bandwidth = opts[:bandwidth]
|
|
166
|
+
lock_freq_duration = opts[:lock_freq_duration]
|
|
167
|
+
strength_lock = opts[:strength_lock]
|
|
168
|
+
|
|
169
|
+
demod_n_passband = gqrx_cmd(
|
|
170
|
+
gqrx_sock: gqrx_sock,
|
|
171
|
+
cmd: 'm'
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
change_freq_resp = gqrx_cmd(
|
|
175
|
+
gqrx_sock: gqrx_sock,
|
|
176
|
+
cmd: "F #{freq}",
|
|
177
|
+
resp_ok: 'RPRT 0'
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
current_freq = gqrx_cmd(
|
|
181
|
+
gqrx_sock: gqrx_sock,
|
|
182
|
+
cmd: 'f'
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
audio_gain_db = gqrx_cmd(
|
|
186
|
+
gqrx_sock: gqrx_sock,
|
|
187
|
+
cmd: 'l AF'
|
|
188
|
+
).to_f
|
|
189
|
+
|
|
190
|
+
current_strength = gqrx_cmd(
|
|
191
|
+
gqrx_sock: gqrx_sock,
|
|
192
|
+
cmd: 'l STRENGTH'
|
|
193
|
+
).to_f
|
|
194
|
+
|
|
195
|
+
current_squelch = gqrx_cmd(
|
|
196
|
+
gqrx_sock: gqrx_sock,
|
|
197
|
+
cmd: 'l SQL'
|
|
198
|
+
).to_f
|
|
199
|
+
|
|
200
|
+
rf_gain = gqrx_cmd(
|
|
201
|
+
gqrx_sock: gqrx_sock,
|
|
202
|
+
cmd: 'l RF_GAIN'
|
|
203
|
+
).to_f
|
|
204
|
+
|
|
205
|
+
if_gain = gqrx_cmd(
|
|
206
|
+
gqrx_sock: gqrx_sock,
|
|
207
|
+
cmd: 'l IF_GAIN'
|
|
208
|
+
).to_f
|
|
209
|
+
|
|
210
|
+
bb_gain = gqrx_cmd(
|
|
211
|
+
gqrx_sock: gqrx_sock,
|
|
212
|
+
cmd: 'l BB_GAIN'
|
|
213
|
+
).to_f
|
|
214
|
+
|
|
215
|
+
init_freq_hash = {
|
|
216
|
+
demod_mode_n_passband: demod_n_passband,
|
|
217
|
+
frequency: current_freq,
|
|
218
|
+
bandwidth: bandwidth,
|
|
219
|
+
audio_gain_db: audio_gain_db,
|
|
220
|
+
squelch: current_squelch,
|
|
221
|
+
rf_gain: rf_gain,
|
|
222
|
+
if_gain: if_gain,
|
|
223
|
+
bb_gain: bb_gain,
|
|
224
|
+
strength: current_strength,
|
|
225
|
+
strength_lock: strength_lock,
|
|
226
|
+
lock_freq_duration: lock_freq_duration
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
print '.'
|
|
230
|
+
sleep lock_freq_duration if current_strength > strength_lock
|
|
231
|
+
|
|
232
|
+
init_freq_hash
|
|
233
|
+
rescue StandardError => e
|
|
234
|
+
raise e
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Supported Method Parameters::
|
|
238
|
+
# PWN::Plugins::GQRX.scan_range(
|
|
239
|
+
# gqrx_sock: 'required - GQRX socket object returned from #connect method',
|
|
240
|
+
# demodulator_mode: 'required - Demodulator mode',
|
|
241
|
+
# bandwidth: 'required - Bandwidth',
|
|
242
|
+
# start_freq: 'required - Starting frequency',
|
|
243
|
+
# target_freq: 'required - Target frequency',
|
|
244
|
+
# precision: 'required - Precision',
|
|
245
|
+
# lock_freq_duration: 'optional - Lock frequency duration (defaults to 0.5)',
|
|
246
|
+
# strength_lock: 'optional - Strength lock (defaults to -60.0)'
|
|
247
|
+
# )
|
|
248
|
+
|
|
249
|
+
public_class_method def self.scan_range(opts = {})
|
|
250
|
+
gqrx_sock = opts[:gqrx_sock]
|
|
251
|
+
demodulator_mode = opts[:demodulator_mode]
|
|
252
|
+
bandwidth = opts[:bandwidth]
|
|
253
|
+
start_freq = opts[:start_freq]
|
|
254
|
+
target_freq = opts[:target_freq]
|
|
255
|
+
precision = opts[:precision]
|
|
256
|
+
lock_freq_duration = opts[:lock_freq_duration]
|
|
257
|
+
strength_lock = opts[:strength_lock]
|
|
258
|
+
|
|
259
|
+
multiplier = 10**(precision - 1)
|
|
260
|
+
prev_freq_hash = {
|
|
261
|
+
demod_mode_n_passband: demodulator_mode,
|
|
262
|
+
frequency: start_freq,
|
|
263
|
+
bandwidth: bandwidth,
|
|
264
|
+
audio_gain_db: 0.0,
|
|
265
|
+
squelch: 0.0,
|
|
266
|
+
rf_gain: 0.0,
|
|
267
|
+
if_gain: 0.0,
|
|
268
|
+
bb_gain: 0.0,
|
|
269
|
+
strength: 0.0,
|
|
270
|
+
strength_lock: strength_lock,
|
|
271
|
+
lock_freq_duration: lock_freq_duration
|
|
272
|
+
}
|
|
273
|
+
if start_freq > target_freq
|
|
274
|
+
start_freq.downto(target_freq) do |freq|
|
|
275
|
+
next unless (freq % multiplier).zero?
|
|
276
|
+
|
|
277
|
+
init_freq_hash = init_freq(
|
|
278
|
+
gqrx_sock: gqrx_sock,
|
|
279
|
+
freq: freq,
|
|
280
|
+
demodulator_mode: demodulator_mode,
|
|
281
|
+
bandwidth: bandwidth,
|
|
282
|
+
lock_freq_duration: lock_freq_duration,
|
|
283
|
+
strength_lock: strength_lock
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
current_strength = init_freq_hash[:strength]
|
|
287
|
+
prev_strength = prev_freq_hash[:strength]
|
|
288
|
+
prev_freq = prev_freq_hash[:frequency]
|
|
289
|
+
|
|
290
|
+
approaching_detection = true if current_strength > prev_strength &&
|
|
291
|
+
current_strength > strength_lock
|
|
292
|
+
if approaching_detection && current_strength <= prev_strength
|
|
293
|
+
puts "\n**** Found a signal ~ #{prev_freq} Hz ****"
|
|
294
|
+
puts JSON.pretty_generate(prev_freq_hash)
|
|
295
|
+
approaching_detection = false
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
prev_freq_hash = init_freq_hash
|
|
299
|
+
end
|
|
300
|
+
else
|
|
301
|
+
freq = start_freq
|
|
302
|
+
while freq <= target_freq
|
|
303
|
+
init_freq_hash = init_freq(
|
|
304
|
+
gqrx_sock: gqrx_sock,
|
|
305
|
+
demodulator_mode: demodulator_mode,
|
|
306
|
+
bandwidth: bandwidth,
|
|
307
|
+
freq: freq,
|
|
308
|
+
lock_freq_duration: lock_freq_duration,
|
|
309
|
+
strength_lock: strength_lock
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
current_strength = init_freq_hash[:strength]
|
|
313
|
+
prev_strength = prev_freq_hash[:strength]
|
|
314
|
+
prev_freq = prev_freq_hash[:frequency]
|
|
315
|
+
|
|
316
|
+
approaching_detection = true if current_strength > prev_strength &&
|
|
317
|
+
current_strength > strength_lock
|
|
318
|
+
if approaching_detection && current_strength < prev_strength
|
|
319
|
+
puts "\n**** Discovered a signal ~ #{prev_freq} Hz ****"
|
|
320
|
+
puts JSON.pretty_generate(prev_freq_hash)
|
|
321
|
+
approaching_detection = false
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
prev_freq_hash = init_freq_hash
|
|
325
|
+
|
|
326
|
+
freq += multiplier
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Supported Method Parameters::
|
|
332
|
+
# PWN::Plugins::GQRX.disconnect(
|
|
333
|
+
# gqrx_sock: 'required - GQRX socket object returned from #connect method'
|
|
334
|
+
# )
|
|
335
|
+
public_class_method def self.disconnect(opts = {})
|
|
336
|
+
gqrx_sock = opts[:gqrx_sock]
|
|
337
|
+
|
|
338
|
+
PWN::Plugins::Sock.disconnect(sock_obj: gqrx_sock)
|
|
339
|
+
rescue StandardError => e
|
|
340
|
+
raise e
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Author(s):: 0day Inc. <request.pentest@0dayinc.com>
|
|
344
|
+
|
|
345
|
+
public_class_method def self.authors
|
|
346
|
+
"AUTHOR(S):
|
|
347
|
+
0day Inc. <request.pentest@0dayinc.com>
|
|
348
|
+
"
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Display Usage for this Module
|
|
352
|
+
|
|
353
|
+
public_class_method def self.help
|
|
354
|
+
puts "USAGE:
|
|
355
|
+
|
|
356
|
+
#{self}.authors
|
|
357
|
+
"
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
data/lib/pwn/plugins.rb
CHANGED
|
@@ -28,6 +28,7 @@ module PWN
|
|
|
28
28
|
autoload :Fuzz, 'pwn/plugins/fuzz'
|
|
29
29
|
autoload :Git, 'pwn/plugins/git'
|
|
30
30
|
autoload :Github, 'pwn/plugins/github'
|
|
31
|
+
autoload :GQRX, 'pwn/plugins/gqrx'
|
|
31
32
|
autoload :HackerOne, 'pwn/plugins/hacker_one'
|
|
32
33
|
autoload :IBMAppscan, 'pwn/plugins/ibm_appscan'
|
|
33
34
|
autoload :IPInfo, 'pwn/plugins/ip_info'
|
data/lib/pwn/version.rb
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe PWN::Plugins::GQRX do
|
|
6
|
+
it 'should display information for authors' do
|
|
7
|
+
authors_response = PWN::Plugins::GQRX
|
|
8
|
+
expect(authors_response).to respond_to :authors
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'should display information for existing help method' do
|
|
12
|
+
help_response = PWN::Plugins::GQRX
|
|
13
|
+
expect(help_response).to respond_to :help
|
|
14
|
+
end
|
|
15
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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.76
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 0day Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-03-
|
|
11
|
+
date: 2024-03-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -968,14 +968,14 @@ dependencies:
|
|
|
968
968
|
requirements:
|
|
969
969
|
- - '='
|
|
970
970
|
- !ruby/object:Gem::Version
|
|
971
|
-
version: 0.
|
|
971
|
+
version: 0.123.0
|
|
972
972
|
type: :runtime
|
|
973
973
|
prerelease: false
|
|
974
974
|
version_requirements: !ruby/object:Gem::Requirement
|
|
975
975
|
requirements:
|
|
976
976
|
- - '='
|
|
977
977
|
- !ruby/object:Gem::Version
|
|
978
|
-
version: 0.
|
|
978
|
+
version: 0.123.0
|
|
979
979
|
- !ruby/object:Gem::Dependency
|
|
980
980
|
name: serialport
|
|
981
981
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -1758,6 +1758,7 @@ files:
|
|
|
1758
1758
|
- lib/pwn/plugins/fuzz.rb
|
|
1759
1759
|
- lib/pwn/plugins/git.rb
|
|
1760
1760
|
- lib/pwn/plugins/github.rb
|
|
1761
|
+
- lib/pwn/plugins/gqrx.rb
|
|
1761
1762
|
- lib/pwn/plugins/hacker_one.rb
|
|
1762
1763
|
- lib/pwn/plugins/ibm_appscan.rb
|
|
1763
1764
|
- lib/pwn/plugins/ip_info.rb
|
|
@@ -2087,6 +2088,7 @@ files:
|
|
|
2087
2088
|
- spec/lib/pwn/plugins/fuzz_spec.rb
|
|
2088
2089
|
- spec/lib/pwn/plugins/git_spec.rb
|
|
2089
2090
|
- spec/lib/pwn/plugins/github_spec.rb
|
|
2091
|
+
- spec/lib/pwn/plugins/gqrx_spec.rb
|
|
2090
2092
|
- spec/lib/pwn/plugins/hacker_one_spec.rb
|
|
2091
2093
|
- spec/lib/pwn/plugins/ibm_appscan_spec.rb
|
|
2092
2094
|
- spec/lib/pwn/plugins/ip_info_spec.rb
|