Skip to content

Commit 97f9b18

Browse files
authored
feat: Add support for android root detection and frida detection (#3837)
new build hints: android.rootCheck=true|false android.fridaDetection=true|false
1 parent b28919c commit 97f9b18

File tree

2 files changed

+177
-13
lines changed

2 files changed

+177
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package com.codename1.impl.android;
2+
3+
import android.content.Context;
4+
import android.util.Log;
5+
6+
import java.io.BufferedReader;
7+
import java.io.File;
8+
import java.io.FileInputStream;
9+
import java.io.InputStreamReader;
10+
import java.lang.reflect.Method;
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
14+
public class FridaDetectionUtil {
15+
16+
private static final String TAG = "Codename One";
17+
18+
// List of common Frida process names
19+
private static final String[] FRIDA_PROCESSES = {
20+
"frida-server",
21+
"frida-agent",
22+
"frida-injector",
23+
"frida"
24+
};
25+
26+
// List of common Frida libraries
27+
private static final String[] FRIDA_LIBRARIES = {
28+
"libfrida-gadget.so"
29+
};
30+
31+
// Check for known Frida processes
32+
public static boolean isFridaProcessRunning() {
33+
try {
34+
Process process = Runtime.getRuntime().exec("ps");
35+
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
36+
String line;
37+
while ((line = reader.readLine()) != null) {
38+
for (String processName : FRIDA_PROCESSES) {
39+
if (line.contains(processName)) {
40+
Log.e(TAG, "Frida process detected: " + processName);
41+
return true;
42+
}
43+
}
44+
}
45+
reader.close();
46+
} catch (Exception e) {
47+
Log.e(TAG, "Error checking for Frida processes: " + e.getMessage());
48+
}
49+
return false;
50+
}
51+
52+
// Check for Frida libraries loaded in the app
53+
public static boolean isFridaLibraryLoaded() {
54+
BufferedReader reader = null;
55+
try {
56+
FileInputStream fis = new FileInputStream(new File("/proc/self/maps"));
57+
reader = new BufferedReader(new InputStreamReader(fis));
58+
String line;
59+
while ((line = reader.readLine()) != null) {
60+
for (String lib : FRIDA_LIBRARIES) {
61+
if (line.contains(lib)) {
62+
Log.e(TAG, "Frida library loaded: " + lib);
63+
return true;
64+
}
65+
}
66+
}
67+
} catch (Exception e) {
68+
Log.e(TAG, "Error checking for Frida libraries: " + e.getMessage());
69+
} finally {
70+
if (reader != null) {
71+
try {
72+
reader.close();
73+
} catch (Exception e) {
74+
Log.e(TAG, "Error closing reader: " + e.getMessage());
75+
}
76+
}
77+
}
78+
return false;
79+
}
80+
81+
// Check for suspicious system properties
82+
public static boolean isFridaPropertySet() {
83+
try {
84+
List<String> suspiciousProperties = new ArrayList<String>();
85+
suspiciousProperties.add(getSystemProperty("ro.debuggable"));
86+
suspiciousProperties.add(getSystemProperty("ro.secure"));
87+
88+
for (String property : suspiciousProperties) {
89+
if ("1".equals(property)) {
90+
Log.e(TAG, "Suspicious system property detected: " + property);
91+
return true;
92+
}
93+
}
94+
} catch (Exception e) {
95+
Log.e(TAG, "Error checking for Frida properties: " + e.getMessage());
96+
}
97+
return false;
98+
}
99+
100+
// Get the value of a system property
101+
private static String getSystemProperty(String propName) {
102+
try {
103+
Class clazz = Class.forName("android.os.SystemProperties");
104+
Method method = clazz.getMethod("get", new Class[] { String.class });
105+
return (String) method.invoke(null, new Object[] { propName });
106+
} catch (Exception e) {
107+
Log.e(TAG, "Error getting system property: " + e.getMessage());
108+
return null;
109+
}
110+
}
111+
112+
// Check for Frida-specific classes
113+
public static boolean isFridaClassDetected() {
114+
try {
115+
Class clazz = Class.forName("re.frida.ServerManager");
116+
if (clazz != null) {
117+
Log.e(TAG, "Frida class detected: re.frida.ServerManager");
118+
return true;
119+
}
120+
} catch (ClassNotFoundException e) {
121+
// Expected if Frida is not present
122+
}
123+
return false;
124+
}
125+
126+
// Comprehensive method to run all checks
127+
public static boolean isFridaDetected() {
128+
return isFridaProcessRunning() || isFridaLibraryLoaded() || isFridaPropertySet() || isFridaClassDetected();
129+
}
130+
131+
// Run the detection checks and log the result
132+
public static void runFridaDetection(Context context) {
133+
if (isFridaDetected()) {
134+
Log.e(TAG, "Frida detected! Exiting app.");
135+
System.exit(0);
136+
} else {
137+
Log.i(TAG, "No Frida detection evidence found.");
138+
}
139+
}
140+
}

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java

+37-13
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ public class AndroidGradleBuilder extends Executor {
8686
private String gradle8DistributionUrl = "https://services.gradle.org/distributions/gradle-8.1-bin.zip";
8787
public boolean PREFER_MANAGED_GRADLE=true;
8888

89+
private boolean rootCheck = false;
90+
91+
private boolean fridaDetection = false;
92+
8993
private boolean useGradle8 = true;
9094

9195
// Flag to indicate whether we should strip kotlin from user classes
@@ -491,6 +495,8 @@ public boolean build(File sourceZip, final BuildRequest request) throws BuildExc
491495
boolean facebookSupported = request.getArg("facebook.appId", null) != null;
492496
newFirebaseMessaging = request.getArg("android.newFirebaseMessaging", "true").equals("true");
493497
useGradle8 = request.getArg("android.useGradle8", ""+(useGradle8 || newFirebaseMessaging || facebookSupported)).equals("true");
498+
rootCheck = request.getArg("android.rootCheck", "false").equals("true");
499+
fridaDetection = request.getArg("android.fridaDetection", "false").equals("true");
494500
extendAppCompatActivity = request.getArg("android.extendAppCompatActivity", "false").equals("true");
495501
// When using gradle 8 we need to strip kotlin files from user classes otherwise we get duplicate class errors
496502
stripKotlinFromUserClasses = useGradle8;
@@ -1316,11 +1322,11 @@ public void usesClassMethod(String cls, String method) {
13161322

13171323
debug("-----USING PLAY SERVICES VERSION "+playServicesVersion+"----");
13181324

1325+
String compile = "compile";
1326+
if (useAndroidX || useArrImplementation) {
1327+
compile = "implementation";
1328+
}
13191329
if (useFCM) {
1320-
String compile = "compile";
1321-
if (useAndroidX || useArrImplementation) {
1322-
compile = "implementation";
1323-
}
13241330
if (!googleServicesJson.exists()) {
13251331
error("google-services.json not found. When using FCM for push notifications (i.e. android.messagingService=fcm), you must include valid google-services.json file. Use the Firebase console to add Firebase messaging to your app. https://console.firebase.google.com/u/0/ Then download the google-services.json file and place it in the native/android directory of your project. If you still want to use GCM (which no longer works) define the build hint android.messagingService=gcm", new RuntimeException());
13261332
return false;
@@ -2532,6 +2538,30 @@ public void usesClassMethod(String cls, String method) {
25322538

25332539
}
25342540

2541+
String rootCheckCall = "";
2542+
if (rootCheck) {
2543+
if (!request.getArg("gradleDependencies", "").contains("com.scottyab:rootbeer-lib")) {
2544+
String rootbeerVersion = request.getArg("android.rootbeerVersion", "0.1.0");
2545+
request.putArgument(
2546+
"gradleDependencies",
2547+
request.getArg("gradleDependencies", "") +
2548+
"\n"+compile+" \"com.scottyab:rootbeer-lib:" + rootbeerVersion + "\"\n"
2549+
);
2550+
}
2551+
2552+
rootCheckCall = " com.scottyab.rootbeer.RootBeer rootBeer = new com.scottyab.rootbeer.RootBeer(this);\n"
2553+
+ " if (rootBeer.isRooted()) {\n"
2554+
+ " android.util.Log.e(\"Codename One\", \"Device is rooted. Exiting app.\");\n"
2555+
+ " System.exit(0);\n"
2556+
+ " }\n";
2557+
}
2558+
2559+
String fridaDetectionCall = "";
2560+
if (fridaDetection) {
2561+
fridaDetectionCall = " com.codename1.impl.android.FridaDetectionUtil.runFridaDetection(this);\n";
2562+
}
2563+
2564+
25352565
String waitingForPermissionsRequest=
25362566
" if (isWaitingForPermissionResult()) {\n" +
25372567
" setWaitingForPermissionResult(false);\n" +
@@ -2588,6 +2618,8 @@ public void usesClassMethod(String cls, String method) {
25882618
+ " }\n\n"
25892619
+ " public void onCreate(Bundle savedInstanceState) {\n"
25902620
+ " super.onCreate(savedInstanceState);\n"
2621+
+ fridaDetectionCall
2622+
+ rootCheckCall
25912623
+ facebookHashCode
25922624
+ facebookSupport
25932625
+ streamMode
@@ -2638,7 +2670,6 @@ public void usesClassMethod(String cls, String method) {
26382670
throw new BuildException("Failed to generate stub source code", ex);
26392671
}
26402672

2641-
26422673
String fcmRegisterPushCode = "";
26432674
if (useFCM) {
26442675
if (newFirebaseMessaging) {
@@ -3319,10 +3350,6 @@ public void usesClassMethod(String cls, String method) {
33193350
request.putArgument("var.android.playServicesVersion", playServicesVersion);
33203351
String additionalDependencies = request.getArg("gradleDependencies", "");
33213352
if (facebookSupported) {
3322-
String compile = "compile";
3323-
if (useAndroidX || useArrImplementation) {
3324-
compile = "implementation";
3325-
}
33263353
minSDK = maxInt("15", minSDK);
33273354

33283355
if(request.getArg("android.excludeBolts", "false").equals("true")) {
@@ -3335,10 +3362,7 @@ public void usesClassMethod(String cls, String method) {
33353362
facebookSdkVersion + "'\n";
33363363
}
33373364
}
3338-
String compile = "compile";
3339-
if (useAndroidX || useArrImplementation) {
3340-
compile = "implementation";
3341-
}
3365+
33423366
if (legacyGplayServicesMode) {
33433367
additionalDependencies += " "+compile+" 'com.google.android.gms:play-services:6.5.87'\n";
33443368
} else {

0 commit comments

Comments
 (0)