pwn 0.5.510 → 0.5.513

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d58d781b8c20f486edb8e75e754abf3ecef32c5452e37b95db9cdc42d99b00c5
4
- data.tar.gz: 3ad4a359bc29b32b0075ab742251049f29ee7fc0e71404205cb799fd3674703b
3
+ metadata.gz: 82756fa89b985658783dd6a2f59ec1e4c591959dd0ea349e8f68ca5e74fc8715
4
+ data.tar.gz: 34f2cc9edcc4eb8a35ad7d91b16fbb760d4a220a3a9d556efe682f009573abcf
5
5
  SHA512:
6
- metadata.gz: 38e23042f38a7f1774e67f1bda23b3e77a9ff1d2242aee7c59d47957cddff06ec831d3fee6fec16681795bab6e450fb178bbb705e1d44a3624d1c8260d55625b
7
- data.tar.gz: 1edb0154984ee110ef2b4d87eddf5f007a98d7977f50cb33fbb1b735d79fc64725ba75bfff288822bbdd723bf893ba25f3022b9aff1094ec4a068dd953cae2ef
6
+ metadata.gz: 9bf3f13770d43feeab86c59ab32b389a02e11727b888e7a002248dd8754769c00c1e5e2a335c316db1d1ccc1ad846fc9a39d463bde22a0e9c3f467a69e2d4e55
7
+ data.tar.gz: db2d0be522131762b6214e6fb3e4e79500bf4b8bfb25510785ac0eaa3eca81c0984c58d9a7a1dc9d195c1d8873b2a1cc559f0ff0a2085d9f3217341fb03042a3
data/.rubocop.yml CHANGED
@@ -16,7 +16,7 @@ Metrics/ClassLength:
16
16
  Metrics/CyclomaticComplexity:
17
17
  Max: 158
18
18
  Metrics/MethodLength:
19
- Max: 652
19
+ Max: 733
20
20
  Metrics/ModuleLength:
21
21
  Max: 1563
22
22
  Metrics/PerceivedComplexity:
data/Gemfile CHANGED
@@ -20,7 +20,7 @@ gem 'base32', '0.3.4'
20
20
  gem 'bitcoin-ruby', '0.0.20'
21
21
  gem 'brakeman', '7.1.1'
22
22
  gem 'bson', '5.2.0'
23
- gem 'bundler', '>=4.0.0'
23
+ gem 'bundler', '>=4.0.2'
24
24
  gem 'bundler-audit', '>=0.9.3'
25
25
  gem 'bunny', '2.24.0'
26
26
  gem 'colorize', '1.1.0'
@@ -44,12 +44,12 @@ gem 'jenkins_api_client2', '1.9.0'
44
44
  gem 'js-beautify', '0.1.8'
45
45
  gem 'json', '>=2.13.2'
46
46
  gem 'jsonpath', '1.1.5'
47
- gem 'json_schemer', '2.4.0'
47
+ gem 'json_schemer', '2.5.0'
48
48
  gem 'jwt', '3.1.2'
49
49
  gem 'libusb', '0.7.2'
50
50
  gem 'luhn', '3.0.0'
51
51
  gem 'mail', '2.9.0'
52
- gem 'meshtastic', '0.0.147'
52
+ gem 'meshtastic', '0.0.149'
53
53
  gem 'metasm', '1.0.5'
54
54
  gem 'mongo', '2.22.0'
55
55
  gem 'msfrpc-client', '1.1.2'
@@ -73,14 +73,14 @@ gem 'pry-doc', '1.6.0'
73
73
  gem 'rake', '13.3.1'
74
74
  gem 'rb-readline', '0.5.5'
75
75
  gem 'rbvmomi2', '3.8.0'
76
- gem 'rdoc', '6.16.1'
76
+ gem 'rdoc', '7.0.1'
77
77
  gem 'rest-client', '2.1.0'
78
78
  gem 'rex', '2.0.13'
79
79
  gem 'rmagick', '6.1.4'
80
80
  gem 'rqrcode', '3.1.1'
81
81
  gem 'rspec', '3.13.2'
82
82
  gem 'rtesseract', '3.1.4'
83
- gem 'rubocop', '1.81.7'
83
+ gem 'rubocop', '1.82.0'
84
84
  gem 'rubocop-rake', '0.7.1'
