Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for android root detection and frida detection #3837

Merged
merged 1 commit into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions Ports/Android/src/com/codename1/impl/android/FridaDetectionUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.codename1.impl.android;

import android.content.Context;
import android.util.Log;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class FridaDetectionUtil {

private static final String TAG = "Codename One";

// List of common Frida process names
private static final String[] FRIDA_PROCESSES = {
"frida-server",
"frida-agent",
"frida-injector",
"frida"
};

// List of common Frida libraries
private static final String[] FRIDA_LIBRARIES = {
"libfrida-gadget.so"
};

// Check for known Frida processes
public static boolean isFridaProcessRunning() {
try {
Process process = Runtime.getRuntime().exec("ps");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
for (String processName : FRIDA_PROCESSES) {
if (line.contains(processName)) {
Log.e(TAG, "Frida process detected: " + processName);
return true;
}
}
}
reader.close();
} catch (Exception e) {
Log.e(TAG, "Error checking for Frida processes: " + e.getMessage());
}
return false;
}

// Check for Frida libraries loaded in the app
public static boolean isFridaLibraryLoaded() {
BufferedReader reader = null;
try {
FileInputStream fis = new FileInputStream(new File("/proc/self/maps"));
reader = new BufferedReader(new InputStreamReader(fis));
String line;
while ((line = reader.readLine()) != null) {
for (String lib : FRIDA_LIBRARIES) {
if (line.contains(lib)) {
Log.e(TAG, "Frida library loaded: " + lib);
return true;
}
}
}
} catch (Exception e) {
Log.e(TAG, "Error checking for Frida libraries: " + e.getMessage());
} finally {
if (reader != null) {
try {
reader.close();
} catch (Exception e) {
Log.e(TAG, "Error closing reader: " + e.getMessage());
}
}
}
return false;
}

// Check for suspicious system properties
public static boolean isFridaPropertySet() {
try {
List<String> suspiciousProperties = new ArrayList<String>();
suspiciousProperties.add(getSystemProperty("ro.debuggable"));
suspiciousProperties.add(getSystemProperty("ro.secure"));

for (String property : suspiciousProperties) {
if ("1".equals(property)) {
Log.e(TAG, "Suspicious system property detected: " + property);
return true;
}
}
} catch (Exception e) {
Log.e(TAG, "Error checking for Frida properties: " + e.getMessage());
}
return false;
}

// Get the value of a system property
private static String getSystemProperty(String propName) {
try {
Class clazz = Class.forName("android.os.SystemProperties");
Method method = clazz.getMethod("get", new Class[] { String.class });
return (String) method.invoke(null, new Object[] { propName });
} catch (Exception e) {
Log.e(TAG, "Error getting system property: " + e.getMessage());
return null;
}
}

// Check for Frida-specific classes
public static boolean isFridaClassDetected() {
try {
Class clazz = Class.forName("re.frida.ServerManager");
if (clazz != null) {
Log.e(TAG, "Frida class detected: re.frida.ServerManager");
return true;
}
} catch (ClassNotFoundException e) {
// Expected if Frida is not present
}
return false;
}

// Comprehensive method to run all checks
public static boolean isFridaDetected() {
return isFridaProcessRunning() || isFridaLibraryLoaded() || isFridaPropertySet() || isFridaClassDetected();
}

// Run the detection checks and log the result
public static void runFridaDetection(Context context) {
if (isFridaDetected()) {
Log.e(TAG, "Frida detected! Exiting app.");
System.exit(0);
} else {
Log.i(TAG, "No Frida detection evidence found.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ public class AndroidGradleBuilder extends Executor {
private String gradle8DistributionUrl = "https://services.gradle.org/distributions/gradle-8.1-bin.zip";
public boolean PREFER_MANAGED_GRADLE=true;

private boolean rootCheck = false;

private boolean fridaDetection = false;

private boolean useGradle8 = true;

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

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

String compile = "compile";
if (useAndroidX || useArrImplementation) {
compile = "implementation";
}
if (useFCM) {
String compile = "compile";
if (useAndroidX || useArrImplementation) {
compile = "implementation";
}
if (!googleServicesJson.exists()) {
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());
return false;
Expand Down Expand Up @@ -2532,6 +2538,30 @@ public void usesClassMethod(String cls, String method) {

}

String rootCheckCall = "";
if (rootCheck) {
if (!request.getArg("gradleDependencies", "").contains("com.scottyab:rootbeer-lib")) {
String rootbeerVersion = request.getArg("android.rootbeerVersion", "0.1.0");
request.putArgument(
"gradleDependencies",
request.getArg("gradleDependencies", "") +
"\n"+compile+" \"com.scottyab:rootbeer-lib:" + rootbeerVersion + "\"\n"
);
}

rootCheckCall = " com.scottyab.rootbeer.RootBeer rootBeer = new com.scottyab.rootbeer.RootBeer(this);\n"
+ " if (rootBeer.isRooted()) {\n"
+ " android.util.Log.e(\"Codename One\", \"Device is rooted. Exiting app.\");\n"
+ " System.exit(0);\n"
+ " }\n";
}

String fridaDetectionCall = "";
if (fridaDetection) {
fridaDetectionCall = " com.codename1.impl.android.FridaDetectionUtil.runFridaDetection(this);\n";
}


String waitingForPermissionsRequest=
" if (isWaitingForPermissionResult()) {\n" +
" setWaitingForPermissionResult(false);\n" +
Expand Down Expand Up @@ -2588,6 +2618,8 @@ public void usesClassMethod(String cls, String method) {
+ " }\n\n"
+ " public void onCreate(Bundle savedInstanceState) {\n"
+ " super.onCreate(savedInstanceState);\n"
+ fridaDetectionCall
+ rootCheckCall
+ facebookHashCode
+ facebookSupport
+ streamMode
Expand Down Expand Up @@ -2638,7 +2670,6 @@ public void usesClassMethod(String cls, String method) {
throw new BuildException("Failed to generate stub source code", ex);
}


String fcmRegisterPushCode = "";
if (useFCM) {
if (newFirebaseMessaging) {
Expand Down Expand Up @@ -3319,10 +3350,6 @@ public void usesClassMethod(String cls, String method) {
request.putArgument("var.android.playServicesVersion", playServicesVersion);
String additionalDependencies = request.getArg("gradleDependencies", "");
if (facebookSupported) {
String compile = "compile";
if (useAndroidX || useArrImplementation) {
compile = "implementation";
}
minSDK = maxInt("15", minSDK);

if(request.getArg("android.excludeBolts", "false").equals("true")) {
Expand All @@ -3335,10 +3362,7 @@ public void usesClassMethod(String cls, String method) {
facebookSdkVersion + "'\n";
}
}
String compile = "compile";
if (useAndroidX || useArrImplementation) {
compile = "implementation";
}

if (legacyGplayServicesMode) {
additionalDependencies += " "+compile+" 'com.google.android.gms:play-services:6.5.87'\n";
} else {
Expand Down
Loading