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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +56 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +88 -0
- data/LICENSE +21 -0
- data/README.md +310 -0
- data/Rakefile +36 -0
- data/examples/device_logs/clean_device.json +74 -0
- data/examples/device_logs/rooted_android.json +93 -0
- data/exe/ai_root_shield +155 -0
- data/lib/ai_root_shield/analyzers/emulator_detector.rb +331 -0
- data/lib/ai_root_shield/analyzers/hooking_detector.rb +375 -0
- data/lib/ai_root_shield/analyzers/integrity_checker.rb +407 -0
- data/lib/ai_root_shield/analyzers/network_analyzer.rb +352 -0
- data/lib/ai_root_shield/analyzers/root_detector.rb +292 -0
- data/lib/ai_root_shield/detector.rb +78 -0
- data/lib/ai_root_shield/device_log_parser.rb +118 -0
- data/lib/ai_root_shield/risk_calculator.rb +161 -0
- data/lib/ai_root_shield/version.rb +5 -0
- data/lib/ai_root_shield.rb +36 -0
- metadata +179 -0
@@ -0,0 +1,375 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AiRootShield
|
4
|
+
module Analyzers
|
5
|
+
# Detects hooking frameworks and runtime instrumentation
|
6
|
+
class HookingDetector
|
7
|
+
# Frida indicators
|
8
|
+
FRIDA_INDICATORS = %w[
|
9
|
+
frida-server
|
10
|
+
frida-agent
|
11
|
+
frida-gadget
|
12
|
+
re.frida.server
|
13
|
+
/data/local/tmp/frida-server
|
14
|
+
/system/bin/frida-server
|
15
|
+
/system/xbin/frida-server
|
16
|
+
libfrida-gadget.so
|
17
|
+
frida-agent.so
|
18
|
+
FridaGadget
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
# Xposed Framework indicators
|
22
|
+
XPOSED_INDICATORS = %w[
|
23
|
+
de.robv.android.xposed.installer
|
24
|
+
XposedBridge
|
25
|
+
XposedHelpers
|
26
|
+
libxposed_art.so
|
27
|
+
libxposed_dalvik.so
|
28
|
+
/system/framework/XposedBridge.jar
|
29
|
+
/system/bin/app_process_xposed
|
30
|
+
/system/xbin/xposed
|
31
|
+
XposedMods
|
32
|
+
].freeze
|
33
|
+
|
34
|
+
# Substrate/Cydia Substrate indicators
|
35
|
+
SUBSTRATE_INDICATORS = %w[
|
36
|
+
MobileSubstrate
|
37
|
+
libsubstrate.dylib
|
38
|
+
/Library/MobileSubstrate/
|
39
|
+
/usr/lib/libsubstrate.dylib
|
40
|
+
CydiaSubstrate
|
41
|
+
MSHookFunction
|
42
|
+
MSHookMessageEx
|
43
|
+
substrate.h
|
44
|
+
].freeze
|
45
|
+
|
46
|
+
# Magisk indicators
|
47
|
+
MAGISK_INDICATORS = %w[
|
48
|
+
magisk
|
49
|
+
magiskhide
|
50
|
+
magiskpolicy
|
51
|
+
/sbin/magisk
|
52
|
+
/system/xbin/magisk
|
53
|
+
com.topjohnwu.magisk
|
54
|
+
magisk.img
|
55
|
+
magisk_merge.img
|
56
|
+
.magisk
|
57
|
+
magiskinit
|
58
|
+
].freeze
|
59
|
+
|
60
|
+
# Debugging indicators
|
61
|
+
DEBUG_INDICATORS = %w[
|
62
|
+
android:debuggable
|
63
|
+
ro.debuggable
|
64
|
+
gdb
|
65
|
+
gdbserver
|
66
|
+
lldb
|
67
|
+
lldb-server
|
68
|
+
strace
|
69
|
+
ltrace
|
70
|
+
/system/bin/gdbserver
|
71
|
+
/data/local/tmp/gdbserver
|
72
|
+
].freeze
|
73
|
+
|
74
|
+
def analyze(device_data)
|
75
|
+
factors = []
|
76
|
+
risk_score = 0
|
77
|
+
|
78
|
+
# Check for Frida
|
79
|
+
frida_result = check_frida_presence(device_data)
|
80
|
+
factors.concat(frida_result[:factors])
|
81
|
+
risk_score += frida_result[:risk_score]
|
82
|
+
|
83
|
+
# Check for Xposed Framework
|
84
|
+
xposed_result = check_xposed_framework(device_data)
|
85
|
+
factors.concat(xposed_result[:factors])
|
86
|
+
risk_score += xposed_result[:risk_score]
|
87
|
+
|
88
|
+
# Check for Substrate (iOS)
|
89
|
+
substrate_result = check_substrate_presence(device_data)
|
90
|
+
factors.concat(substrate_result[:factors])
|
91
|
+
risk_score += substrate_result[:risk_score]
|
92
|
+
|
93
|
+
# Check for Magisk
|
94
|
+
magisk_result = check_magisk_presence(device_data)
|
95
|
+
factors.concat(magisk_result[:factors])
|
96
|
+
risk_score += magisk_result[:risk_score]
|
97
|
+
|
98
|
+
# Check for debugging tools
|
99
|
+
debug_result = check_debugging_tools(device_data)
|
100
|
+
factors.concat(debug_result[:factors])
|
101
|
+
risk_score += debug_result[:risk_score]
|
102
|
+
|
103
|
+
# Check runtime behavior indicators
|
104
|
+
runtime_result = check_runtime_indicators(device_data)
|
105
|
+
factors.concat(runtime_result[:factors])
|
106
|
+
risk_score += runtime_result[:risk_score]
|
107
|
+
|
108
|
+
{
|
109
|
+
factors: factors,
|
110
|
+
risk_score: [risk_score, 100].min
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def check_frida_presence(device_data)
|
117
|
+
factors = []
|
118
|
+
risk_score = 0
|
119
|
+
|
120
|
+
# Check processes
|
121
|
+
processes = device_data[:processes] || []
|
122
|
+
process_names = processes.map { |proc| proc.is_a?(Hash) ? proc["name"] : proc.to_s }
|
123
|
+
|
124
|
+
FRIDA_INDICATORS.each do |indicator|
|
125
|
+
if process_names.any? { |proc| proc.downcase.include?(indicator.downcase) }
|
126
|
+
factors << "FRIDA_GADGET"
|
127
|
+
risk_score += 25
|
128
|
+
break
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Check file system
|
133
|
+
file_system = device_data[:file_system] || {}
|
134
|
+
all_files = (file_system[:suspicious_files] || []) + (file_system[:system_binaries] || [])
|
135
|
+
|
136
|
+
FRIDA_INDICATORS.each do |indicator|
|
137
|
+
if all_files.any? { |file| file.to_s.downcase.include?(indicator.downcase) }
|
138
|
+
factors << "FRIDA_FILES_DETECTED"
|
139
|
+
risk_score += 20
|
140
|
+
break
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Check installed packages
|
145
|
+
packages = device_data[:installed_packages] || []
|
146
|
+
package_names = packages.map { |pkg| pkg.is_a?(Hash) ? pkg["name"] : pkg.to_s }
|
147
|
+
|
148
|
+
if package_names.any? { |pkg| pkg.downcase.include?("frida") }
|
149
|
+
factors << "FRIDA_PACKAGE_INSTALLED"
|
150
|
+
risk_score += 22
|
151
|
+
end
|
152
|
+
|
153
|
+
{
|
154
|
+
factors: factors,
|
155
|
+
risk_score: risk_score
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
def check_xposed_framework(device_data)
|
160
|
+
factors = []
|
161
|
+
risk_score = 0
|
162
|
+
|
163
|
+
# Check installed packages
|
164
|
+
packages = device_data[:installed_packages] || []
|
165
|
+
package_names = packages.map { |pkg| pkg.is_a?(Hash) ? pkg["name"] : pkg.to_s }
|
166
|
+
|
167
|
+
XPOSED_INDICATORS.each do |indicator|
|
168
|
+
if package_names.any? { |pkg| pkg.downcase.include?(indicator.downcase) }
|
169
|
+
factors << "XPOSED_FRAMEWORK"
|
170
|
+
risk_score += 22
|
171
|
+
break
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Check file system for Xposed files
|
176
|
+
file_system = device_data[:file_system] || {}
|
177
|
+
all_files = (file_system[:suspicious_files] || []) + (file_system[:system_binaries] || [])
|
178
|
+
|
179
|
+
XPOSED_INDICATORS.each do |indicator|
|
180
|
+
if all_files.any? { |file| file.to_s.include?(indicator) }
|
181
|
+
factors << "XPOSED_FILES_DETECTED"
|
182
|
+
risk_score += 18
|
183
|
+
break
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Check system logs for Xposed activity
|
188
|
+
logs = device_data[:logs] || []
|
189
|
+
if logs.any? { |log| log.to_s.downcase.include?("xposed") }
|
190
|
+
factors << "XPOSED_LOG_ACTIVITY"
|
191
|
+
risk_score += 15
|
192
|
+
end
|
193
|
+
|
194
|
+
{
|
195
|
+
factors: factors,
|
196
|
+
risk_score: risk_score
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
def check_substrate_presence(device_data)
|
201
|
+
factors = []
|
202
|
+
risk_score = 0
|
203
|
+
|
204
|
+
return { factors: factors, risk_score: risk_score } unless device_data[:platform] == "ios"
|
205
|
+
|
206
|
+
# Check file system for Substrate files
|
207
|
+
file_system = device_data[:file_system] || {}
|
208
|
+
all_files = (file_system[:suspicious_files] || []) + (file_system[:system_binaries] || [])
|
209
|
+
|
210
|
+
SUBSTRATE_INDICATORS.each do |indicator|
|
211
|
+
if all_files.any? { |file| file.to_s.include?(indicator) }
|
212
|
+
factors << "SUBSTRATE_INJECTION"
|
213
|
+
risk_score += 18
|
214
|
+
break
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Check installed packages for Substrate-related apps
|
219
|
+
packages = device_data[:installed_packages] || []
|
220
|
+
package_names = packages.map { |pkg| pkg.is_a?(Hash) ? pkg["name"] : pkg.to_s }
|
221
|
+
|
222
|
+
if package_names.any? { |pkg| pkg.downcase.include?("substrate") || pkg.downcase.include?("cydia") }
|
223
|
+
factors << "SUBSTRATE_PACKAGE_DETECTED"
|
224
|
+
risk_score += 20
|
225
|
+
end
|
226
|
+
|
227
|
+
{
|
228
|
+
factors: factors,
|
229
|
+
risk_score: risk_score
|
230
|
+
}
|
231
|
+
end
|
232
|
+
|
233
|
+
def check_magisk_presence(device_data)
|
234
|
+
factors = []
|
235
|
+
risk_score = 0
|
236
|
+
|
237
|
+
return { factors: factors, risk_score: risk_score } unless device_data[:platform] == "android"
|
238
|
+
|
239
|
+
# Check processes for Magisk
|
240
|
+
processes = device_data[:processes] || []
|
241
|
+
process_names = processes.map { |proc| proc.is_a?(Hash) ? proc["name"] : proc.to_s }
|
242
|
+
|
243
|
+
MAGISK_INDICATORS.each do |indicator|
|
244
|
+
if process_names.any? { |proc| proc.downcase.include?(indicator.downcase) }
|
245
|
+
factors << "MAGISK_MODULES"
|
246
|
+
risk_score += 20
|
247
|
+
break
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Check file system for Magisk files
|
252
|
+
file_system = device_data[:file_system] || {}
|
253
|
+
all_files = (file_system[:suspicious_files] || []) + (file_system[:system_binaries] || [])
|
254
|
+
|
255
|
+
MAGISK_INDICATORS.each do |indicator|
|
256
|
+
if all_files.any? { |file| file.to_s.downcase.include?(indicator.downcase) }
|
257
|
+
factors << "MAGISK_FILES_DETECTED"
|
258
|
+
risk_score += 18
|
259
|
+
break
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Check installed packages
|
264
|
+
packages = device_data[:installed_packages] || []
|
265
|
+
package_names = packages.map { |pkg| pkg.is_a?(Hash) ? pkg["name"] : pkg.to_s }
|
266
|
+
|
267
|
+
if package_names.any? { |pkg| pkg.downcase.include?("magisk") }
|
268
|
+
factors << "MAGISK_APP_INSTALLED"
|
269
|
+
risk_score += 22
|
270
|
+
end
|
271
|
+
|
272
|
+
{
|
273
|
+
factors: factors,
|
274
|
+
risk_score: risk_score
|
275
|
+
}
|
276
|
+
end
|
277
|
+
|
278
|
+
def check_debugging_tools(device_data)
|
279
|
+
factors = []
|
280
|
+
risk_score = 0
|
281
|
+
|
282
|
+
# Check processes for debugging tools
|
283
|
+
processes = device_data[:processes] || []
|
284
|
+
process_names = processes.map { |proc| proc.is_a?(Hash) ? proc["name"] : proc.to_s }
|
285
|
+
|
286
|
+
DEBUG_INDICATORS.each do |indicator|
|
287
|
+
if process_names.any? { |proc| proc.downcase.include?(indicator.downcase) }
|
288
|
+
factors << "DEBUGGER_ATTACHED"
|
289
|
+
risk_score += 15
|
290
|
+
break
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# Check file system for debugging binaries
|
295
|
+
file_system = device_data[:file_system] || {}
|
296
|
+
all_files = (file_system[:suspicious_files] || []) + (file_system[:system_binaries] || [])
|
297
|
+
|
298
|
+
DEBUG_INDICATORS.each do |indicator|
|
299
|
+
if all_files.any? { |file| file.to_s.include?(indicator) }
|
300
|
+
factors << "DEBUG_TOOLS_PRESENT"
|
301
|
+
risk_score += 12
|
302
|
+
break
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# Check system settings for debugging flags
|
307
|
+
system_info = device_data[:system_info] || {}
|
308
|
+
security_settings = device_data[:security_settings] || {}
|
309
|
+
|
310
|
+
if security_settings[:usb_debugging] == true
|
311
|
+
factors << "USB_DEBUGGING_ENABLED"
|
312
|
+
risk_score += 10
|
313
|
+
end
|
314
|
+
|
315
|
+
{
|
316
|
+
factors: factors,
|
317
|
+
risk_score: risk_score
|
318
|
+
}
|
319
|
+
end
|
320
|
+
|
321
|
+
def check_runtime_indicators(device_data)
|
322
|
+
factors = []
|
323
|
+
risk_score = 0
|
324
|
+
|
325
|
+
# Check for suspicious library injections
|
326
|
+
processes = device_data[:processes] || []
|
327
|
+
|
328
|
+
processes.each do |process|
|
329
|
+
next unless process.is_a?(Hash)
|
330
|
+
|
331
|
+
libraries = process["loaded_libraries"] || []
|
332
|
+
|
333
|
+
# Check for known hooking libraries
|
334
|
+
hooking_libs = %w[libfrida libxposed libsubstrate libmagisk]
|
335
|
+
|
336
|
+
hooking_libs.each do |lib|
|
337
|
+
if libraries.any? { |library| library.to_s.downcase.include?(lib) }
|
338
|
+
factors << "RUNTIME_LIBRARY_INJECTION"
|
339
|
+
risk_score += 18
|
340
|
+
break
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# Check system logs for hooking activity
|
346
|
+
logs = device_data[:logs] || []
|
347
|
+
|
348
|
+
hooking_keywords = %w[hook inject frida xposed substrate magisk]
|
349
|
+
|
350
|
+
hooking_keywords.each do |keyword|
|
351
|
+
if logs.any? { |log| log.to_s.downcase.include?(keyword) }
|
352
|
+
factors << "HOOKING_LOG_ACTIVITY"
|
353
|
+
risk_score += 12
|
354
|
+
break
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
# Check for method swizzling indicators (iOS)
|
359
|
+
if device_data[:platform] == "ios"
|
360
|
+
swizzling_indicators = %w[method_exchangeImplementations class_replaceMethod]
|
361
|
+
|
362
|
+
if logs.any? { |log| swizzling_indicators.any? { |indicator| log.to_s.include?(indicator) } }
|
363
|
+
factors << "METHOD_SWIZZLING_DETECTED"
|
364
|
+
risk_score += 15
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
{
|
369
|
+
factors: factors,
|
370
|
+
risk_score: risk_score
|
371
|
+
}
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|