85
85
  gem 'rubocop-rspec', '3.8.0'
86
86
  gem 'ruby-audio', '1.6.1'
@@ -88,8 +88,8 @@ gem 'ruby-nmap', '1.0.3'
88
88
  gem 'ruby-saml', '1.18.1'
89
89
  gem 'rvm', '1.11.3.9'
90
90
  gem 'savon', '2.15.1'
91
- gem 'selenium-devtools', '0.142.0'
92
- gem 'selenium-webdriver', '4.38.0'
91
+ gem 'selenium-devtools', '0.143.0'
92
+ gem 'selenium-webdriver', '4.39.0'
93
93
  gem 'slack-ruby-client', '3.1.0'
94
94
  gem 'socksify', '1.8.1'
95
95
  gem 'spreadsheet', '1.3.4'
@@ -104,4 +104,4 @@ gem 'webrick', '1.9.2'
104
104
  gem 'whois', '6.0.3'
105
105
  gem 'whois-parser', '2.0.0'
106
106
  gem 'wicked_pdf', '2.8.2'
107
- gem 'yard', '0.9.37'
107
+ gem 'yard', '0.9.38'
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.510]:001 >>> PWN.help
40
+ pwn[v0.5.513]:001 >>> PWN.help
41
41
  ```
42
42
 
43
43
  [![Installing the pwn Security Automation Framework](https://raw.githubusercontent.com/0dayInc/pwn/master/documentation/pwn_install.png)](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.510]:001 >>> PWN.help
55
+ pwn[v0.5.513]: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.510]:001 >>> PWN.help
65
+ pwn[v0.5.513]: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
@@ -13,12 +13,12 @@ PWN::Driver::Parser.new do |options|
13
13
  opts[:list_scan_profiles] = l
14
14
  end
15
15
 
16
- options.on('-tFREQ', '--target-freq=FREQ', '<Required if "--assume-profile" is Nil - Frequency to Conclude Scanning (e.g. 900.000.000 == 900 mHz>') do |e|
17
- opts[:target_freq] = e
16
+ options.on('-sFREQ', '--start-freq=FREQ', '<Required if "--assume-profile" is Nil - Frequency to Start Scanning (e.g. 800.000.000 == 800 mHz>') do |s|
17
+ opts[:start_freq] = s
18
18
  end
19
19
 
20
- options.on('-sFREQ', '--start-freq=FREQ', '<Optional - Frequency to Set when Scanning Begins (Defaults to last known frequency)>') do |s|
21
- opts[:start_freq] = s
20
+ options.on('-tFREQ', '--target-freq=FREQ', '<Required if "--assume-profile" is Nil - Frequency to Conclude Scanning (e.g. 900.000.000 == 900 mHz>') do |e|
21
+ opts[:target_freq] = e
22
22
  end
23
23
 
24
24
  options.on('-hHOST', '--host=HOST', '<Optional - GQRX Host (Defaults to 127.0.0.1)>') do |h|
@@ -29,7 +29,7 @@ PWN::Driver::Parser.new do |options|
29
29
  opts[:port] = p
30
30
  end
31
31
 
32
- options.on('-AFLOAT', '--audio-gain=FLOAT', '<Optional - Set audio gain -80.0 to 50.0 (Defaults to 1.0)>') do |a|
32
+ options.on('-AFLOAT', '--audio-gain=FLOAT', '<Optional - Set audio gain -80.0 to 50.0 (Defaults to 9.0)>') do |a|
33
33
  opts[:audio_gain_db] = a
34
34
  end
35
35
 
@@ -37,52 +37,74 @@ PWN::Driver::Parser.new do |options|
37
37
  opts[:bandwidth] = b
38
38
  end
39
39
 
40
+ options.on('-OLAP', '--overlap-protection', '<Optional - Enable Overlap Protection to prevent false positives when scanning (Defaults to false)>') do |o|
41
+ opts[:overlap_protection] = o
42
+ end
43
+
40
44
  options.on('-DMODE', '--demodulator-mode=MODE', '<Optional - Set Demodulator Mode OFF | RAW | AM | FM | WFM | WFM_ST | WFM_ST_OIRT | LSB | USB | CW | CWL | CWU (Defaults to WFM_ST)>') do |d|
41
45
  opts[:demodulator_mode] = d
42
46
  end
43
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
+
44
52
  options.on('-PINT', '--precision=INT', '<Optional - Precision of Frequency 1-12 (Defaults to 5)>') do |p|
45
53
  opts[:precision] = p
46
54
  end
47
55
 
48
- options.on('-SFLOAT', '--strength-lock=FLOAT', '<Optional - Strength to lock onto frequency (Defaults to -45.0)>') do |s|
56
+ options.on('-SFLOAT', '--strength-lock=FLOAT', '<Optional - Strength to lock onto frequency (Defaults to -70.0)>') do |s|
49
57
  opts[:strength_lock] = s
50
58
  end
51
59
 
52
- options.on('-LFLOAT', '--lock-freq-duration=FLOAT', '<Optional - Duration to lock onto Freqency when Strength < --strength-lock value (Defaults to 0.5)>') do |l|
60
+ options.on('-LFLOAT', '--lock-freq-duration=FLOAT', '<Optional - Duration to lock onto Freqency when Strength < --strength-lock value (Defaults to 0.04)>') do |l|
53
61
  opts[:lock_freq_duration] = l
54
62
  end
55
63
 
56
- options.on('-QFLOAT', '--squelch=FLOAT', '<Optional - Squelch Threshold -150.0 to 0 (Defaults to -50.0)>') do |q|
64
+ options.on('-QFLOAT', '--squelch=FLOAT', '<Optional - Squelch Threshold -150.0 to 0 (Defaults to --strength-lock - 3)>') do |q|
57
65
  opts[:squelch] = q
58
66
  end
59
67
 
60
- options.on('-RFLOAT', '--rf-gain=FLOAT', '<Optional - RF Gain 0.0-16.0(Defaults to 16.0)>') do |r|
68
+ options.on('-RFLOAT', '--rf-gain=FLOAT', '<Optional - RF Gain 0.0-16.0(Defaults to 0.0)>') do |r|
61
69
  opts[:rf_gain] = r
62
70
  end
63
71
 
64
- options.on('-IFLOAT', '--intermediate-gain=FLOAT', '<Optional - Intermediate Gain 0.0-40.0 (Defaults to 40.0)>') do |i|
72
+ options.on('-IFLOAT', '--intermediate-gain=FLOAT', '<Optional - Intermediate Gain 0.0-40.0 (Defaults to 32.0)>') do |i|
65
73
  opts[:intermediate_gain] = i
66
74
  end
67
75
 
68
- options.on('-BFLOAT', '--basedband-gain=FLOAT', '<Optional - Baseband Gain 0.0-62.0 (Defaults to 10.0)>') do |b|
76
+ options.on('-BFLOAT', '--baseband-gain=FLOAT', '<Optional - Baseband Gain 0.0-62.0 (Defaults to 10.0)>') do |b|
69
77
  opts[:baseband_gain] = b
70
78
  end
79
+
80
+ options.on('-LFILE', '--scan-log=FILE', '<Optional - Path to log scan results to (Defaults to /tmp/pwn_sdr_gqrx_scan_<start_freq>-<target_freq>_<timestamp>.json)>') do |l|
81
+ opts[:scan_log] = l
82
+ end
83
+
84
+ options.on('-LOC', '--location', '<Optional - Location string to include in AI analysis (e.g. "New York, NY", 90210, GPS coords, etc.)>') do |l|
85
+ opts[:location] = l
86
+ end
71
87
  end.parse!
72
88
 
73
89
  begin
74
90
  pwn_provider = 'ruby-gem'
75
91
  pwn_provider = ENV.fetch('PWN_PROVIDER') if ENV.keys.any? { |s| s == 'PWN_PROVIDER' }
76
92
 
93
+ profiles_available = PWN::SDR::FrequencyAllocation.profiles
94
+
77
95
  list_scan_profiles = opts[:list_scan_profiles]
78
96
  if list_scan_profiles
79
- profiles_available = PWN::SDR::GQRX.list_scan_profiles
80
97
  puts JSON.pretty_generate(profiles_available)
81
98
  exit 0
82
99
  end
83
100
 
84
101
  profile = opts[:profile]
85
- opts = PWN::SDR::GQRX.assume_profile(profile: profile) unless profile.nil?
102
+ opts.merge!(profiles_available[profile.to_sym]) unless profile.nil?
103
+
104
+ start_freq = opts[:start_freq]
105
+ start_freq = start_freq.to_s.delete('.') unless start_freq.nil?
106
+ start_freq = start_freq.to_i
107
+ raise 'ERROR: Invalid start frequency.' if !start_freq.nil? && start_freq.zero?
86
108
 
87
109
  target_freq = opts[:target_freq]
88
110
  target_freq = target_freq.to_s.delete('.') unless target_freq.nil?
@@ -91,106 +113,64 @@ begin
91
113
 
92
114
  host = opts[:host]
93
115
  port = opts[:port]
94
-
95
- puts "Connecting to GQRX at #{host}:#{port}..."
96
116
  gqrx_sock = PWN::SDR::GQRX.connect(target: host, port: port)
97
117
 
98
- start_freq = opts[:start_freq]
99
- start_freq = start_freq.to_s.delete('.') unless start_freq.nil?
100
- start_freq = start_freq.to_i
101
- if start_freq.zero?
102
- start_freq = PWN::SDR::GQRX.gqrx_cmd(
103
- gqrx_sock: gqrx_sock, cmd: 'f',
104
- resp_ok: 'RPRT 0'
105
- ).to_i
106
- end
107
-
108
- demodulator_mode = opts[:demodulator_mode] ||= 'WFM_ST'
109
- puts "Demodulator Mode: #{demodulator_mode}"
110
- # demodulator_mode.upcase! if opts[:demodulator_mode]
111
- demodulator_modes = %i[OFF RAW AM FM WFM WFM_ST WFM_ST_OIRT LSB USB CW CWL CWU]
112
- raise "ERROR: Invalid demodulator mode: #{demodulator_mode}" unless demodulator_modes.include?(demodulator_mode)
113
-
114
- bandwidth = opts[:bandwidth] ||= '200.000'
115
-
116
- puts "Setting demodulator mode to #{demodulator_mode} and bandwidth to #{bandwidth}..."
117
- bandwidth = bandwidth.to_s.delete('.').to_i unless bandwidth.nil?
118
- demod_resp = PWN::SDR::GQRX.gqrx_cmd(
119
- gqrx_sock: gqrx_sock,
120
- cmd: "M #{demodulator_mode} #{bandwidth}",
121
- resp_ok: 'RPRT 0'
122
- )
118
+ demodulator_mode = opts[:demodulator_mode]
119
+ rds = opts[:rds]
120
+ bandwidth = opts[:bandwidth]
121
+ overlap_protection = opts[:overlap_protection]
123
122
 
124
- audio_gain_db = opts[:audio_gain_db] ||= 1.0
125
- audio_gain_db = audio_gain_db.to_f
126
- audio_gain_db_resp = PWN::SDR::GQRX.gqrx_cmd(
127
- gqrx_sock: gqrx_sock,
128
- cmd: "L AF #{audio_gain_db}",
129
- resp_ok: 'RPRT 0'
130
- )
123
+ audio_gain_db = opts[:audio_gain_db]
124
+ audio_gain_db = audio_gain_db.to_f unless audio_gain_db.nil?
131
125
 
132
- squelch = opts[:squelch] ||= -63.0
133
- squelch = squelch.to_f
134
- squelch_resp = PWN::SDR::GQRX.gqrx_cmd(
135
- gqrx_sock: gqrx_sock,
136
- cmd: "L SQL #{squelch}",
137
- resp_ok: 'RPRT 0'
138
- )
126
+ squelch = opts[:squelch]
127
+ squelch = squelch.to_f unless squelch.nil?
139
128
 
140
129
  precision = opts[:precision] ||= 5
141
130
  precision = precision.to_i
142
131
  raise "ERROR: Invalid precision: #{precision}" unless (1..12).include?(precision)
143
132
 
144
- lock_freq_duration = opts[:lock_freq_duration] ||= 0.5
145
- lock_freq_duration = lock_freq_duration.to_f
133
+ lock_freq_duration = opts[:lock_freq_duration]
134
+ strength_lock = opts[:strength_lock]
135
+ strength_lock = strength_lock.to_f unless strength_lock.nil?
146
136
 
147
- strength_lock = opts[:strength_lock] ||= -60.0
148
- strength_lock = strength_lock.to_f
137
+ rf_gain = opts[:rf_gain]
138
+ rf_gain = rf_gain.to_f unless rf_gain.nil?
149
139
 
150
- rf_gain = opts[:rf_gain] ||= 0.0
151
- rf_gain = rf_gain.to_f
152
- rf_gain_resp = PWN::SDR::GQRX.gqrx_cmd(
153
- gqrx_sock: gqrx_sock,
154
- cmd: "L RF_GAIN #{rf_gain}",
155
- resp_ok: 'RPRT 0'
156
- )
140
+ intermediate_gain = opts[:intermediate_gain]
141
+ intermediate_gain = intermediate_gain.to_f unless intermediate_gain.nil?
157
142
 
158
- intermediate_gain = opts[:intermediate_gain] ||= 32.0
159
- intermediate_gain = intermediate_gain.to_f
160
- intermediate_resp = PWN::SDR::GQRX.gqrx_cmd(
161
- gqrx_sock: gqrx_sock,
162
- cmd: "L IF_GAIN #{intermediate_gain}",
163
- resp_ok: 'RPRT 0'
164
- )
165
-
166
- baseband_gain = opts[:baseband_gain] ||= 10.0
167
- baseband_gain = baseband_gain.to_f
168
- baseband_resp = PWN::SDR::GQRX.gqrx_cmd(
169
- gqrx_sock: gqrx_sock,
170
- cmd: "L BB_GAIN #{baseband_gain}",
171
- resp_ok: 'RPRT 0'
172
- )
143
+ baseband_gain = opts[:baseband_gain]
144
+ baseband_gain = baseband_gain.to_f unless baseband_gain.nil?
173
145
 
174
- s_freq_pretty = start_freq.to_s.chars.insert(-4, '.').insert(-8, '.').join
175
- t_freq_pretty = target_freq.to_s.chars.insert(-4, '.').insert(-8, '.').join
176
- puts "*** Scanning from #{s_freq_pretty} to #{t_freq_pretty}\n\n\n"
146
+ scan_log = opts[:scan_log]
147
+ location = opts[:location]
177
148
 
149
+ # Merge opts again to ensure we override profile values with CLI values
178
150
  PWN::SDR::GQRX.scan_range(
179
151
  gqrx_sock: gqrx_sock,
180
- demodulator_mode: demodulator_mode,
181
- bandwidth: bandwidth,
182
152
  start_freq: start_freq,
183
153
  target_freq: target_freq,
154
+ demodulator_mode: demodulator_mode,
155
+ rds: rds,
156
+ bandwidth: bandwidth,
157
+ overlap_protection: overlap_protection,
158
+ audio_gain_db: audio_gain_db,
159
+ squelch: squelch,
184
160
  precision: precision,
185
- lock_freq_duration: lock_freq_duration,
186
161
  strength_lock: strength_lock,
187
- squelch: squelch
162
+ lock_freq_duration: lock_freq_duration,
163
+ rf_gain: rf_gain,
164
+ intermediate_gain: intermediate_gain,
165
+ baseband_gain: baseband_gain,
166
+ scan_log: scan_log,
167
+ location: location
188
168
  )
189
169
  puts 'Scan Complete.'
190
170
  rescue StandardError => e
191
171
  raise e
192
172
  rescue Interrupt, SystemExit
193
- puts "\nGoodbye."
173
+ puts "\nCTRL+C detected - goodbye."
194
174
  ensure
195
- gqrx_sock = PWN::SDR::GQRX.disconnect(gqrx_sock: gqrx_sock) unless gqrx_sock.nil?
175
+ PWN::SDR::GQRX.disconnect(gqrx_sock: gqrx_sock) unless gqrx_sock.closed?
196
176
  end
@@ -18,7 +18,7 @@ rvmsudo gem update --system
18
18
 
19
19
  if [[ $old_ruby_version == $new_ruby_version ]]; then
20
20
  export rvmsudo_secure_path=1
21
- rvmsudo /bin/bash --login -c "cd ${pwn_root} && ./reinstall_pwn_gemset.sh"
21
+ rvmsudo /bin/bash --login -c "cd ${pwn_root} && ./reinstall_gemset.sh"
22
22
  cd /tmp && cd $pwn_root
23
23
  rvmsudo rake
24
24
  rvmsudo rake install
@@ -11,9 +11,9 @@ if (( $# == 3 )); then
11
11
  git pull
12
12
  git add . --all
13
13
  echo 'Updating Gems to Latest Versions in Gemfile...'
14
- ./find_latest_gem_versions_per_Gemfile.sh
14
+ ./upgrade_Gemfile_gems.sh
15
15
  if [[ $? -ne 0 ]]; then
16
- echo 'ERROR: find_latest_gem_versions_per_Gemfile.sh failed!'
16
+ echo 'ERROR: upgrade_Gemfile_gems.sh failed!'
17
17
  exit 1
18
18
  fi
19
19
 
@@ -34,9 +34,9 @@ if (( $# == 3 )); then
34
34
  fi
35
35
 
36
36
  git commit -a -S --author="${1} <${2}>" -m "${3}"
37
- ./update_pwn.sh
37
+ ./upgrade_pwn.sh
38
38
  if [[ $? -ne 0 ]]; then
39
- echo 'ERROR: update_pwn.sh failed!'
39
+ echo 'ERROR: upgrade_pwn.sh failed!'
40
40
  exit 1
41
41
  fi
42
42
 
@@ -229,10 +229,20 @@ module PWN
229
229
 
230
230
  public_class_method def self.disconnect(opts = {})
231
231
  sock_obj = opts[:sock_obj]
232
+ return unless sock_obj.respond_to?(:close)
233
+
234
+ # Shutdown both directions to terminate flows immediately
235
+ # sock_obj.shutdown(Socket::SHUT_RDWR)
236
+
237
+ # Set SO_LINGER=0 to force RST (skips TIME_WAIT; ideal for fuzzing)
238
+ # linger = [1, 0].pack('ii')
239
+ # sock_obj.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, linger)
240
+
232
241
  sock_obj.close
233
- sock_obj = nil
234
242
  rescue StandardError => e
235
243
  raise e
244
+ ensure
245
+ sock_obj = nil
236
246
  end
237
247
 
238
248
  # Author(s):: 0day Inc. <support@0dayinc.com>
@@ -15,15 +15,13 @@ module PWN
15
15
 
16
16
  # Starts the live decoding thread.
17
17
  def self.start(opts = {})
18
- gqrx_obj = opts[:gqrx_obj]
19
- raise ':ERROR: :gqrx_obj is required' unless gqrx_obj.is_a?(Hash)
18
+ freq_obj = opts[:freq_obj]
19
+ raise ':ERROR: :freq_obj is required' unless freq_obj.is_a?(Hash)
20
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]
21
+ gqrx_sock = freq_obj[:gqrx_sock]
22
+ freq = freq_obj[:freq]
23
+ bandwidth = freq_obj[:bandwidth].to_i
24
+ record_path = freq_obj[:record_path]
27
25
 
28
26
  sleep 0.1 until File.exist?(record_path)
29
27
 
@@ -32,12 +30,10 @@ module PWN
32
30
 
33
31
  bytes_read = HEADER_SIZE
34
32
 
35
- puts "GSM Decoder started for frequency: #{frequency}, sample_rate: #{sample_rate}"
33
+ puts "GSM Decoder started for freq: #{freq}, bandwidth: #{bandwidth}"
36
34
 
37
35
  Thread.new do
38
36
  loop do
39
- break if gqrx_obj[:decoder_stop_flag][0]
40
-
41
37
  current_size = File.size(record_path)
42
38
  if current_size > bytes_read
43
39
  new_bytes = current_size - bytes_read
@@ -46,8 +42,8 @@ module PWN
46
42
  data = File.binread(record_path, new_bytes, bytes_read)
47
43
  process_chunk(
48
44
  data: data,
49
- sample_rate: sample_rate,
50
- frequency: frequency
45
+ bandwidth: bandwidth,
46
+ freq: freq
51
47
  )
52
48
  bytes_read = current_size
53
49
  end
@@ -63,12 +59,11 @@ module PWN
63
59
 
64
60
  # Stops the decoding thread.
65
61
  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)
62
+ freq_obj = opts[:freq_obj]
63
+ raise 'ERROR: :freq_obj is required' unless freq_obj.is_a?(Hash)
69
64
 
70
- gqrx_obj[:decoder_stop_flag][0] = true
71
- thread.join(1.0)
65
+ decoder_thread = freq_obj[:decoder_thread]
66
+ decoder_thread.kill if decoder_thread.is_a?(Thread)
72
67
  end
73
68
 
74
69
  class << self
@@ -76,9 +71,9 @@ module PWN
76
71
 
77
72
  def process_chunk(opts = {})
78
73
  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
74
+ bandwidth = opts[:bandwidth]
75
+ freq = opts[:freq]
76
+ raise ':ERROR: :data, :bandwidth, and :freq are required' unless data && bandwidth && freq
82
77
 
83
78
  samples = data.unpack('f< *')
84
79
  return if samples.length.odd? # Skip incomplete
@@ -88,7 +83,7 @@ module PWN
88
83
  complex_samples << Complex(samples[i], samples[i + 1])
89
84
  end
90
85
 
91
- window_size = [(sample_rate * BURST_DURATION_SEC).round, complex_samples.length].min
86
+ window_size = [(bandwidth * BURST_DURATION_SEC).round, complex_samples.length].min
92
87
  return if window_size <= 0
93
88
 
94
89
  # Simplified power on sliding windows
@@ -112,10 +107,10 @@ module PWN
112
107
  return unless burst_start >= 0 && burst_start + 148 <= bits.length
113
108
 
114
109
  data_bits = extract_data_bits(bits, burst_start)
115
- puts "Burst synchronized at offset #{sync_offset} for #{frequency} Hz (power: #{max_power.round(4)})"
110
+ puts "Burst synchronized at offset #{sync_offset} for #{freq} Hz (power: #{max_power.round(4)})"
116
111
  decode_imsi(
117
112
  data_bits: data_bits,
118
- frequency: frequency
113
+ freq: freq
119
114
  )
120
115
  end
121
116
 
@@ -159,8 +154,8 @@ module PWN
159
154
 
160
155
  def decode_imsi(opts = {})
161
156
  data_bits = opts[:data_bits]
162
- frequency = opts[:frequency]
163
- raise ':ERROR: :data_bits and :frequency are required' unless data_bits && frequency
157
+ freq = opts[:freq]
158
+ raise ':ERROR: :data_bits and :freq are required' unless data_bits && freq
164
159
 
165
160
  # Simplified "IMSI extraction": Interpret first ~60 bits as packed digits (4 bits per digit, BCD-like).
166
161
  # In reality: Deinterleave (over bursts), Viterbi decode convolutional code (polys G0=10011b, G1=11011b),
@@ -180,7 +175,7 @@ module PWN
180
175
  msin = imsi_digits[6, 9].join
181
176
  imsi = "#{mcc.ljust(3, '0')}#{mnc.ljust(3, '0')}#{msin.ljust(9, '0')}"
182
177
 
183
- puts "Decoded IMSI: #{imsi} at #{frequency} Hz"
178
+ puts "Decoded IMSI: #{imsi} at #{freq} Hz"
184
179
  # TODO: Integrate full L3 parser (e.g., from ruby-gsm gem or custom).
185
180
  end
186
181
 
@@ -208,18 +203,12 @@ module PWN
208
203
  public_class_method def self.help
209
204
  puts "USAGE:
210
205
  gsm_decoder_thread = PWN::SDR::Decoder::GSM.start(
211
- gqrx_obj: {
212
- record_path: 'path/to/record.wav',
213
- frequency: 935_000_000, # Frequency in Hz
214
- bandwidth: 200_000, # Sample rate in Hz
215
- gqrx_sock: gqrx_socket_object
216
- }
206
+ freq_obj: 'required - freq_obj returned from PWN::SDR::Receiver::GQRX.init_freq method'
217
207
  )
218
208
 
219
209
  # To stop the decoder thread:
220
210
  PWN::SDR::Decoder::GSM.stop(
221
- thread: gsm_decoder_thread,
222
- gqrx_obj: gqrx_object_used_to_start_decoder
211
+ freq_obj: 'required - freq_obj returned from PWN::SDR::Receiver::GQRX.init_freq method'
223
212
  )
224
213
 
225
214
  #{self}.authors