jailbreak-root-detection 0.0.1

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,706 @@
1
+ package com.smartwinnr.plugins.jailbreakrootdetection;
2
+
3
+ import android.util.Log;
4
+ import android.content.Context;
5
+ import android.content.pm.PackageInfo;
6
+ import android.content.pm.PackageManager;
7
+ import android.os.Build;
8
+
9
+ import java.io.BufferedReader;
10
+ import java.io.File;
11
+ import java.io.IOException;
12
+ import java.io.InputStreamReader;
13
+ import java.io.InputStream;
14
+ import java.util.ArrayList;
15
+ import java.util.Arrays;
16
+ import java.util.List;
17
+ import java.util.HashMap;
18
+ import java.util.Map;
19
+ import java.util.NoSuchElementException;
20
+ import java.util.Scanner;
21
+
22
+ import java.security.KeyFactory;
23
+ import java.security.PublicKey;
24
+ import java.security.spec.X509EncodedKeySpec;
25
+ import javax.crypto.SecretKey;
26
+ import javax.crypto.spec.SecretKeySpec;
27
+ import java.security.NoSuchAlgorithmException;
28
+ import java.security.spec.InvalidKeySpecException;
29
+ import org.jose4j.jwe.JsonWebEncryption;
30
+ import org.jose4j.jws.JsonWebSignature;
31
+ import org.jose4j.lang.JoseException;
32
+ import org.json.JSONArray;
33
+ import org.json.JSONException;
34
+ import org.json.JSONObject;
35
+ import com.google.android.gms.tasks.Task;
36
+ import com.google.android.gms.tasks.Tasks;
37
+ import com.google.android.play.core.integrity.IntegrityManager;
38
+ import com.google.android.play.core.integrity.IntegrityManagerFactory;
39
+ import com.google.android.play.core.integrity.IntegrityTokenRequest;
40
+ import com.google.android.play.core.integrity.IntegrityTokenResponse;
41
+
42
+ import java.util.Objects;
43
+ import java.util.concurrent.CompletableFuture;
44
+ import java.util.concurrent.ExecutionException;
45
+ import java.util.concurrent.ExecutorService;
46
+ import java.util.concurrent.Executors;
47
+
48
+ import android.util.Base64;
49
+ import androidx.appcompat.app.AppCompatActivity;
50
+ import java.io.OutputStreamWriter;
51
+
52
+
53
+ public class JailbreakRootDetection extends AppCompatActivity {
54
+
55
+ private static final String TAG = "JailbreakRootDetection";
56
+ static final String BINARY_SU = "su";
57
+ static final String BINARY_BUSYBOX = "busybox";
58
+ private boolean isJailbroken = false;
59
+ public String echo(String value) {
60
+ Log.i("Echo", value);
61
+ return value;
62
+ }
63
+
64
+ public Boolean jailbroken(Context context, String verificationKey, String decryptionKey) {
65
+ Log.i("JailbreakRootDetection", "Checking root detectection");
66
+
67
+ boolean rootMethod1Result = checkRootMethod1();
68
+ boolean checkRootMethod2Result = checkRootMethod2();
69
+ boolean checkRootMethod3Result = checkRootMethod3();
70
+ boolean checkRootBypassAppsResult = checkRootBypassApps(context);
71
+ boolean checkKeyLoggerAppsResult = checkKeyLoggerApps(context);
72
+ boolean checkDirPermissionsResult = checkDirPermissions();
73
+ boolean checkforOverTheAirCertificatesResult = checkforOverTheAirCertificates();
74
+ boolean checkForBinaryResultSu = checkForBinary(BINARY_SU);
75
+ boolean checkForDangerousProps = checkForDangerousProps();
76
+ boolean checkForRWPathsResult = checkForRWPaths();
77
+ boolean checkSuResult = checkSuExists();
78
+ boolean checkForMagiskBinaryResult = checkForMagiskBinary();
79
+
80
+ Log.i("JailbreakRootDetection", "Dangerous File " + rootMethod1Result);
81
+ Log.i("JailbreakRootDetection", "Dangerous Command " + checkRootMethod2Result);
82
+ Log.i("JailbreakRootDetection", "Test Keys " + checkRootMethod3Result);
83
+ Log.i("JailbreakRootDetection", "RootBypass App Check " + checkRootBypassAppsResult);
84
+ Log.i("JailbreakRootDetection", "Key Logger App Check " + checkKeyLoggerAppsResult);
85
+ Log.i("JailbreakRootDetection", "Directory Permission " + checkDirPermissionsResult);
86
+ Log.i("JailbreakRootDetection", "OTA Cert "+ checkforOverTheAirCertificatesResult);
87
+ Log.i("JailbreakRootDetection", "SU Binary "+ checkForBinaryResultSu);
88
+ Log.i("JailbreakRootDetection", "Dangerous Properties " + checkForDangerousProps);
89
+ Log.i("JailbreakRootDetection", "Read/Write Path Check "+ checkForRWPathsResult);
90
+ Log.i("JailbreakRootDetection", "SU Commnad Execution "+ checkSuResult);
91
+ Log.i("JailbreakRootDetection", "Magisk Binary "+ checkForMagiskBinaryResult);
92
+
93
+ boolean isJailbroken =
94
+ rootMethod1Result ||
95
+ checkRootMethod2Result ||
96
+ checkRootMethod3Result ||
97
+ checkRootBypassAppsResult ||
98
+ checkKeyLoggerAppsResult ||
99
+ checkDirPermissionsResult ||
100
+ checkforOverTheAirCertificatesResult ||
101
+ checkForBinaryResultSu ||
102
+ checkForDangerousProps ||
103
+ checkForRWPathsResult ||
104
+ checkSuResult ||
105
+ checkForMagiskBinaryResult;
106
+
107
+ CompletableFuture<Boolean> future = performPlayIntegrityCheckAsync(context, verificationKey, decryptionKey);
108
+ // Apply the device integrity test then send the result to the app
109
+ try {
110
+ boolean googlePlayIntegrityCheck = future.get(); // This will block until the future completes
111
+ Log.i(TAG, "Is device jailbroken: " + isJailbroken);
112
+ isJailbroken = googlePlayIntegrityCheck || isJailbroken;
113
+ } catch (InterruptedException | ExecutionException e) {
114
+ Log.i(TAG, "An error occurred: " + e.getMessage());
115
+ }
116
+ isDeviceRooted();
117
+ Log.i("JailbreakRootDetection", "Jailbreak Detection Completed");
118
+ return isJailbroken;
119
+ }
120
+
121
+ public static boolean isDeviceRooted() {
122
+ String[] commands = {
123
+ "which su",
124
+ "ls -l /sbin/su /system/bin/su /system/xbin/su /data/local/xbin/su /data/local/bin/su /system/sd/xbin/su /system/bin/failsafe/su /data/local/su",
125
+ "ls -l /system/app/Superuser.apk /system/app/SuperSU.apk /system/app/Magisk.apk",
126
+ "which busybox",
127
+ "which magisk",
128
+ "ls -l /sbin/.magisk",
129
+ "ls -l /system/xbin/daemonsu /system/xbin/supolicy",
130
+ "cat /init.rc | grep 'su'",
131
+ "cat /init.environ.rc | grep 'su'",
132
+ "pm list packages | grep 'superuser'",
133
+ "pm list packages | grep 'supersu'",
134
+ "pm list packages | grep 'magisk'"
135
+ };
136
+
137
+ for (String command : commands) {
138
+ if (executeCommand(command)) {
139
+ return true; // If any command indicates root, return true
140
+ }
141
+ }
142
+
143
+ return false;
144
+ }
145
+
146
+ private static boolean executeCommand(String command) {
147
+ Log.i("executeCommand", String.valueOf(command));
148
+ try {
149
+ Process process = Runtime.getRuntime().exec(command);
150
+ Log.i("executeCommand", String.valueOf(process));
151
+ BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
152
+ String output;
153
+ Log.i("executeCommand", String.valueOf(in.readLine()));
154
+ while ((output = in.readLine()) != null) {
155
+ Log.i("executeCommand", String.valueOf(output));
156
+ if (!output.isEmpty()) {
157
+ return true; // If any output is received, consider it as an indication of root
158
+ }
159
+ }
160
+ in.close();
161
+ process.waitFor();
162
+ } catch (Exception e) {
163
+ e.printStackTrace();
164
+ }
165
+ return false;
166
+ }
167
+
168
+
169
+ private boolean checkRootMethod1() {
170
+ String[] paths = {
171
+ "/sbin/su",
172
+ "/data/local/xbin/su",
173
+ "/data/local/bin/su",
174
+ "/system/bin/failsafe/su",
175
+ "/data/local/su",
176
+ "/system/xbin/daemonsu",
177
+ "/system/sd/xbin/su",
178
+ "/system/usr/we-need-root/su-backup",
179
+ "/system/bin/busybox",
180
+ "/system/xbin/busybox",
181
+ "/sbin/busybox",
182
+ "/system/su",
183
+ "/data/local/xbin/busybox",
184
+ "/data/local/bin/busybox",
185
+ "/data/local/busybox",
186
+ "/su/bin/busybox",
187
+ "/system/bin/su",
188
+ "/system/xbin/su",
189
+ "/su/bin/su"
190
+ };
191
+
192
+ for (String path : paths) {
193
+ if (new File(path).exists()) return true;
194
+ }
195
+ return false;
196
+ }
197
+
198
+ private boolean checkRootMethod2() {
199
+ List<String> commands = new ArrayList<>();
200
+ commands.add("which su");
201
+ commands.add("/system/xbin/which su");
202
+ commands.add("/system/bin/which su");
203
+ return executeCommands(commands);
204
+ }
205
+
206
+ private boolean checkRootMethod3() {
207
+ String buildTags = Build.TAGS;
208
+ return buildTags != null && buildTags.contains("test-keys");
209
+ }
210
+
211
+ private boolean executeCommands(List<String> commands) {
212
+ try {
213
+ for (String command : commands) {
214
+ Process process = Runtime.getRuntime().exec(command);
215
+ BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
216
+ if (in.readLine() != null) return true;
217
+ in.close();
218
+ }
219
+ return false;
220
+ } catch (Exception e) {
221
+ return false;
222
+ }
223
+ }
224
+
225
+ private boolean checkRootBypassApps(final Context context) {
226
+ Log.i("checkRootBypassApps", "Root Bypassapp checking ");
227
+ List<String> rootAppsPackages = Arrays.asList(
228
+ "com.topjohnwu.magisk", // Magisk
229
+ "eu.chainfire.supersu", // SuperSU
230
+ "com.koushikdutta.superuser", // Superuser
231
+ "com.noshufou.android.su", // Superuser (older version)
232
+ "com.kingroot.kinguser", // KingRoot
233
+ "com.kingouser.com", // KingRoot
234
+ "com.kingroot.kinguser.activity", // Kingoroot
235
+ "com.kingoroot.kingoapp", // Kingoroot
236
+ "com.alephzain.framaroot", // Framaroot
237
+ "com.baidu.easyroot", // Baidu Root
238
+ "com.oneclickroot", // One Click Root
239
+ "com.shuame.rootgenius", // Root Genius
240
+ "com.mgyun.shua.su", // iRoot
241
+ "com.mgyun.shua", // iRoot
242
+ "com.geohot.towelroot", // Towelroot
243
+ "com.root.master", // Root Master
244
+ "com.z4mod.z4root", // Z4Root
245
+ "com.saurik.Cydia", // Cydia
246
+ "stericson.busybox", // BusyBox
247
+ "stericson.busybox.donate", // BusyBox (Donate)
248
+ "com.zachspong.temprootremovejb",
249
+ "com.ramdroid.appquarantine",
250
+ "eu.chainfire.stickmount",
251
+ "eu.chainfire.mobileodin.pro",
252
+ "eu.chainfire.liveboot",
253
+ "eu.chainfire.pryfi",
254
+ "eu.chainfire.adbd",
255
+ "eu.chainfire.recently",
256
+ "eu.chainfire.flash",
257
+ "eu.chainfire.stickmount.pro",
258
+ "eu.chainfire.triangleaway",
259
+ "org.adblockplus.android"
260
+ );
261
+ return isAnyPackageInstalled(rootAppsPackages, context);
262
+ }
263
+
264
+ private boolean checkKeyLoggerApps(final Context context) {
265
+ Log.i("checkKeyLoggerApps", "Key Logger App Checking");
266
+ List<String> rootAppsPackages = Arrays.asList(
267
+ "com.abifog.lokiboard",
268
+ "apk.typingrecorder",
269
+ "com.gpow.keylogger",
270
+ "com.onemanarmy.keylogger",
271
+ "com.mni.password.manager.keylogger",
272
+ "com.AwamiSolution.smartkeylogger",
273
+ "monitor.mubeen.androidkeylogger",
274
+ "com.as.keylogger"
275
+ );
276
+ return isAnyPackageInstalled(rootAppsPackages, context);
277
+ }
278
+
279
+ private boolean isAnyPackageInstalled(List<String> packages, final Context context) {
280
+ PackageManager pm = context.getPackageManager();
281
+ for (String packageName : packages) {
282
+ try {
283
+ PackageInfo isPackageInstalled = pm.getPackageInfo(packageName, 0);
284
+ return true; // Package found
285
+ } catch (PackageManager.NameNotFoundException e) {
286
+ // Package not found
287
+ }
288
+ }
289
+ return false;
290
+ }
291
+
292
+ private boolean checkDirPermissions() {
293
+ boolean isWritableDir;
294
+ boolean isReadableDataDir;
295
+ boolean result = false;
296
+ List<String> pathShouldNotWritable = Arrays.asList(
297
+ "/system",
298
+ "/system/bin",
299
+ "/system/sbin",
300
+ "/system/xbin",
301
+ "/vendor/bin",
302
+ "/sbin",
303
+ "/etc"
304
+ );
305
+
306
+ for (String dirName : pathShouldNotWritable) {
307
+ final File currentDir = new File(dirName);
308
+
309
+ Log.i("currentDir", String.valueOf(currentDir));
310
+ Log.i("exists", String.valueOf(currentDir.exists()));
311
+ Log.i("canWrite", String.valueOf(currentDir.canWrite()));
312
+ Log.i("canRead", String.valueOf(currentDir.canRead()));
313
+
314
+ isWritableDir = currentDir.exists() && currentDir.canWrite();
315
+ isReadableDataDir = (dirName.equals("/data") && currentDir.canRead());
316
+
317
+ if (isWritableDir || isReadableDataDir) {
318
+ result = true;
319
+ }
320
+ }
321
+ return result;
322
+ }
323
+
324
+ private boolean checkforOverTheAirCertificates() {
325
+ File otacerts = new File("/etc/security/otacerts.zip");
326
+ boolean exist = otacerts.exists();
327
+ boolean result = !exist;
328
+ return result;
329
+ }
330
+
331
+ private boolean performPlayIntegrityCheck(final Context context, String verifyKey, String decryptKey) {
332
+ final String nonce = NonceUtil.generateNonce(16);
333
+ Log.i("Nonce", nonce);
334
+ // but can be stored in a safer way, for example on a server
335
+ // and obtained by a secure http request
336
+ final String DECRYPTION_KEY = decryptKey;
337
+ final String VERIFICATION_KEY = verifyKey;
338
+ // Create an instance of IntegrityManager
339
+ IntegrityManager integrityManager = IntegrityManagerFactory.create(context);
340
+
341
+ // Request the integrity token by providing the nonce
342
+ Task<IntegrityTokenResponse> integrityTokenResponse = integrityManager
343
+ .requestIntegrityToken(IntegrityTokenRequest.builder().setNonce(nonce).build())
344
+ .addOnSuccessListener(response -> {
345
+ String integrityToken = response.token();
346
+ Log.i(TAG, "Integrity Token: " + integrityToken);
347
+
348
+ byte[] decryptionKeyBytes = Base64.decode(DECRYPTION_KEY, Base64.DEFAULT);
349
+ SecretKey decryptionKey = new SecretKeySpec(decryptionKeyBytes, 0, decryptionKeyBytes.length, "AES");
350
+
351
+ byte[] encodedVerificationKey = Base64.decode(VERIFICATION_KEY, Base64.DEFAULT);
352
+ PublicKey verificationKey = null;
353
+
354
+ try {
355
+ verificationKey = KeyFactory.getInstance("EC")
356
+ .generatePublic(new X509EncodedKeySpec(encodedVerificationKey));
357
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
358
+ Log.i(TAG, e.getMessage());
359
+ }
360
+
361
+ if (verificationKey == null) {
362
+ return;
363
+ }
364
+
365
+ JsonWebEncryption jwe = null;
366
+ try {
367
+ jwe = (JsonWebEncryption) JsonWebSignature.fromCompactSerialization(integrityToken);
368
+ } catch (JoseException e) {
369
+ e.printStackTrace();
370
+ }
371
+
372
+ if (jwe == null) {
373
+ return;
374
+ }
375
+
376
+ jwe.setKey(decryptionKey);
377
+
378
+ String compactJws = null;
379
+ try {
380
+ compactJws = jwe.getPayload();
381
+ } catch (JoseException e) {
382
+ Log.i(TAG, e.getMessage());
383
+ }
384
+
385
+ JsonWebSignature jws = null;
386
+ try {
387
+ jws = (JsonWebSignature) JsonWebSignature.fromCompactSerialization(compactJws);
388
+ } catch (JoseException e) {
389
+ Log.i(TAG, e.getMessage());
390
+ }
391
+
392
+ if (jws == null) {
393
+ return;
394
+ }
395
+
396
+ jws.setKey(verificationKey);
397
+
398
+ String jsonPlainVerdict = "";
399
+ try {
400
+ jsonPlainVerdict = jws.getPayload();
401
+ } catch (JoseException e) {
402
+ Log.i(TAG, e.getMessage());
403
+ return;
404
+ }
405
+
406
+ isJailbroken = parseDeviceIntegrity(jsonPlainVerdict);
407
+
408
+ Log.i(TAG, jsonPlainVerdict);
409
+ })
410
+ .addOnFailureListener(ex -> {
411
+ isJailbroken = true;
412
+ Log.i(TAG, "Error requesting integrity token: " + ex.getMessage());
413
+ });
414
+
415
+ return isJailbroken;
416
+ }
417
+
418
+ private CompletableFuture<Boolean> performPlayIntegrityCheckAsync(final Context context, String verifyKey, String decryptKey) {
419
+ return CompletableFuture.supplyAsync(() -> {
420
+ final String nonce = NonceUtil.generateNonce(16);
421
+ String decKey = "";
422
+ if (Objects.equals(decryptKey, "NoVerification")) {
423
+ decKey = "nmzeD9LVp6yxJ3kvn3KETASozsqM+yx45G4NqKLeiFc=";
424
+ } else {
425
+ decKey = decryptKey;
426
+ }
427
+
428
+ String verKey ="";
429
+ if (Objects.equals(verifyKey, "NoVerification")) {
430
+ verKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOE63FZbxD8193Sz/KwlBfb5LYyBZSYOckuys17CuGp6KWgzju8xUmwy0gXpkSgNIZZxDTdD6mGMBnUOmwk0zSQ==";
431
+ } else {
432
+ verKey = verifyKey;
433
+ }
434
+
435
+ final String DECRYPTION_KEY = decKey;
436
+ final String VERIFICATION_KEY = verKey;
437
+ IntegrityManager integrityManager = IntegrityManagerFactory.create(context);
438
+
439
+ Task<IntegrityTokenResponse> integrityTokenResponse = integrityManager
440
+ .requestIntegrityToken(IntegrityTokenRequest.builder().setNonce(nonce).build());
441
+
442
+ try {
443
+ IntegrityTokenResponse response = Tasks.await(integrityTokenResponse);
444
+ String integrityToken = response.token();
445
+ Log.i(TAG, "Integrity Token: " + integrityToken);
446
+
447
+ byte[] decryptionKeyBytes = Base64.decode(DECRYPTION_KEY, Base64.DEFAULT);
448
+ SecretKey decryptionKey = new SecretKeySpec(decryptionKeyBytes, 0, decryptionKeyBytes.length, "AES");
449
+
450
+ byte[] encodedVerificationKey = Base64.decode(VERIFICATION_KEY, Base64.DEFAULT);
451
+ PublicKey verificationKey = KeyFactory.getInstance("EC")
452
+ .generatePublic(new X509EncodedKeySpec(encodedVerificationKey));
453
+
454
+ JsonWebEncryption jwe = (JsonWebEncryption) JsonWebSignature.fromCompactSerialization(integrityToken);
455
+ jwe.setKey(decryptionKey);
456
+
457
+ String compactJws = jwe.getPayload();
458
+
459
+ JsonWebSignature jws = (JsonWebSignature) JsonWebSignature.fromCompactSerialization(compactJws);
460
+ jws.setKey(verificationKey);
461
+
462
+ String jsonPlainVerdict = jws.getPayload();
463
+ isJailbroken = parseDeviceIntegrity(jsonPlainVerdict);
464
+
465
+ Log.i(TAG, jsonPlainVerdict);
466
+ } catch (Exception e) {
467
+ // Its going in exception because google play integrity api might be blocked by some third pary service or internet access is not given in this case we will consider the app as jailbroken
468
+ isJailbroken = false;
469
+ Log.i(TAG, "Error requesting integrity token: " + e.getMessage());
470
+ }
471
+
472
+ return isJailbroken;
473
+ });
474
+ }
475
+
476
+ private boolean parseDeviceIntegrity(String jsonString) {
477
+ try {
478
+ // Parse the JSON string into a JSONObject
479
+ JSONObject jsonObject = new JSONObject(jsonString);
480
+
481
+ // Get the deviceIntegrity object
482
+ JSONObject deviceIntegrity = jsonObject.getJSONObject("deviceIntegrity");
483
+
484
+ JSONArray deviceRecognitionVerdict;
485
+ // Device recognistion will come in non rooted device if not coming then device might be rooted
486
+ try {
487
+ // Get the deviceRecognitionVerdict array
488
+ deviceRecognitionVerdict = deviceIntegrity.getJSONArray("deviceRecognitionVerdict");
489
+ } catch(JSONException error) {
490
+ return true;
491
+ }
492
+
493
+
494
+ // Log the values for debugging
495
+ for (int i = 0; i < deviceRecognitionVerdict.length(); i++) {
496
+ String verdict = deviceRecognitionVerdict.getString(i);
497
+ Log.d(TAG, "Verdict: " + verdict);
498
+ }
499
+
500
+ // Check if the required values are present
501
+ boolean meetsCriteria = false;
502
+ for (int i = 0; i < deviceRecognitionVerdict.length(); i++) {
503
+ String verdict = deviceRecognitionVerdict.getString(i);
504
+ if (verdict.equals("MEETS_BASIC_INTEGRITY") ||
505
+ verdict.equals("MEETS_DEVICE_INTEGRITY") ||
506
+ verdict.equals("MEETS_STRONG_INTEGRITY")) {
507
+ meetsCriteria = true;
508
+ break;
509
+ }
510
+ }
511
+
512
+ if (meetsCriteria) {
513
+
514
+ // The device meets the required integrity criteria
515
+ Log.d(TAG, "Device meets the required integrity criteria.");
516
+ return false;
517
+ } else {
518
+ // The device does not meet the required integrity criteria
519
+ Log.d(TAG, "Device does not meet the required integrity criteria.");
520
+ return true;
521
+ }
522
+
523
+ } catch (JSONException e) {
524
+ Log.e(TAG, "JSON Exception: " + e.getMessage());
525
+ e.printStackTrace();
526
+ }
527
+ return false;
528
+ }
529
+
530
+ private boolean checkNativeLibraryLoaded() {
531
+ boolean libraryLoaded = false;
532
+ try {
533
+ System.loadLibrary("toolChecker");
534
+ libraryLoaded = true;
535
+ } catch (UnsatisfiedLinkError e) {
536
+
537
+ }
538
+ return libraryLoaded;
539
+
540
+ }
541
+
542
+ private boolean checkSuExists() {
543
+ Process process = null;
544
+ Log.i("checkSuExists", String.valueOf(process));
545
+ try {
546
+ Log.i("checkSuExists", String.valueOf(479));
547
+ process = Runtime.getRuntime().exec("ls -lart");
548
+
549
+ BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
550
+ Log.i("checkSuExists", String.valueOf(in.readLine()));
551
+
552
+ return in.readLine() != null;
553
+ } catch (Throwable t) {
554
+ Log.i("checkSuExists", String.valueOf(t));
555
+ return false;
556
+ } finally {
557
+ if (process != null) process.destroy();
558
+ }
559
+ }
560
+
561
+ public boolean checkForBinary(String filename) {
562
+
563
+ String[] pathsArray = Const.getPaths();
564
+ boolean result = false;
565
+
566
+ for (String path : pathsArray) {
567
+ String completePath = path + filename;
568
+ File f = new File(path, filename);
569
+ boolean fileExists = f.exists();
570
+ if (fileExists) {
571
+ result = true;
572
+ }
573
+ }
574
+
575
+ return result;
576
+ }
577
+
578
+ private String[] propsReader() {
579
+ try {
580
+ InputStream inputstream = Runtime.getRuntime().exec("getprop").getInputStream();
581
+ if (inputstream == null) return null;
582
+ String propVal = new Scanner(inputstream).useDelimiter("\\A").next();
583
+ return propVal.split("\n");
584
+ } catch (IOException | NoSuchElementException e) {
585
+ return null;
586
+ }
587
+ }
588
+
589
+ private String[] mountReader() {
590
+ try {
591
+ InputStream inputstream = Runtime.getRuntime().exec("mount").getInputStream();
592
+ if (inputstream == null) return null;
593
+ String propVal = new Scanner(inputstream).useDelimiter("\\A").next();
594
+ return propVal.split("\n");
595
+ } catch (NoSuchElementException | IOException e) {
596
+ return null;
597
+ }
598
+ }
599
+
600
+ public boolean checkForDangerousProps() {
601
+
602
+ final Map<String, String> dangerousProps = new HashMap<>();
603
+ dangerousProps.put("ro.debuggable", "1");
604
+ dangerousProps.put("ro.secure", "0");
605
+
606
+ boolean result = false;
607
+
608
+ String[] lines = propsReader();
609
+
610
+ if (lines == null){
611
+ // Could not read, assume false;
612
+ return false;
613
+ }
614
+
615
+ for (String line : lines) {
616
+ for (String key : dangerousProps.keySet()) {
617
+ if (line.contains(key)) {
618
+ String badValue = dangerousProps.get(key);
619
+ badValue = "[" + badValue + "]";
620
+ if (line.contains(badValue)) {
621
+ result = true;
622
+ }
623
+ }
624
+ }
625
+ }
626
+ return result;
627
+ }
628
+
629
+ /**
630
+ * When you're root you can change the permissions on common system directories, this method checks if any of these patha Const.pathsThatShouldNotBeWritable are writable.
631
+ * @return true if one of the dir is writable
632
+ */
633
+ public boolean checkForRWPaths() {
634
+
635
+ boolean result = false;
636
+
637
+ //Run the command "mount" to retrieve all mounted directories
638
+ String[] lines = mountReader();
639
+
640
+ if (lines == null){
641
+ // Could not read, assume false;
642
+ return false;
643
+ }
644
+
645
+ //The SDK version of the software currently running on this hardware device.
646
+ int sdkVersion = android.os.Build.VERSION.SDK_INT;
647
+ Log.i("sdkVersion", String.valueOf(sdkVersion));
648
+
649
+ for (String line : lines) {
650
+
651
+ // Split lines into parts
652
+ String[] args = line.split(" ");
653
+
654
+ if ((sdkVersion <= android.os.Build.VERSION_CODES.M && args.length < 4)
655
+ || (sdkVersion > android.os.Build.VERSION_CODES.M && args.length < 6)) {
656
+ // If we don't have enough options per line, skip this and log an error
657
+ continue;
658
+ }
659
+
660
+ String mountPoint;
661
+ String mountOptions;
662
+
663
+ /**
664
+ * To check if the device is running Android version higher than Marshmallow or not
665
+ */
666
+ if (sdkVersion > android.os.Build.VERSION_CODES.M) {
667
+ mountPoint = args[2];
668
+ mountOptions = args[5];
669
+ } else {
670
+ mountPoint = args[1];
671
+ mountOptions = args[3];
672
+ }
673
+
674
+ for(String pathToCheck: Const.pathsThatShouldNotBeWritable) {
675
+ if (mountPoint.equalsIgnoreCase(pathToCheck)) {
676
+
677
+ /**
678
+ * If the device is running an Android version above Marshmallow,
679
+ * need to remove parentheses from options parameter;
680
+ */
681
+ if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.M) {
682
+ mountOptions = mountOptions.replace("(", "");
683
+ mountOptions = mountOptions.replace(")", "");
684
+
685
+ }
686
+
687
+ // Split options out and compare against "rw" to avoid false positives
688
+ for (String option : mountOptions.split(",")){
689
+
690
+ if (option.equalsIgnoreCase("rw")){
691
+ result = true;
692
+ break;
693
+ }
694
+ }
695
+ }
696
+ }
697
+ }
698
+
699
+ return result;
700
+ }
701
+
702
+ private boolean checkForMagiskBinary(){ return checkForBinary("magisk"); }
703
+
704
+
705
+
706
+ }