diff --git a/remote-droid-guard/src/main/java/org/microg/gms/droidguard/DalvikHook.java b/remote-droid-guard/src/main/java/org/microg/gms/droidguard/DalvikHook.java new file mode 100644 index 0000000..9e32b79 --- /dev/null +++ b/remote-droid-guard/src/main/java/org/microg/gms/droidguard/DalvikHook.java @@ -0,0 +1,200 @@ +/* + * Copyright 2013-2016 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.microg.gms.droidguard; + +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.telephony.TelephonyManager; +import android.util.Log; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.microg.gms.droidguard.Constants.GMS_PACKAGE_NAME; + +public class DalvikHook { + private static final String TAG = "DalvikHook"; + + static boolean done = false; + static String odexArch = "arm"; // or "arm64" + static String checksum; + + // From TelephonyManager + static String deviceId; + static String subscriberId; + + public synchronized static void activate(String odexArch, String checksum, String deviceId, String subscriberId) { + DalvikHook.odexArch = odexArch; + DalvikHook.checksum = checksum; + DalvikHook.deviceId = deviceId; + DalvikHook.subscriberId = subscriberId; + if (done) return; + done = true; + System.loadLibrary("dalvikhook"); + } + + //Hook android.telephony.TelephonyManager->getSubscriberId + public static String TelephonyManager_getSubscriberId(TelephonyManager tm) { + Log.d(TAG, "Set subscriber id: " + DalvikHook.subscriberId); + return subscriberId; + } + + //Hook android.telephony.TelephonyManager->getDeviceId + public static String TelephonyManager_getDeviceId(TelephonyManager tm) { + Log.d(TAG, "Set device id: " + DalvikHook.deviceId); + return deviceId; + } + + //Hook android.telephony.TelephonyManager->getDeviceId + public static String TelephonyManager_getDeviceId(TelephonyManager tm, int slot) { + Log.d(TAG, "Set device id 2: " + DalvikHook.deviceId); + return deviceId; + } + + //Hook android.net.ConnectivityManager->getActiveNetworkInfo + public static NetworkInfo ConnectivityManager_getActiveNetworkInfo(ConnectivityManager cm) { + return null; // null is a valid response + } + + //Hook java.util.regex.Pattern->matcher + public static Matcher Pattern_matcher(Pattern pattern, CharSequence cs) { + if (cs instanceof String) { + String s = (String) cs; + if (s.contains("Xposed") || s.contains("xposed") || s.contains("XPosed") || s.contains("XPOSED")) + return DalvikHook.Pattern_matcher(pattern, ""); + if (s.contains("org.microg.gms.droidguard")) + return DalvikHook.Pattern_matcher(pattern, s.replace("org.microg.gms.droidguard", "com.google.android.gms")); + } + return DalvikHook.Pattern_matcher(pattern, cs); + } + + //Hook android.content.ContextWrapper->getPackageName + public static String ContextWrapper_getPackageName(Object o) { + Log.d(TAG, "Set package name: " + GMS_PACKAGE_NAME); + return GMS_PACKAGE_NAME; + } + + //Hook android.os.SystemProperties->getInt + public static int SystemProperties_getInt(String key, int def) { + if (key.equals("ro.debuggable") || key.equals("service.adb.root")) + return 0; + if (key.equals("ro.secure")) + return 1; + return DalvikHook.SystemProperties_getInt(key, def); + } + + //Hook java.util.Arrays->asList + public static List Arrays_asList(Object[] objs) { + Log.d(TAG, "Arrays->asList: "+Arrays.toString(objs)); + if (detectLibrariesList(objs)) + return DalvikHook.Arrays_asList(getModifiedSystemSharedLibraries()); + return DalvikHook.Arrays_asList(objs); + } + + static boolean detectLibrariesList(Object[] list) { + boolean isList = false; + boolean wasModified = false; + for (int i = 0; i < list.length; i++) { + if (!(list[i] instanceof String)) + return false; + if ("com.android.location.provider".equals(list[i]) || "android.test.runner".equals(list[i])) + isList = true; + if ("com.google.widevine.software.drm".equals(list[i])) + wasModified = true; + } + return isList && !wasModified; + } + + //Hook android.app.ApplicationPackageManager->getSystemSharedLibraryNames + public static String[] PackageManager_getSystemSharedLibraryNames(Object o) { + Log.d(TAG, "Modified SystemSharedLibraries"); + return getModifiedSystemSharedLibraries(); + } + + static String[] getModifiedSystemSharedLibraries() { + return new String[]{"android.test.runner", + "com.android.future.usb.accessory", + "com.android.location.provider", + "com.android.media.remotedisplay", + "com.android.mediadrm.signer", + "com.google.android.maps", + "com.google.widevine.software.drm", + "com.qualcomm.qcrilhook", + "com.qualcomm.qti.rcsservice", + "com.quicinc.cneapiclient", + "javax.obex", + "org.apache.http.legacy"}; + } + + //Hook android.content.ContextWrapper->getClassLoader + public static ClassLoader ContextWrapper_getClassLoader(Object o) { + return createModifiedClassLoader((ClassLoader) DalvikHook.ContextWrapper_getClassLoader(o)); + } + + static ClassLoader createModifiedClassLoader(ClassLoader original) { + return new URLClassLoader(new URL[0], original) { + @Override + public String toString() { + return "dalvik.system.PathClassLoader[DexPathList[[zip file \"/system/framework/com.android.location.provider.jar\", zip file \"/system/framework/com.android.media.remotedisplay.jar\", zip file \"/data/app/com.google.android.gms-1/base.apk\"],nativeLibraryDirectories=[/data/app/com.google.android.gms-1/lib/arm, /data/app/com.google.android.gms-1/base.apk!/lib/armeabi-v7a, /vendor/lib, /system/lib]]]"; + } + }; + } + + //Hook java.util.TreeSet->iterator + public static Iterator TreeSet_iterator(TreeSet set) { + if (detectMapsSet((Iterator) DalvikHook.TreeSet_iterator(set))) + return DalvikHook.TreeSet_iterator((TreeSet) createMapsReplacementSet()); + return DalvikHook.TreeSet_iterator(set); + } + + static boolean detectMapsSet(Iterator iterator) { + boolean hasDgCache = false; + boolean hasFrameworkRes = false; + while (iterator.hasNext()) { + Object o = iterator.next(); + if (!(o instanceof String)) { + return false; + } + String s = (String) o; + Log.d(TAG, "mapdetect: " + s); + if (s.contains("app_dg_cache")) hasDgCache = true; + if (s.contains("framework-res.apk")) hasFrameworkRes = true; + } + return hasDgCache && hasFrameworkRes; + } + + static Set createMapsReplacementSet() { + Set replacement = new TreeSet<>(); + replacement.add("/data/app/com.google.android.gms-1/base.apk"); + replacement.add("/data/app/com.google.android.gms-1/oat/" + odexArch + "/base.odex"); + replacement.add("/data/dalvik-cache/" + odexArch + "/system@framework@com.android.location.provider.jar@classes.dex"); + replacement.add("/data/dalvik-cache/" + odexArch + "/system@framework@com.android.media.remotedisplay.jar@classes.dex"); + replacement.add("/data/data/com.google.android.gms/app_dg_cache/" + checksum.toUpperCase() + "/opt/the.dex"); + replacement.add("/data/data/com.google.android.gms/app_fb/f.dex (deleted)"); + replacement.add("/system/framework/com.android.location.provider.jar"); + replacement.add("/system/framework/com.android.media.remotedisplay.jar"); + replacement.add("/system/framework/framework-res.apk"); + return replacement; + } +} diff --git a/remote-droid-guard/src/main/java/org/microg/gms/droidguard/DroidguardHelper.java b/remote-droid-guard/src/main/java/org/microg/gms/droidguard/DroidguardHelper.java index d6fc979..1b2b21c 100644 --- a/remote-droid-guard/src/main/java/org/microg/gms/droidguard/DroidguardHelper.java +++ b/remote-droid-guard/src/main/java/org/microg/gms/droidguard/DroidguardHelper.java @@ -88,7 +88,13 @@ public static byte[] guard(Context context, RemoteDroidGuardRequest request) thr } String odexArch = context.getClassLoader().toString().contains("arm64") ? "arm64" : "arm"; - SysHook.activate(odexArch, checksum, request.deviceId, request.subscriberId); + String vmVersion = System.getProperty("java.vm.version"); + if (vmVersion != null && vmVersion.startsWith("2")) { + SysHook.activate(odexArch, checksum, request.deviceId, request.subscriberId); + } + else { + DalvikHook.activate(odexArch, checksum, request.deviceId, request.subscriberId); + } return invoke(context, clazz, request.packageName, request.reason, response.byteCode.toByteArray(), request.androidIdLong, request.extras); } diff --git a/remote-droid-guard/src/main/jni/.gitignore b/remote-droid-guard/src/main/jni/.gitignore new file mode 100644 index 0000000..ddaacd8 --- /dev/null +++ b/remote-droid-guard/src/main/jni/.gitignore @@ -0,0 +1,2 @@ +*.so + diff --git a/remote-droid-guard/src/main/jni/Makefile b/remote-droid-guard/src/main/jni/Makefile new file mode 100644 index 0000000..c55d33e --- /dev/null +++ b/remote-droid-guard/src/main/jni/Makefile @@ -0,0 +1,6 @@ +all: + $(TOOL_CHAIN)gcc -o libdalvikhook.so -shared -llog dalvik_hook.c + +install: + adb push libdalvikhook.so /data/data/org.microg.gms.droidguard/lib/libdalvikhook.so + diff --git a/remote-droid-guard/src/main/jni/dalvik_hook.c b/remote-droid-guard/src/main/jni/dalvik_hook.c new file mode 100644 index 0000000..1b52df1 --- /dev/null +++ b/remote-droid-guard/src/main/jni/dalvik_hook.c @@ -0,0 +1,100 @@ +/* +Library to hook dalvik methods in runtime. +The idea came from https://github.com/crmulliner/ddi +*/ + +#include +#include +#include + +#include "dalvik_hook.h" + +#define LOG_TAG "dalvik_hook" +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) + +static struct dvm_functions d; + +void dalvik_hook_resolv_dvm() +{ + void *handle = dlopen("libdvm.so", RTLD_NOW); + if(!handle) + LOGE("error loading libdvm.so\n"); + + d.dvmFindLoadedClass = dlsym(handle, "_Z18dvmFindLoadedClassPKc"); + if (!d.dvmFindLoadedClass) + d.dvmFindLoadedClass = dlsym(handle, "dvmFindLoadedClass"); + + d.dvmFindVirtualMethodHierByDescriptor = dlsym(handle, "_Z36dvmFindVirtualMethodHierByDescriptorPK11ClassObjectPKcS3_"); + if (!d.dvmFindVirtualMethodHierByDescriptor) + d.dvmFindVirtualMethodHierByDescriptor = dlsym(handle, "dvmFindVirtualMethodHierByDescriptor"); + + d.dvmFindDirectMethodByDescriptor = dlsym(handle, "_Z31dvmFindDirectMethodByDescriptorPK11ClassObjectPKcS3_"); + if (!d.dvmFindDirectMethodByDescriptor) + d.dvmFindDirectMethodByDescriptor = dlsym(handle, "dvmFindDirectMethodByDescriptor"); +} + +void dalvik_hook_add(char *cls_name, char *mthd_name, char *sig_str, char *cls_name_hk, char *mthd_name_hk, char *sig_str_hk) +{ + void *cls_ptr; + struct method *method; + struct method *method_hook; + struct method *buf; + LOGD("hooking method %s %s %s\n", cls_name, mthd_name, sig_str); + //get origin method + cls_ptr = d.dvmFindLoadedClass(cls_name); + method = d.dvmFindVirtualMethodHierByDescriptor(cls_ptr, mthd_name, sig_str); + if(!method) + method = d.dvmFindDirectMethodByDescriptor(cls_ptr, mthd_name, sig_str); + if(!method) + LOGD("error finding method %s %s %s\n", cls_name, mthd_name, sig_str); + //get hook method + cls_ptr = d.dvmFindLoadedClass(cls_name_hk); + method_hook = d.dvmFindVirtualMethodHierByDescriptor(cls_ptr, mthd_name_hk, sig_str_hk); + if(!method_hook) + method_hook = d.dvmFindDirectMethodByDescriptor(cls_ptr, mthd_name_hk, sig_str_hk); + if(!method_hook) + LOGD("error finding method %s %s %s\n", cls_name_hk, mthd_name_hk, sig_str_hk); + //switch methods + buf = malloc(sizeof(struct method)); + memcpy(buf, method, sizeof(struct method)); //backup origin method + method->insns = method_hook->insns; + method->clazz = method_hook->clazz; + method->registersSize=method_hook->registersSize; + method->outsSize=method_hook->outsSize; + method->insSize=method_hook->insSize; + method_hook->clazz = buf->clazz; //write backup to hook method + method_hook->insns = buf->insns; + method_hook->registersSize=buf->registersSize; + method_hook->outsSize=buf->outsSize; + method_hook->insSize=buf->insSize; + free(buf); +} + +__attribute__((constructor)) +void dalvik_hook_init(void) +{ + LOGD("starting dalvik_hook\n"); + dalvik_hook_resolv_dvm(); + dalvik_hook_add("Landroid/telephony/TelephonyManager;", "getSubscriberId", "()Ljava/lang/String;", + "Lorg/microg/gms/droidguard/DalvikHook;", "TelephonyManager_getSubscriberId", "(Landroid/telephony/TelephonyManager;)Ljava/lang/String;"); + dalvik_hook_add("Landroid/telephony/TelephonyManager;", "getDeviceId", "()Ljava/lang/String;", + "Lorg/microg/gms/droidguard/DalvikHook;", "TelephonyManager_getDeviceId", "(Landroid/telephony/TelephonyManager;)Ljava/lang/String;"); + dalvik_hook_add("Landroid/net/ConnectivityManager;", "getActiveNetworkInfo", "()Landroid/net/NetworkInfo;", + "Lorg/microg/gms/droidguard/DalvikHook;", "ConnectivityManager_getActiveNetworkInfo", "(Landroid/net/ConnectivityManager;)Landroid/net/NetworkInfo;"); + dalvik_hook_add("Landroid/content/ContextWrapper;", "getPackageName", "()Ljava/lang/String;", + "Lorg/microg/gms/droidguard/DalvikHook;", "ContextWrapper_getPackageName", "(Ljava/lang/Object;)Ljava/lang/String;"); + dalvik_hook_add("Landroid/content/ContextWrapper;", "getClassLoader", "()Ljava/lang/ClassLoader;", + "Lorg/microg/gms/droidguard/DalvikHook;", "ContextWrapper_getClassLoader", "(Ljava/lang/Object;)Ljava/lang/ClassLoader;"); + dalvik_hook_add("Ljava/util/TreeSet;", "iterator", "()Ljava/util/Iterator;", + "Lorg/microg/gms/droidguard/DalvikHook;", "TreeSet_iterator", "(Ljava/util/TreeSet;)Ljava/util/Iterator;"); + dalvik_hook_add("Ljava/util/regex/Pattern;", "matcher", "(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;", + "Lorg/microg/gms/droidguard/DalvikHook;", "Pattern_matcher", "(Ljava/util/regex/Pattern;Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;"); + dalvik_hook_add("Landroid/os/SystemProperties;", "getInt", "(Ljava/lang/String;I)I", + "Lorg/microg/gms/droidguard/DalvikHook;", "SystemProperties_getInt", "(Ljava/lang/String;I)I"); + dalvik_hook_add("Ljava/util/Arrays;", "asList", "([Ljava/lang/Object;)Ljava/util/List;", + "Lorg/microg/gms/droidguard/DalvikHook;", "Arrays_asList", "([Ljava/lang/Object;)Ljava/util/List;"); + dalvik_hook_add("Landroid/app/ApplicationPackageManager;", "getSystemSharedLibraryNames", "()[Ljava/lang/String;", + "Lorg/microg/gms/droidguard/DalvikHook;", "PackageManager_getSystemSharedLibraryNames", "(Ljava/lang/Object;)[Ljava/lang/String;"); +} + diff --git a/remote-droid-guard/src/main/jni/dalvik_hook.h b/remote-droid-guard/src/main/jni/dalvik_hook.h new file mode 100644 index 0000000..cfd80e1 --- /dev/null +++ b/remote-droid-guard/src/main/jni/dalvik_hook.h @@ -0,0 +1,51 @@ +#include + +struct dvm_functions +{ + void* (*dvmFindLoadedClass)(const char*); + void* (*dvmFindVirtualMethodHierByDescriptor)(void*, const char*, const char*); + void* (*dvmFindDirectMethodByDescriptor)(void*, const char*, const char*); +}; + +struct method { + void *clazz; + uint32_t a; // accessflags + + uint16_t methodIndex; + + uint16_t registersSize; /* ins + locals */ + uint16_t outsSize; + uint16_t insSize; + + /* method name, e.g. "" or "eatLunch" */ + const char* name; + + /* + * Method prototype descriptor string (return and argument types). + * + * TODO: This currently must specify the DexFile as well as the proto_ids + * index, because generated Proxy classes don't have a DexFile. We can + * remove the DexFile* and reduce the size of this struct if we generate + * a DEX for proxies. + */ + struct DexProto { + uint32_t* dexFile; /* file the idx refers to */ + uint32_t protoIdx; /* index into proto_ids table of dexFile */ + } prototype; + + /* short-form method descriptor string */ + const char* shorty; + + /* + * The remaining items are not used for abstract or native methods. + * (JNI is currently hijacking "insns" as a function pointer, set + * after the first call. For internal-native this stays null.) + */ + + /* the actual code */ + uint16_t* insns; +}; + +void dalvik_hook_resolv_dvm(); +void dalvik_hook_add(char *cls_name, char *mthd_name, char *sig_str, char *cls_name_hk, char *mthd_name_hk, char *sig_str_hk); +