Skip to content
This repository was archived by the owner on Jan 26, 2022. It is now read-only.
Open
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
Original file line number Diff line number Diff line change
@@ -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<String> createMapsReplacementSet() {
Set<String> 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@[email protected]@classes.dex");
replacement.add("/data/dalvik-cache/" + odexArch + "/system@[email protected]@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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
2 changes: 2 additions & 0 deletions remote-droid-guard/src/main/jni/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.so

6 changes: 6 additions & 0 deletions remote-droid-guard/src/main/jni/Makefile
Original file line number Diff line number Diff line change
@@ -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

100 changes: 100 additions & 0 deletions remote-droid-guard/src/main/jni/dalvik_hook.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
Library to hook dalvik methods in runtime.
The idea came from https://github.com/crmulliner/ddi
*/

#include <dlfcn.h>
#include <android/log.h>
#include <stdlib.h>

#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;");
}

51 changes: 51 additions & 0 deletions remote-droid-guard/src/main/jni/dalvik_hook.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <stdint.h>

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. "<init>" 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);