ai_root_shield 0.1.0

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.
@@ -0,0 +1,93 @@
1
+ {
2
+ "platform": "android",
3
+ "system_info": {
4
+ "os_version": "Android 11",
5
+ "kernel_version": "4.19.95-g0123456789ab",
6
+ "build_fingerprint": "google/flame/flame:11/RQ3A.210905.001/7511028:user/test-keys",
7
+ "bootloader_status": "unlocked",
8
+ "selinux_status": "permissive",
9
+ "developer_options": true
10
+ },
11
+ "installed_packages": [
12
+ {
13
+ "name": "com.topjohnwu.magisk",
14
+ "signature": "test-keys"
15
+ },
16
+ {
17
+ "name": "eu.chainfire.supersu",
18
+ "signature": "debug.keystore"
19
+ },
20
+ {
21
+ "name": "com.android.chrome",
22
+ "signature": "platform.pk8"
23
+ }
24
+ ],
25
+ "file_system": {
26
+ "suspicious_files": [
27
+ "/system/bin/su",
28
+ "/system/xbin/su",
29
+ "/system/app/Superuser.apk",
30
+ "/data/local/tmp/frida-server"
31
+ ],
32
+ "system_binaries": [
33
+ "/system/bin/magisk",
34
+ "/system/xbin/daemonsu"
35
+ ],
36
+ "writable_system_dirs": [
37
+ "/system/app"
38
+ ]
39
+ },
40
+ "running_processes": [
41
+ {
42
+ "name": "magisk",
43
+ "pid": 1234
44
+ },
45
+ {
46
+ "name": "frida-server",
47
+ "pid": 5678,
48
+ "loaded_libraries": [
49
+ "libfrida-gadget.so"
50
+ ]
51
+ }
52
+ ],
53
+ "network": {
54
+ "proxy_settings": {
55
+ "enabled": true,
56
+ "host": "127.0.0.1",
57
+ "port": 8080
58
+ },
59
+ "vpn_active": false,
60
+ "certificates": [
61
+ {
62
+ "subject": "CN=PortSwigger CA, O=PortSwigger, L=Knaresborough",
63
+ "issuer": "CN=PortSwigger CA, O=PortSwigger, L=Knaresborough",
64
+ "user_installed": true
65
+ }
66
+ ]
67
+ },
68
+ "security": {
69
+ "screen_lock_enabled": true,
70
+ "encryption_enabled": true,
71
+ "unknown_sources": true,
72
+ "usb_debugging": true
73
+ },
74
+ "hardware": {
75
+ "device_model": "Pixel 4",
76
+ "manufacturer": "Google",
77
+ "sensors": ["accelerometer", "gyroscope", "magnetometer"],
78
+ "baseband_version": "g7250-00042-200421-B-6404797",
79
+ "serial_number": "HT1ABC123456"
80
+ },
81
+ "certificates": [
82
+ {
83
+ "subject": "CN=Android Debug, O=Android, C=US",
84
+ "issuer": "CN=Android Debug, O=Android, C=US",
85
+ "not_after": "2025-01-01T00:00:00Z"
86
+ }
87
+ ],
88
+ "system_logs": [
89
+ "magisk: Magisk v23.0 daemon started",
90
+ "frida: Frida gadget loaded successfully",
91
+ "xposed: Xposed framework initialized"
92
+ ]
93
+ }
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/ai_root_shield"
5
+ require "optparse"
6
+ require "json"
7
+
8
+ # CLI interface for AI Root Shield
9
+ class AiRootShieldCLI
10
+ def initialize
11
+ @options = {
12
+ config: {},
13
+ output_format: "json",
14
+ verbose: false
15
+ }
16
+ end
17
+
18
+ def run(args)
19
+ parse_options(args)
20
+
21
+ if args.empty?
22
+ puts "Error: Please provide a device logs file path"
23
+ puts "Usage: ai_root_shield [options] <device_logs.json>"
24
+ exit 1
25
+ end
26
+
27
+ device_logs_path = args.first
28
+
29
+ unless File.exist?(device_logs_path)
30
+ puts "Error: Device logs file not found: #{device_logs_path}"
31
+ exit 1
32
+ end
33
+
34
+ begin
35
+ result = AiRootShield.scan_device_with_config(device_logs_path, @options[:config])
36
+ output_result(result)
37
+ rescue AiRootShield::Error => e
38
+ puts "Error: #{e.message}"
39
+ exit 1
40
+ rescue StandardError => e
41
+ puts "Unexpected error: #{e.message}"
42
+ puts e.backtrace if @options[:verbose]
43
+ exit 1
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def parse_options(args)
50
+ OptionParser.new do |opts|
51
+ opts.banner = "Usage: ai_root_shield [options] <device_logs.json>"
52
+ opts.separator ""
53
+ opts.separator "Options:"
54
+
55
+ opts.on("-f", "--format FORMAT", ["json", "text", "summary"],
56
+ "Output format (json, text, summary)") do |format|
57
+ @options[:output_format] = format
58
+ end
59
+
60
+ opts.on("-v", "--verbose", "Enable verbose output") do
61
+ @options[:verbose] = true
62
+ end
63
+
64
+ opts.on("-t", "--threshold SCORE", Integer,
65
+ "Risk threshold (0-100, default: 50)") do |threshold|
66
+ @options[:config][:risk_threshold] = threshold
67
+ end
68
+
69
+ opts.on("--no-root", "Disable root detection") do
70
+ @options[:config][:enable_root_detection] = false
71
+ end
72
+
73
+ opts.on("--no-emulator", "Disable emulator detection") do
74
+ @options[:config][:enable_emulator_detection] = false
75
+ end
76
+
77
+ opts.on("--no-hooking", "Disable hooking detection") do
78
+ @options[:config][:enable_hooking_detection] = false
79
+ end
80
+
81
+ opts.on("--no-integrity", "Disable integrity checks") do
82
+ @options[:config][:enable_integrity_checks] = false
83
+ end
84
+
85
+ opts.on("--no-network", "Disable network analysis") do
86
+ @options[:config][:enable_network_analysis] = false
87
+ end
88
+
89
+ opts.on("-h", "--help", "Show this help message") do
90
+ puts opts
91
+ exit
92
+ end
93
+
94
+ opts.on("--version", "Show version") do
95
+ puts "AI Root Shield v#{AiRootShield::VERSION}"
96
+ exit
97
+ end
98
+ end.parse!(args)
99
+ end
100
+
101
+ def output_result(result)
102
+ case @options[:output_format]
103
+ when "json"
104
+ puts JSON.pretty_generate(result)
105
+ when "text"
106
+ output_text_format(result)
107
+ when "summary"
108
+ output_summary_format(result)
109
+ end
110
+ end
111
+
112
+ def output_text_format(result)
113
+ puts "AI Root Shield Security Scan Results"
114
+ puts "=" * 40
115
+ puts "Risk Score: #{result[:risk_score]}/100 (#{AiRootShield::RiskCalculator.risk_level_description(result[:risk_score])})"
116
+ puts "Scan Time: #{Time.at(result[:timestamp]).strftime('%Y-%m-%d %H:%M:%S')}"
117
+ puts "Version: #{result[:version]}"
118
+ puts ""
119
+
120
+ if result[:factors].any?
121
+ puts "Detected Security Factors:"
122
+ result[:factors].each do |factor|
123
+ puts " • #{factor.gsub('_', ' ').downcase.capitalize}"
124
+ end
125
+ puts ""
126
+
127
+ recommended_actions = AiRootShield::RiskCalculator.recommended_actions(result[:factors])
128
+ if recommended_actions.any?
129
+ puts "Recommended Actions:"
130
+ recommended_actions.each do |action|
131
+ puts " → #{action}"
132
+ end
133
+ end
134
+ else
135
+ puts "No security threats detected."
136
+ end
137
+ end
138
+
139
+ def output_summary_format(result)
140
+ risk_level = AiRootShield::RiskCalculator.risk_level_description(result[:risk_score])
141
+
142
+ puts "Risk Level: #{risk_level} (#{result[:risk_score]}/100)"
143
+ puts "Threats: #{result[:factors].length} detected"
144
+
145
+ if result[:factors].any?
146
+ puts "Primary Concerns: #{result[:factors].first(3).join(', ')}"
147
+ end
148
+ end
149
+ end
150
+
151
+ # Run CLI if this file is executed directly
152
+ if __FILE__ == $0
153
+ cli = AiRootShieldCLI.new
154
+ cli.run(ARGV)
155
+ end
@@ -0,0 +1,331 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AiRootShield
4
+ module Analyzers
5
+ # Detects emulator and simulator environments
6
+ class EmulatorDetector
7
+ # Known emulator indicators
8
+ EMULATOR_FILES = %w[
9
+ /dev/socket/qemud
10
+ /dev/qemu_pipe
11
+ /system/lib/libc_malloc_debug_qemu.so
12
+ /sys/qemu_trace
13
+ /system/bin/qemu-props
14
+ /dev/socket/baseband_genyd
15
+ /dev/socket/genyd
16
+ /proc/tty/drivers
17
+ /proc/cpuinfo
18
+ /system/lib/libdvm.so
19
+ /system/bin/androVM-prop
20
+ /system/bin/microvirt-prop
21
+ /system/lib/libhoudini.so
22
+ /system/lib/arm/libhoudini.so
23
+ ].freeze
24
+
25
+ EMULATOR_PROPERTIES = %w[
26
+ ro.kernel.qemu
27
+ ro.bootmode
28
+ ro.hardware
29
+ ro.product.device
30
+ ro.product.model
31
+ ro.product.name
32
+ ro.product.brand
33
+ ro.build.product
34
+ ro.build.fingerprint
35
+ init.svc.qemud
36
+ qemu.hw.mainkeys
37
+ qemu.sf.fake_camera
38
+ qemu.sf.lcd_density
39
+ ro.kernel.android.qemud
40
+ ].freeze
41
+
42
+ EMULATOR_PACKAGES = %w[
43
+ com.google.android.launcher.layouts.genymotion
44
+ com.bluestacks
45
+ com.bignox.app
46
+ com.vphone.launcher
47
+ com.microvirt.guide
48
+ com.microvirt.market
49
+ com.microvirt.memuplay
50
+ com.android.development_settings
51
+ com.android.development
52
+ com.android.emulator.smoketests
53
+ ].freeze
54
+
55
+ # iOS Simulator indicators
56
+ IOS_SIMULATOR_INDICATORS = %w[
57
+ i386
58
+ x86_64
59
+ Simulator
60
+ iPhone Simulator
61
+ iPad Simulator
62
+ AppleTV Simulator
63
+ ].freeze
64
+
65
+ def analyze(device_data)
66
+ factors = []
67
+ risk_score = 0
68
+
69
+ case device_data[:platform]
70
+ when "android"
71
+ android_result = check_android_emulator(device_data)
72
+ factors.concat(android_result[:factors])
73
+ risk_score += android_result[:risk_score]
74
+ when "ios"
75
+ ios_result = check_ios_simulator(device_data)
76
+ factors.concat(ios_result[:factors])
77
+ risk_score += ios_result[:risk_score]
78
+ end
79
+
80
+ # Cross-platform checks
81
+ cross_platform_result = check_cross_platform_indicators(device_data)
82
+ factors.concat(cross_platform_result[:factors])
83
+ risk_score += cross_platform_result[:risk_score]
84
+
85
+ {
86
+ factors: factors,
87
+ risk_score: [risk_score, 100].min
88
+ }
89
+ end
90
+
91
+ private
92
+
93
+ def check_android_emulator(device_data)
94
+ factors = []
95
+ risk_score = 0
96
+
97
+ # Check for emulator files
98
+ emulator_files = check_emulator_files(device_data[:file_system])
99
+ factors.concat(emulator_files)
100
+ risk_score += emulator_files.length * 15
101
+
102
+ # Check system properties
103
+ property_factors = check_system_properties(device_data[:system_info])
104
+ factors.concat(property_factors)
105
+ risk_score += property_factors.length * 12
106
+
107
+ # Check for emulator packages
108
+ package_factors = check_emulator_packages(device_data[:installed_packages])
109
+ factors.concat(package_factors)
110
+ risk_score += package_factors.length * 18
111
+
112
+ # Check hardware characteristics
113
+ hardware_factors = check_hardware_indicators(device_data[:hardware_info])
114
+ factors.concat(hardware_factors)
115
+ risk_score += hardware_factors.length * 10
116
+
117
+ {
118
+ factors: factors,
119
+ risk_score: risk_score
120
+ }
121
+ end
122
+
123
+ def check_ios_simulator(device_data)
124
+ factors = []
125
+ risk_score = 0
126
+
127
+ # Check hardware info for simulator indicators
128
+ hardware_info = device_data[:hardware_info] || {}
129
+
130
+ IOS_SIMULATOR_INDICATORS.each do |indicator|
131
+ device_model = hardware_info[:device_model].to_s
132
+ if device_model.include?(indicator)
133
+ factors << "SIMULATOR_IOS"
134
+ risk_score += 20
135
+ break
136
+ end
137
+ end
138
+
139
+ # Check for simulator-specific file paths
140
+ file_system = device_data[:file_system] || {}
141
+ suspicious_files = file_system[:suspicious_files] || []
142
+
143
+ if suspicious_files.any? { |file| file.to_s.include?("Simulator") }
144
+ factors << "SIMULATOR_FILES_DETECTED"
145
+ risk_score += 15
146
+ end
147
+
148
+ {
149
+ factors: factors,
150
+ risk_score: risk_score
151
+ }
152
+ end
153
+
154
+ def check_cross_platform_indicators(device_data)
155
+ factors = []
156
+ risk_score = 0
157
+
158
+ # Check for missing baseband (common in emulators)
159
+ baseband_factor = check_baseband_presence(device_data[:hardware_info])
160
+ if baseband_factor
161
+ factors << baseband_factor
162
+ risk_score += 12
163
+ end
164
+
165
+ # Check sensor anomalies
166
+ sensor_factors = check_sensor_anomalies(device_data[:hardware_info])
167
+ factors.concat(sensor_factors)
168
+ risk_score += sensor_factors.length * 8
169
+
170
+ # Check for virtualization indicators in processes
171
+ process_factors = check_virtualization_processes(device_data[:processes])
172
+ factors.concat(process_factors)
173
+ risk_score += process_factors.length * 10
174
+
175
+ {
176
+ factors: factors,
177
+ risk_score: risk_score
178
+ }
179
+ end
180
+
181
+ def check_emulator_files(file_system)
182
+ factors = []
183
+ suspicious_files = file_system[:suspicious_files] || []
184
+ system_binaries = file_system[:system_binaries] || []
185
+
186
+ all_files = suspicious_files + system_binaries
187
+
188
+ EMULATOR_FILES.each do |emulator_file|
189
+ if all_files.any? { |file| file.to_s.include?(emulator_file) }
190
+ case emulator_file
191
+ when /qemu/i
192
+ factors << "EMULATOR_QEMU"
193
+ when /genyd/i
194
+ factors << "EMULATOR_GENYMOTION"
195
+ when /microvirt/i
196
+ factors << "EMULATOR_MICROVIRT"
197
+ else
198
+ factors << "EMULATOR_FILE_DETECTED"
199
+ end
200
+ end
201
+ end
202
+
203
+ factors
204
+ end
205
+
206
+ def check_system_properties(system_info)
207
+ factors = []
208
+
209
+ # Check build fingerprint
210
+ fingerprint = system_info[:build_fingerprint].to_s
211
+ if fingerprint.include?("generic") || fingerprint.include?("emulator")
212
+ factors << "EMULATOR_BUILD_GENERIC"
213
+ end
214
+
215
+ # Check kernel version for emulator indicators
216
+ kernel_version = system_info[:kernel_version].to_s
217
+ if kernel_version.include?("goldfish") || kernel_version.include?("ranchu")
218
+ factors << "EMULATOR_KERNEL_GOLDFISH"
219
+ end
220
+
221
+ factors
222
+ end
223
+
224
+ def check_emulator_packages(packages)
225
+ factors = []
226
+ package_names = packages.map { |pkg| pkg.is_a?(Hash) ? pkg["name"] : pkg.to_s }
227
+
228
+ EMULATOR_PACKAGES.each do |emulator_pkg|
229
+ if package_names.any? { |pkg| pkg&.include?(emulator_pkg) }
230
+ case emulator_pkg
231
+ when /genymotion/i
232
+ factors << "EMULATOR_GENYMOTION"
233
+ when /bluestacks/i
234
+ factors << "EMULATOR_BLUESTACKS"
235
+ when /nox/i
236
+ factors << "EMULATOR_NOX"
237
+ when /memu/i
238
+ factors << "EMULATOR_MEMU"
239
+ else
240
+ factors << "EMULATOR_PACKAGE_DETECTED"
241
+ end
242
+ end
243
+ end
244
+
245
+ factors
246
+ end
247
+
248
+ def check_hardware_indicators(hardware_info)
249
+ factors = []
250
+
251
+ device_model = hardware_info[:device_model].to_s.downcase
252
+ manufacturer = hardware_info[:manufacturer].to_s.downcase
253
+
254
+ # Check for generic device names
255
+ if device_model.include?("generic") || device_model.include?("emulator")
256
+ factors << "EMULATOR_GENERIC_DEVICE"
257
+ end
258
+
259
+ # Check for known emulator manufacturers
260
+ if manufacturer.include?("genymotion") || manufacturer.include?("android")
261
+ factors << "EMULATOR_MANUFACTURER"
262
+ end
263
+
264
+ # Check serial number patterns
265
+ serial = hardware_info[:serial_number].to_s
266
+ if serial.include?("android") || serial == "unknown"
267
+ factors << "EMULATOR_SERIAL_PATTERN"
268
+ end
269
+
270
+ factors
271
+ end
272
+
273
+ def check_baseband_presence(hardware_info)
274
+ baseband_version = hardware_info[:baseband_version]
275
+
276
+ # Missing or null baseband is common in emulators
277
+ if baseband_version.nil? || baseband_version.to_s.empty? || baseband_version.to_s.downcase.include?("unknown")
278
+ return "MISSING_BASEBAND"
279
+ end
280
+
281
+ nil
282
+ end
283
+
284
+ def check_sensor_anomalies(hardware_info)
285
+ factors = []
286
+ sensors = hardware_info[:sensors] || []
287
+
288
+ # Check for missing common sensors
289
+ common_sensors = %w[accelerometer gyroscope magnetometer proximity light]
290
+ missing_sensors = common_sensors - sensors.map(&:downcase)
291
+
292
+ if missing_sensors.length >= 3
293
+ factors << "VIRTUAL_SENSORS"
294
+ end
295
+
296
+ # Check for emulator-specific sensor patterns
297
+ if sensors.any? { |sensor| sensor.to_s.downcase.include?("goldfish") }
298
+ factors << "EMULATOR_GOLDFISH_SENSORS"
299
+ end
300
+
301
+ factors
302
+ end
303
+
304
+ def check_virtualization_processes(processes)
305
+ factors = []
306
+ process_names = processes.map { |proc| proc.is_a?(Hash) ? proc["name"] : proc.to_s }
307
+
308
+ virtualization_indicators = %w[
309
+ qemu
310
+ vbox
311
+ vmware
312
+ virtualbox
313
+ genymotion
314
+ bluestacks
315
+ nox
316
+ memu
317
+ ldplayer
318
+ ]
319
+
320
+ virtualization_indicators.each do |indicator|
321
+ if process_names.any? { |proc| proc&.downcase&.include?(indicator) }
322
+ factors << "VIRTUALIZATION_PROCESS_DETECTED"
323
+ break
324
+ end
325
+ end
326
+
327
+ factors
328
+ end
329
+ end
330
+ end
331
+ end