Skip to content
This repository has been archived by the owner on Jun 1, 2023. It is now read-only.

Commit

Permalink
Close certain files temporarily before forking
Browse files Browse the repository at this point in the history
This is to work around a new security feature introduced by Google in
the November 16 patches. It would abort the runtime if files are open
that are not whitelisted. To avoid this, we close the files ourselves
and reopen them after Zygote has forked. The mechanism is the same that
Google has introduced, with a few modifications to fit Xposed's needs.

This requires also a change on the native side.

Fixes #129.
  • Loading branch information
rovo89 committed Nov 24, 2016
1 parent 9e88044 commit 0b4ec2c
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 0 deletions.
3 changes: 3 additions & 0 deletions app/src/main/java/de/robv/android/xposed/XposedBridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@ public static Object invokeOriginalMethod(Member method, Object thisObject, Obje

private static native Object cloneToSubclassNative(Object obj, Class<?> targetClazz);

/*package*/ static native void closeFilesBeforeForkNative();
/*package*/ static native void reopenFilesAfterForkNative();

/** @hide */
public static final class CopyOnWriteSortedSet<E> {
private transient volatile Object[] elements = EMPTY_ARRAY;
Expand Down
21 changes: 21 additions & 0 deletions app/src/main/java/de/robv/android/xposed/XposedHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import android.content.res.AssetManager;
import android.content.res.Resources;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
Expand Down Expand Up @@ -1598,6 +1601,24 @@ protected AtomicInteger initialValue() {
}
}

/*package*/ static boolean fileContains(File file, String str) throws IOException {
// There are certainly more efficient algorithms (e.g. Boyer-Moore used in grep),
// but the naive approach should be sufficient here.
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(file));
String line;
while ((line = in.readLine()) != null) {
if (line.contains(str)) {
return true;
}
}
return false;
} finally {
closeSilently(in);
}
}

//#################################################################################################
// TODO helpers for view traversing
/*To make it easier, I will try and implement some more helpers:
Expand Down
38 changes: 38 additions & 0 deletions app/src/main/java/de/robv/android/xposed/XposedInit.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import android.content.res.TypedArray;
import android.content.res.XResources;
import android.os.Build;
import android.os.Environment;
import android.os.Process;
import android.util.Log;

Expand All @@ -36,7 +37,9 @@
import static de.robv.android.xposed.XposedBridge.hookAllConstructors;
import static de.robv.android.xposed.XposedBridge.hookAllMethods;
import static de.robv.android.xposed.XposedHelpers.closeSilently;
import static de.robv.android.xposed.XposedHelpers.fileContains;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import static de.robv.android.xposed.XposedHelpers.findClass;
import static de.robv.android.xposed.XposedHelpers.getBooleanField;
import static de.robv.android.xposed.XposedHelpers.getObjectField;
import static de.robv.android.xposed.XposedHelpers.setObjectField;
Expand All @@ -61,6 +64,24 @@ private XposedInit() {}
* Hook some methods which we want to create an easier interface for developers.
*/
/*package*/ static void initForZygote() throws Throwable {
if (needsToCloseFilesForFork()) {
XC_MethodHook callback = new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.closeFilesBeforeForkNative();
}

@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.reopenFilesAfterForkNative();
}
};

Class<?> zygote = findClass("com.android.internal.os.Zygote", null);
hookAllMethods(zygote, "nativeForkAndSpecialize", callback);
hookAllMethods(zygote, "nativeForkSystemServer", callback);
}

final HashSet<String> loadedPackagesInProcess = new HashSet<>(1);

// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
Expand Down Expand Up @@ -332,6 +353,23 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XResources.init(latestResKey);
}

private static boolean needsToCloseFilesForFork() {
if (Build.VERSION.SDK_INT >= 24) {
return true;
} else if (Build.VERSION.SDK_INT < 21) {
return false;
}

File lib = new File(Environment.getRootDirectory(), "lib/libandroid_runtime.so");
try {
return fileContains(lib, "Unable to construct file descriptor table");
} catch (IOException e) {
Log.e(TAG, "Could not check whether " + lib + " has security patch level 5");
// In doubt, just do it. The worst case should be unnecessary work and log messages.
return true;
}
}

private static void hookXposedInstaller(ClassLoader classLoader) {
try {
findAndHookMethod(INSTALLER_PACKAGE_NAME + ".XposedApp", classLoader, "getActiveXposedVersion",
Expand Down

0 comments on commit 0b4ec2c

Please sign in to comment.