From 0d6578160be0fc995811d112dc2ed7a89326cfd4 Mon Sep 17 00:00:00 2001 From: Steve Hannah Date: Sat, 31 Aug 2024 08:17:34 -0700 Subject: [PATCH] feat: Add support for android root detection and frida detection new build hints: android.rootCheck=true|false android.fridaDetection=true|false --- .../impl/android/FridaDetectionUtil.java | 140 ++++++++++++++++++ .../builders/AndroidGradleBuilder.java | 50 +++++-- 2 files changed, 177 insertions(+), 13 deletions(-) create mode 100644 Ports/Android/src/com/codename1/impl/android/FridaDetectionUtil.java diff --git a/Ports/Android/src/com/codename1/impl/android/FridaDetectionUtil.java b/Ports/Android/src/com/codename1/impl/android/FridaDetectionUtil.java new file mode 100644 index 0000000000..9f7a6373b5 --- /dev/null +++ b/Ports/Android/src/com/codename1/impl/android/FridaDetectionUtil.java @@ -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 suspiciousProperties = new ArrayList(); + 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."); + } + } +} diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java index c6523b425d..b1ddcf4cd5 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java @@ -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 @@ -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; @@ -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; @@ -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" + @@ -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 @@ -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) { @@ -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")) { @@ -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 {