From 711c589088874804f8d0489f579bc4444a528b06 Mon Sep 17 00:00:00 2001 From: solohsu Date: Sun, 21 Apr 2019 11:46:58 +0800 Subject: [PATCH 01/13] Add support for resources hooking. (1/2) --- edxp-core/jni/main/yahfa/HookMain.c | 4 +- hiddenapi-stubs/libs/framework-stub.jar | Bin 14886 -> 15348 bytes .../java/android/content/res/Resources.java | 8 + .../android/content/res/ResourcesImpl.java | 4 + .../java/android/content/res/TypedArray.java | 4 + .../java/android/content/res/XResources.java | 265 +++++++++--------- .../de/robv/android/xposed/XposedBridge.java | 7 +- .../de/robv/android/xposed/XposedInit.java | 233 ++++++++++++++- 8 files changed, 390 insertions(+), 135 deletions(-) create mode 100644 hiddenapi-stubs/src/main/java/android/content/res/ResourcesImpl.java diff --git a/edxp-core/jni/main/yahfa/HookMain.c b/edxp-core/jni/main/yahfa/HookMain.c index ac317049..a3c522ab 100644 --- a/edxp-core/jni/main/yahfa/HookMain.c +++ b/edxp-core/jni/main/yahfa/HookMain.c @@ -203,7 +203,9 @@ static void ensureMethodCached(void *hookMethod, void *backupMethod, int methodIndex = read32( (void *) ((char *) backupMethod + OFFSET_dex_method_index_in_ArtMethod)); - LOGI("methodIndex = %d", methodIndex); + if (methodIndex >= 512) { + LOGW("methodIndex = %d", methodIndex); + } // update the cached method manually // first we find the array of cached methods diff --git a/hiddenapi-stubs/libs/framework-stub.jar b/hiddenapi-stubs/libs/framework-stub.jar index 6ae1ebdec61fa3e4eed65942bc6730b3eb17e0df..b1d3a0fb55a4d3f9d54c79159a43bc35caf6e62e 100644 GIT binary patch delta 4013 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 NhzSe=Ns|NF{{>U$ttkKi delta 3584 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 DPmVau diff --git a/hiddenapi-stubs/src/main/java/android/content/res/Resources.java b/hiddenapi-stubs/src/main/java/android/content/res/Resources.java index 545d7d36..7894d566 100644 --- a/hiddenapi-stubs/src/main/java/android/content/res/Resources.java +++ b/hiddenapi-stubs/src/main/java/android/content/res/Resources.java @@ -25,6 +25,14 @@ public class Resources { throw new UnsupportedOperationException("STUB"); } + public Resources(ClassLoader classLoader) { + throw new UnsupportedOperationException("STUB"); + } + + public void setImpl(ResourcesImpl impl) { + throw new UnsupportedOperationException("STUB"); + } + public static Resources getSystem() { throw new UnsupportedOperationException("STUB"); } diff --git a/hiddenapi-stubs/src/main/java/android/content/res/ResourcesImpl.java b/hiddenapi-stubs/src/main/java/android/content/res/ResourcesImpl.java new file mode 100644 index 00000000..3f29d11d --- /dev/null +++ b/hiddenapi-stubs/src/main/java/android/content/res/ResourcesImpl.java @@ -0,0 +1,4 @@ +package android.content.res; + +public class ResourcesImpl { +} diff --git a/hiddenapi-stubs/src/main/java/android/content/res/TypedArray.java b/hiddenapi-stubs/src/main/java/android/content/res/TypedArray.java index 53256c1b..f45897c2 100644 --- a/hiddenapi-stubs/src/main/java/android/content/res/TypedArray.java +++ b/hiddenapi-stubs/src/main/java/android/content/res/TypedArray.java @@ -8,6 +8,10 @@ public class TypedArray { throw new UnsupportedOperationException("STUB"); } + protected TypedArray(Resources resources) { + throw new UnsupportedOperationException("STUB"); + } + protected TypedArray(Resources resources, int[] data, int[] indices, int len) { throw new UnsupportedOperationException("STUB"); } diff --git a/xposed-bridge/src/main/java/android/content/res/XResources.java b/xposed-bridge/src/main/java/android/content/res/XResources.java index e8d67258..c8a4fad9 100644 --- a/xposed-bridge/src/main/java/android/content/res/XResources.java +++ b/xposed-bridge/src/main/java/android/content/res/XResources.java @@ -34,8 +34,6 @@ import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet; import de.robv.android.xposed.callbacks.XC_LayoutInflated; import de.robv.android.xposed.callbacks.XC_LayoutInflated.LayoutInflatedParam; import de.robv.android.xposed.callbacks.XCallback; -import xposed.dummy.XResourcesSuperClass; -import xposed.dummy.XTypedArraySuperClass; import static de.robv.android.xposed.XposedHelpers.decrementMethodDepth; import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; @@ -52,7 +50,7 @@ import static de.robv.android.xposed.XposedHelpers.incrementMethodDepth; * be set using the methods made available via the API methods in this class. */ @SuppressWarnings("JniMissingFunction") -public class XResources extends XResourcesSuperClass { +public class XResources extends Resources { private static final SparseArray> sReplacements = new SparseArray<>(); private static final SparseArray> sResourceNames = new SparseArray<>(); @@ -80,11 +78,19 @@ public class XResources extends XResourcesSuperClass { private String mResDir; private String mPackageName; - /** Dummy, will never be called (objects are transferred to this class only). */ - private XResources() { - throw new UnsupportedOperationException(); + public XResources(AssetManager assets, DisplayMetrics metrics, Configuration config) { + super(assets, metrics, config); } + public XResources(ClassLoader classLoader) { + super(classLoader); + } + + /** Dummy, will never be called (objects are transferred to this class only). */ +// private XResources() { +// throw new UnsupportedOperationException(); +// } + /** @hide */ public void initObject(String resDir) { if (mIsObjectInited) @@ -168,7 +174,7 @@ public class XResources extends XResourcesSuperClass { pkgInfo = PackageParser.parsePackageLite(resDir, 0); } if (pkgInfo != null && pkgInfo.packageName != null) { - Log.w(XposedBridge.TAG, "Package name for " + resDir + " had to be retrieved via parser"); +// Log.w(XposedBridge.TAG, "Package name for " + resDir + " had to be retrieved via parser"); packageName = pkgInfo.packageName; setPackageNameForResDir(packageName, resDir); return packageName; @@ -624,28 +630,28 @@ public class XResources extends XResourcesSuperClass { } } - /** @hide */ - @Override - public XmlResourceParser getAnimation(int id) throws NotFoundException { - Object replacement = getReplacement(id); - if (replacement instanceof XResForwarder) { - Resources repRes = ((XResForwarder) replacement).getResources(); - int repId = ((XResForwarder) replacement).getId(); - - boolean loadedFromCache = isXmlCached(repRes, repId); - XmlResourceParser result = repRes.getAnimation(repId); - - if (!loadedFromCache) { - long parseState = (Build.VERSION.SDK_INT >= 21) - ? getLongField(result, "mParseState") - : getIntField(result, "mParseState"); - rewriteXmlReferencesNative(parseState, this, repRes); - } - - return result; - } - return super.getAnimation(id); - } +// /** @hide */ +// @Override +// public XmlResourceParser getAnimation(int id) throws NotFoundException { +// Object replacement = getReplacement(id); +// if (replacement instanceof XResForwarder) { +// Resources repRes = ((XResForwarder) replacement).getResources(); +// int repId = ((XResForwarder) replacement).getId(); +// +// boolean loadedFromCache = isXmlCached(repRes, repId); +// XmlResourceParser result = repRes.getAnimation(repId); +// +// if (!loadedFromCache) { +// long parseState = (Build.VERSION.SDK_INT >= 21) +// ? getLongField(result, "mParseState") +// : getIntField(result, "mParseState"); +// rewriteXmlReferencesNative(parseState, this, repRes); +// } +// +// return result; +// } +// return super.getAnimation(id); +// } /** @hide */ @Override @@ -937,76 +943,76 @@ public class XResources extends XResourcesSuperClass { return super.getIntArray(id); } - /** @hide */ - @Override - public XmlResourceParser getLayout(int id) throws NotFoundException { - XmlResourceParser result; - Object replacement = getReplacement(id); - if (replacement instanceof XResForwarder) { - Resources repRes = ((XResForwarder) replacement).getResources(); - int repId = ((XResForwarder) replacement).getId(); - - boolean loadedFromCache = isXmlCached(repRes, repId); - result = repRes.getLayout(repId); - - if (!loadedFromCache) { - long parseState = (Build.VERSION.SDK_INT >= 21) - ? getLongField(result, "mParseState") - : getIntField(result, "mParseState"); - rewriteXmlReferencesNative(parseState, this, repRes); - } - } else { - result = super.getLayout(id); - } - - // Check whether this layout is hooked - HashMap> inner; - synchronized (sLayoutCallbacks) { - inner = sLayoutCallbacks.get(id); - } - if (inner != null) { - CopyOnWriteSortedSet callbacks; - synchronized (inner) { - callbacks = inner.get(mResDir); - if (callbacks == null && mResDir != null) - callbacks = inner.get(null); - } - if (callbacks != null) { - String variant = "layout"; - TypedValue value = (TypedValue) getObjectField(this, "mTmpValue"); - getValue(id, value, true); - if (value.type == TypedValue.TYPE_STRING) { - String[] components = value.string.toString().split("/", 3); - if (components.length == 3) - variant = components[1]; - else - XposedBridge.log("Unexpected resource path \"" + value.string.toString() - + "\" for resource id 0x" + Integer.toHexString(id)); - } else { - XposedBridge.log(new NotFoundException("Could not find file name for resource id 0x") + Integer.toHexString(id)); - } - - synchronized (sXmlInstanceDetails) { - synchronized (sResourceNames) { - HashMap resNamesInner = sResourceNames.get(id); - if (resNamesInner != null) { - synchronized (resNamesInner) { - XMLInstanceDetails details = new XMLInstanceDetails(resNamesInner.get(mResDir), variant, callbacks); - sXmlInstanceDetails.put(result, details); - - // if we were called inside LayoutInflater.parseInclude, store the details for it - MethodHookParam top = sIncludedLayouts.get().peek(); - if (top != null) - top.setObjectExtra(EXTRA_XML_INSTANCE_DETAILS, details); - } - } - } - } - } - } - - return result; - } +// /** @hide */ +// @Override +// public XmlResourceParser getLayout(int id) throws NotFoundException { +// XmlResourceParser result; +// Object replacement = getReplacement(id); +// if (replacement instanceof XResForwarder) { +// Resources repRes = ((XResForwarder) replacement).getResources(); +// int repId = ((XResForwarder) replacement).getId(); +// +// boolean loadedFromCache = isXmlCached(repRes, repId); +// result = repRes.getLayout(repId); +// +// if (!loadedFromCache) { +// long parseState = (Build.VERSION.SDK_INT >= 21) +// ? getLongField(result, "mParseState") +// : getIntField(result, "mParseState"); +// rewriteXmlReferencesNative(parseState, this, repRes); +// } +// } else { +// result = super.getLayout(id); +// } +// +// // Check whether this layout is hooked +// HashMap> inner; +// synchronized (sLayoutCallbacks) { +// inner = sLayoutCallbacks.get(id); +// } +// if (inner != null) { +// CopyOnWriteSortedSet callbacks; +// synchronized (inner) { +// callbacks = inner.get(mResDir); +// if (callbacks == null && mResDir != null) +// callbacks = inner.get(null); +// } +// if (callbacks != null) { +// String variant = "layout"; +// TypedValue value = (TypedValue) getObjectField(this, "mTmpValue"); +// getValue(id, value, true); +// if (value.type == TypedValue.TYPE_STRING) { +// String[] components = value.string.toString().split("/", 3); +// if (components.length == 3) +// variant = components[1]; +// else +// XposedBridge.log("Unexpected resource path \"" + value.string.toString() +// + "\" for resource id 0x" + Integer.toHexString(id)); +// } else { +// XposedBridge.log(new NotFoundException("Could not find file name for resource id 0x") + Integer.toHexString(id)); +// } +// +// synchronized (sXmlInstanceDetails) { +// synchronized (sResourceNames) { +// HashMap resNamesInner = sResourceNames.get(id); +// if (resNamesInner != null) { +// synchronized (resNamesInner) { +// XMLInstanceDetails details = new XMLInstanceDetails(resNamesInner.get(mResDir), variant, callbacks); +// sXmlInstanceDetails.put(result, details); +// +// // if we were called inside LayoutInflater.parseInclude, store the details for it +// MethodHookParam top = sIncludedLayouts.get().peek(); +// if (top != null) +// top.setObjectExtra(EXTRA_XML_INSTANCE_DETAILS, details); +// } +// } +// } +// } +// } +// } +// +// return result; +// } /** @hide */ @Override @@ -1094,28 +1100,28 @@ public class XResources extends XResourcesSuperClass { return super.getTextArray(id); } - /** @hide */ - @Override - public XmlResourceParser getXml(int id) throws NotFoundException { - Object replacement = getReplacement(id); - if (replacement instanceof XResForwarder) { - Resources repRes = ((XResForwarder) replacement).getResources(); - int repId = ((XResForwarder) replacement).getId(); - - boolean loadedFromCache = isXmlCached(repRes, repId); - XmlResourceParser result = repRes.getXml(repId); - - if (!loadedFromCache) { - long parseState = (Build.VERSION.SDK_INT >= 21) - ? getLongField(result, "mParseState") - : getIntField(result, "mParseState"); - rewriteXmlReferencesNative(parseState, this, repRes); - } - - return result; - } - return super.getXml(id); - } +// /** @hide */ +// @Override +// public XmlResourceParser getXml(int id) throws NotFoundException { +// Object replacement = getReplacement(id); +// if (replacement instanceof XResForwarder) { +// Resources repRes = ((XResForwarder) replacement).getResources(); +// int repId = ((XResForwarder) replacement).getId(); +// +// boolean loadedFromCache = isXmlCached(repRes, repId); +// XmlResourceParser result = repRes.getXml(repId); +// +// if (!loadedFromCache) { +// long parseState = (Build.VERSION.SDK_INT >= 21) +// ? getLongField(result, "mParseState") +// : getIntField(result, "mParseState"); +// rewriteXmlReferencesNative(parseState, this, repRes); +// } +// +// return result; +// } +// return super.getXml(id); +// } private static boolean isXmlCached(Resources res, int id) { int[] mCachedXmlBlockIds = (int[]) getObjectField(res, "mCachedXmlBlockIds"); @@ -1253,12 +1259,17 @@ public class XResources extends XResourcesSuperClass { * Mainly used when inflating layouts. * @hide */ - public static class XTypedArray extends XTypedArraySuperClass { - /** Dummy, will never be called (objects are transferred to this class only). */ - private XTypedArray() { - super(null, null, null, 0); - throw new UnsupportedOperationException(); - } + public static class XTypedArray extends TypedArray { + + public XTypedArray(Resources resources) { + super(resources); + } + + /** Dummy, will never be called (objects are transferred to this class only). */ +// private XTypedArray() { +// super(null, null, null, 0); +// throw new UnsupportedOperationException(); +// } @Override public boolean getBoolean(int index, boolean defValue) { diff --git a/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java b/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java index 92e69d02..d1711170 100644 --- a/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java +++ b/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java @@ -378,10 +378,9 @@ public final class XposedBridge { * @hide */ public static void hookInitPackageResources(XC_InitPackageResources callback) { - // TODO not supported yet -// synchronized (sInitPackageResourcesCallbacks) { -// sInitPackageResourcesCallbacks.add(callback); -// } + synchronized (sInitPackageResourcesCallbacks) { + sInitPackageResourcesCallbacks.add(callback); + } } public static void clearInitPackageResources() { diff --git a/xposed-bridge/src/main/java/de/robv/android/xposed/XposedInit.java b/xposed-bridge/src/main/java/de/robv/android/xposed/XposedInit.java index bba953fd..ca87553b 100644 --- a/xposed-bridge/src/main/java/de/robv/android/xposed/XposedInit.java +++ b/xposed-bridge/src/main/java/de/robv/android/xposed/XposedInit.java @@ -1,6 +1,16 @@ package de.robv.android.xposed; +import android.app.ActivityThread; +import android.app.AndroidAppHelper; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageParser; +import android.content.res.Resources; +import android.content.res.ResourcesImpl; +import android.content.res.TypedArray; +import android.content.res.XResources; import android.os.Build; +import android.os.IBinder; +import android.os.Process; import android.text.TextUtils; import android.util.Log; @@ -12,20 +22,34 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import dalvik.system.DexFile; import dalvik.system.PathClassLoader; +import de.robv.android.xposed.callbacks.XC_InitPackageResources; +import de.robv.android.xposed.callbacks.XCallback; import de.robv.android.xposed.services.BaseService; +import static de.robv.android.xposed.XposedBridge.hookAllConstructors; +import static de.robv.android.xposed.XposedBridge.hookAllMethods; +import static de.robv.android.xposed.XposedHelpers.callMethod; import static de.robv.android.xposed.XposedHelpers.closeSilently; +import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; import static de.robv.android.xposed.XposedHelpers.findClass; import static de.robv.android.xposed.XposedHelpers.findFieldIfExists; +import static de.robv.android.xposed.XposedHelpers.getObjectField; +import static de.robv.android.xposed.XposedHelpers.getParameterIndexByType; import static de.robv.android.xposed.XposedHelpers.setStaticBooleanField; import static de.robv.android.xposed.XposedHelpers.setStaticLongField; +import static de.robv.android.xposed.XposedHelpers.setStaticObjectField; public final class XposedInit { private static final String TAG = XposedBridge.TAG; @@ -34,7 +58,7 @@ public final class XposedInit { private static final String INSTANT_RUN_CLASS = "com.android.tools.fd.runtime.BootstrapApplication"; // TODO not supported yet - private static boolean disableResources = true; + private static boolean disableResources = false; private static final String[] XRESOURCES_CONFLICTING_PACKAGES = {"com.sygic.aura"}; private XposedInit() { @@ -58,11 +82,214 @@ public final class XposedInit { } catch (NoSuchFieldError ignored) { } } + findAndHookMethod("android.app.ApplicationPackageManager", null, "getResourcesForApplication", + ApplicationInfo.class, new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + ApplicationInfo app = (ApplicationInfo) param.args[0]; + XResources.setPackageNameForResDir(app.packageName, + app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir); + } + }); + hookResources(); } /*package*/ - static void hookResources() throws Throwable { - // ed: not for now + public static void hookResources() throws Throwable { + + String BASE_DIR = EdXpConfigGlobal.getConfig().getInstallerBaseDir(); + + if (SELinuxHelper.getAppDataFileService().checkFileExists(BASE_DIR + "conf/disable_resources")) { + Log.w(TAG, "Found " + BASE_DIR + "conf/disable_resources, not hooking resources"); + disableResources = true; + return; + } + + /* + * getTopLevelResources(a) + * -> getTopLevelResources(b) + * -> key = new ResourcesKey() + * -> r = new Resources() + * -> mActiveResources.put(key, r) + * -> return r + */ + + final Class classGTLR; + final Class classResKey; + final ThreadLocal latestResKey = new ThreadLocal<>(); + + if (Build.VERSION.SDK_INT <= 18) { + classGTLR = ActivityThread.class; + classResKey = Class.forName("android.app.ActivityThread$ResourcesKey"); + } else { + classGTLR = Class.forName("android.app.ResourcesManager"); + classResKey = Class.forName("android.content.res.ResourcesKey"); + } + + if (Build.VERSION.SDK_INT >= 24) { + hookAllMethods(classGTLR, "getOrCreateResources", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + // At least on OnePlus 5, the method has an additional parameter compared to AOSP. + final int activityTokenIdx = getParameterIndexByType(param.method, IBinder.class); + final int resKeyIdx = getParameterIndexByType(param.method, classResKey); + + String resDir = (String) getObjectField(param.args[resKeyIdx], "mResDir"); + XResources newRes = cloneToXResources(param, resDir); + if (newRes == null) { + return; + } + + Object activityToken = param.args[activityTokenIdx]; + synchronized (param.thisObject) { + ArrayList> resourceReferences; + if (activityToken != null) { + Object activityResources = callMethod(param.thisObject, "getOrCreateActivityResourcesStructLocked", activityToken); + resourceReferences = (ArrayList>) getObjectField(activityResources, "activityResources"); + } else { + resourceReferences = (ArrayList>) getObjectField(param.thisObject, "mResourceReferences"); + } + resourceReferences.add(new WeakReference(newRes)); + } + } + }); + } else { + hookAllConstructors(classResKey, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + latestResKey.set(param.thisObject); + } + }); + + hookAllMethods(classGTLR, "getTopLevelResources", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + latestResKey.set(null); + } + + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + Object key = latestResKey.get(); + if (key == null) { + return; + } + latestResKey.set(null); + + String resDir = (String) getObjectField(key, "mResDir"); + XResources newRes = cloneToXResources(param, resDir); + if (newRes == null) { + return; + } + + @SuppressWarnings("unchecked") + Map> mActiveResources = + (Map>) getObjectField(param.thisObject, "mActiveResources"); + Object lockObject = (Build.VERSION.SDK_INT <= 18) + ? getObjectField(param.thisObject, "mPackages") : param.thisObject; + + synchronized (lockObject) { + WeakReference existing = mActiveResources.put(key, new WeakReference(newRes)); + if (existing != null && existing.get() != null && existing.get().getAssets() != newRes.getAssets()) { + existing.get().getAssets().close(); + } + } + } + }); + + if (Build.VERSION.SDK_INT >= 19) { + // This method exists only on CM-based ROMs + hookAllMethods(classGTLR, "getTopLevelThemedResources", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + String resDir = (String) param.args[0]; + cloneToXResources(param, resDir); + } + }); + } + } + + // Invalidate callers of methods overridden by XTypedArray +// if (Build.VERSION.SDK_INT >= 24) { +// Set methods = getOverriddenMethods(XResources.XTypedArray.class); +// XposedBridge.invalidateCallersNative(methods.toArray(new Member[methods.size()])); +// } + + // Replace TypedArrays with XTypedArrays +// hookAllConstructors(TypedArray.class, new XC_MethodHook() { +// @Override +// protected void afterHookedMethod(MethodHookParam param) throws Throwable { +// TypedArray typedArray = (TypedArray) param.thisObject; +// Resources res = typedArray.getResources(); +// if (res instanceof XResources) { +// XResources.XTypedArray newTypedArray = new XResources.XTypedArray(res); +// XposedBridge.setObjectClass(typedArray, XResources.XTypedArray.class); +// } +// } +// }); + + findAndHookMethod(TypedArray.class, "obtain", Resources.class, int.class, + new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + if (param.getResult() instanceof XResources.XTypedArray) { + return; + } + if (!(param.args[0] instanceof XResources)) { + return; + } + XResources.XTypedArray newResult = + new XResources.XTypedArray((Resources) param.args[0]); + int len = (int) param.args[1]; + Method resizeMethod = XposedHelpers.findMethodBestMatch( + TypedArray.class, "resize", new Class[]{int.class}); + resizeMethod.setAccessible(true); + resizeMethod.invoke(newResult, len); + param.setResult(newResult); + } + }); + + // Replace system resources + XResources systemRes = new XResources( + (ClassLoader) XposedHelpers.getObjectField(Resources.getSystem(), "mClassLoader")); + systemRes.setImpl((ResourcesImpl) XposedHelpers.getObjectField(Resources.getSystem(), "mResourcesImpl")); + systemRes.initObject(null); + setStaticObjectField(Resources.class, "mSystem", systemRes); + + XResources.init(latestResKey); + + //custom + hookAllConstructors(PackageParser.PackageParserException.class, new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + XposedBridge.log(new Throwable()); + } + }); + } + + private static XResources cloneToXResources(XC_MethodHook.MethodHookParam param, String resDir) { + Object result = param.getResult(); + if (result == null || result instanceof XResources || + Arrays.binarySearch(XRESOURCES_CONFLICTING_PACKAGES, AndroidAppHelper.currentPackageName()) == 0) { + return null; + } + + // Replace the returned resources with our subclass. + XResources newRes = new XResources( + (ClassLoader) XposedHelpers.getObjectField(param.getResult(), "mClassLoader")); + newRes.setImpl((ResourcesImpl) XposedHelpers.getObjectField(param.getResult(), "mResourcesImpl")); + newRes.initObject(resDir); + + // Invoke handleInitPackageResources(). + if (newRes.isFirstLoad()) { + String packageName = newRes.getPackageName(); + XC_InitPackageResources.InitPackageResourcesParam resparam = new XC_InitPackageResources.InitPackageResourcesParam(XposedBridge.sInitPackageResourcesCallbacks); + resparam.packageName = packageName; + resparam.res = newRes; + XCallback.callAll(resparam); + } + + param.setResult(newRes); + return newRes; } private static boolean needsToCloseFilesForFork() { From bf9b2707754237764424f1ca5546be8651cc5453 Mon Sep 17 00:00:00 2001 From: solohsu Date: Mon, 22 Apr 2019 16:12:18 +0800 Subject: [PATCH 02/13] Add support for resources hooking. (2/2) --- .../riru/edxp/config/BaseHookProvider.java | 8 + edxp-core/jni/main/Android.mk | 1 + edxp-core/jni/main/include/ByteOrder.h | 43 +++ edxp-core/jni/main/java_hook/java_hook.cpp | 9 +- edxp-core/jni/main/java_hook/java_hook.h | 2 + .../jni/main/native_hook/resource_hook.cpp | 161 +++++++++++ .../jni/main/native_hook/resource_hook.h | 256 ++++++++++++++++++ .../java/com/elderdrivers/riru/edxp/Main.java | 9 +- .../edxp/yahfa/config/YahfaHookProvider.java | 9 + .../riru/edxp/yahfa/entry/Router.java | 2 + .../java/android/content/res/XResources.java | 239 ++++++++-------- .../riru/edxp/config/EdXpConfigGlobal.java | 8 + .../riru/edxp/hook/HookProvider.java | 5 + 13 files changed, 633 insertions(+), 119 deletions(-) create mode 100644 edxp-core/jni/main/include/ByteOrder.h create mode 100644 edxp-core/jni/main/native_hook/resource_hook.cpp create mode 100644 edxp-core/jni/main/native_hook/resource_hook.h diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java index 5f7fd05e..599fd613 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java @@ -1,5 +1,8 @@ package com.elderdrivers.riru.edxp.config; +import android.content.res.Resources; +import android.content.res.XResources; + import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter; import com.elderdrivers.riru.edxp.hook.HookProvider; @@ -32,4 +35,9 @@ public abstract class BaseHookProvider implements HookProvider { public void deoptMethodNative(Object method) { } + + @Override + public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { + + } } diff --git a/edxp-core/jni/main/Android.mk b/edxp-core/jni/main/Android.mk index 08325358..55f0035a 100644 --- a/edxp-core/jni/main/Android.mk +++ b/edxp-core/jni/main/Android.mk @@ -13,6 +13,7 @@ LOCAL_LDFLAGS := -Wl LOCAL_SRC_FILES:= \ main.cpp \ native_hook/native_hook.cpp \ + native_hook/resource_hook.cpp \ native_hook/riru_hook.cpp \ include/misc.cpp \ include/riru.c \ diff --git a/edxp-core/jni/main/include/ByteOrder.h b/edxp-core/jni/main/include/ByteOrder.h new file mode 100644 index 00000000..d44ef43e --- /dev/null +++ b/edxp-core/jni/main/include/ByteOrder.h @@ -0,0 +1,43 @@ +// +// Created by solo on 2019/3/24. +// + +#ifndef EDXPOSED_TEMP_BYTEORDER_H +#define EDXPOSED_TEMP_BYTEORDER_H + +#include + +static inline uint32_t android_swap_long(uint32_t v) +{ + return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24); +} +static inline uint16_t android_swap_short(uint16_t v) +{ + return (v<<8) | (v>>8); +} + +#define DEVICE_BYTE_ORDER LITTLE_ENDIAN +#if BYTE_ORDER == DEVICE_BYTE_ORDER +#define dtohl(x) (x) +#define dtohs(x) (x) +#define htodl(x) (x) +#define htods(x) (x) +#else +#define dtohl(x) (android_swap_long(x)) +#define dtohs(x) (android_swap_short(x)) +#define htodl(x) (android_swap_long(x)) +#define htods(x) (android_swap_short(x)) +#endif +#if BYTE_ORDER == LITTLE_ENDIAN +#define fromlel(x) (x) +#define fromles(x) (x) +#define tolel(x) (x) +#define toles(x) (x) +#else +#define fromlel(x) (android_swap_long(x)) +#define fromles(x) (android_swap_short(x)) +#define tolel(x) (android_swap_long(x)) +#define toles(x) (android_swap_short(x)) +#endif + +#endif //EDXPOSED_TEMP_BYTEORDER_H diff --git a/edxp-core/jni/main/java_hook/java_hook.cpp b/edxp-core/jni/main/java_hook/java_hook.cpp index 2e9194ec..19852660 100644 --- a/edxp-core/jni/main/java_hook/java_hook.cpp +++ b/edxp-core/jni/main/java_hook/java_hook.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "java_hook/java_hook.h" #include "include/logging.h" #include "include/fd_utils-inl.h" @@ -125,6 +126,12 @@ static JNINativeMethod hookMethods[] = { }, { "waitForGcToComplete", "(J)I", (void *) waitForGcToComplete + }, + { + "initXResourcesNative", "()Z", (void *) XposedBridge_initXResourcesNative + }, + { + "rewriteXmlReferencesNative", "(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V", (void *) XResources_rewriteXmlReferencesNative } }; @@ -162,7 +169,7 @@ void loadDexAndInit(JNIEnv *env, const char *dexPath) { jclass entry_class = findClassFromLoader(env, myClassLoader, ENTRY_CLASS_NAME); if (NULL != entry_class) { LOGD("HookEntry Class %p", entry_class); - env->RegisterNatives(entry_class, hookMethods, 15); + env->RegisterNatives(entry_class, hookMethods, NELEM(hookMethods)); isInited = true; LOGD("RegisterNatives succeed for HookEntry."); } else { diff --git a/edxp-core/jni/main/java_hook/java_hook.h b/edxp-core/jni/main/java_hook/java_hook.h index ac6e2939..9e678baf 100644 --- a/edxp-core/jni/main/java_hook/java_hook.h +++ b/edxp-core/jni/main/java_hook/java_hook.h @@ -4,6 +4,8 @@ #include #include +#define NELEM(x) (sizeof(x)/sizeof((x)[0])) + extern jobject gInjectDexClassLoader; void loadDexAndInit(JNIEnv *env, const char *dexPath); diff --git a/edxp-core/jni/main/native_hook/resource_hook.cpp b/edxp-core/jni/main/native_hook/resource_hook.cpp new file mode 100644 index 00000000..4f485bac --- /dev/null +++ b/edxp-core/jni/main/native_hook/resource_hook.cpp @@ -0,0 +1,161 @@ +// +// Created by solo on 2019/3/24. +// + +#include +#include +#include +#include +#include "resource_hook.h" + +#define CLASS_XRESOURCES "android/content/res/XResources" + +jclass classXResources; +jmethodID methodXResourcesTranslateAttrId; +jmethodID methodXResourcesTranslateResId; + +int32_t (*ResXMLParser_next)(void *); + +void (*ResXMLParser_restart)(void *); + +int32_t (*ResXMLParser_getAttributeNameID)(void *, int); + +char16_t *(*ResStringPool_stringAt)(void *, int32_t, size_t *); + +bool prepareSymbols() { + void *fwHandle = dlopen(kLibFwPath, RTLD_LAZY | RTLD_GLOBAL); + if (!fwHandle) { + LOGE("can't open libandroidfw: %s", dlerror()); + return false; + } + ResXMLParser_next = reinterpret_cast(dlsym(fwHandle, + "_ZN7android12ResXMLParser4nextEv")); + if (!ResXMLParser_next) { + LOGE("can't get ResXMLParser_next: %s", dlerror()); + return false; + } + ResXMLParser_restart = reinterpret_cast(dlsym(fwHandle, + "_ZN7android12ResXMLParser7restartEv")); + if (!ResXMLParser_restart) { + LOGE("can't get ResXMLParser_restart: %s", dlerror()); + return false; + } + ResXMLParser_getAttributeNameID = reinterpret_cast(dlsym(fwHandle, +#if defined(__LP64__) + "_ZNK7android12ResXMLParser18getAttributeNameIDEm" +#else + "_ZNK7android12ResXMLParser18getAttributeNameIDEj" +#endif + )); + if (!ResXMLParser_getAttributeNameID) { + LOGE("can't get ResXMLParser_getAttributeNameID: %s", dlerror()); + return false; + } + ResStringPool_stringAt = reinterpret_cast(dlsym( + fwHandle, +#if defined(__LP64__) + "_ZNK7android13ResStringPool8stringAtEmPm" +#else + "_ZNK7android13ResStringPool8stringAtEjPj" +#endif + )); + if (!ResStringPool_stringAt) { + LOGE("can't get ResStringPool_stringAt: %s", dlerror()); + return false; + } + return true; +} + +jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass) { + classXResources = env->FindClass(CLASS_XRESOURCES); + if (classXResources == NULL) { + LOGE("Error while loading XResources class '%s':", CLASS_XRESOURCES); + env->ExceptionClear(); + return false; + } + classXResources = reinterpret_cast(env->NewGlobalRef(classXResources)); + + methodXResourcesTranslateResId = env->GetStaticMethodID(classXResources, "translateResId", + "(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I"); + if (methodXResourcesTranslateResId == NULL) { + LOGE("ERROR: could not find method %s.translateResId(int, XResources, Resources)", + CLASS_XRESOURCES); + env->ExceptionClear(); + return false; + } + + methodXResourcesTranslateAttrId = env->GetStaticMethodID(classXResources, "translateAttrId", + "(Ljava/lang/String;Landroid/content/res/XResources;)I"); + if (methodXResourcesTranslateAttrId == NULL) { + LOGE("ERROR: could not find method %s.findAttrId(String, XResources)", CLASS_XRESOURCES); + env->ExceptionClear(); + return false; + } + + return prepareSymbols(); +} + +void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass, + jlong parserPtr, jobject origRes, jobject repRes) { + ResXMLParser* parser = (ResXMLParser*)parserPtr; + const ResXMLTree& mTree = parser->mTree; + uint32_t* mResIds = (uint32_t*)mTree.mResIds; + ResXMLTree_attrExt* tag; + int attrCount; + + if (parser == NULL) + return; + + do { + switch (ResXMLParser_next(parser)) { + case ResXMLParser::START_TAG: + tag = (ResXMLTree_attrExt*)parser->mCurExt; + attrCount = dtohs(tag->attributeCount); + for (int idx = 0; idx < attrCount; idx++) { + ResXMLTree_attribute* attr = (ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + + // find resource IDs for attribute names + int32_t attrNameID = ResXMLParser_getAttributeNameID(parser, idx); + // only replace attribute name IDs for app packages + if (attrNameID >= 0 && (size_t)attrNameID < mTree.mNumResIds && dtohl(mResIds[attrNameID]) >= 0x7f000000) { + size_t attNameLen; + const char16_t* attrName = ResStringPool_stringAt(mTree.mStrings, attrNameID, &attNameLen); + jint attrResID = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateAttrId, + env->NewString((const jchar*)attrName, attNameLen), origRes); + if (env->ExceptionCheck()) + goto leave; + + mResIds[attrNameID] = htodl(attrResID); + } + + // find original resource IDs for reference values (app packages only) + if (attr->typedValue.dataType != Res_value::TYPE_REFERENCE) + continue; + + jint oldValue = dtohl(attr->typedValue.data); + if (oldValue < 0x7f000000) + continue; + + jint newValue = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateResId, + oldValue, origRes, repRes); + if (env->ExceptionCheck()) + goto leave; + + if (newValue != oldValue) + attr->typedValue.data = htodl(newValue); + } + continue; + case ResXMLParser::END_DOCUMENT: + case ResXMLParser::BAD_DOCUMENT: + goto leave; + default: + continue; + } + } while (true); + + leave: + ResXMLParser_restart(parser); +} \ No newline at end of file diff --git a/edxp-core/jni/main/native_hook/resource_hook.h b/edxp-core/jni/main/native_hook/resource_hook.h new file mode 100644 index 00000000..52e91bbd --- /dev/null +++ b/edxp-core/jni/main/native_hook/resource_hook.h @@ -0,0 +1,256 @@ +// +// Created by solo on 2019/3/24. +// + +#ifndef EDXPOSED_TEMP_RESOURCE_HOOK_H +#define EDXPOSED_TEMP_RESOURCE_HOOK_H + +#include + +#if defined(__LP64__) +static constexpr const char *kLibFwPath = "/system/lib64/libandroidfw.so"; +#else +static constexpr const char *kLibFwPath = "/system/lib/libandroidfw.so"; +#endif + +jboolean XposedBridge_initXResourcesNative(JNIEnv* env, jclass); +void XResources_rewriteXmlReferencesNative(JNIEnv* env, jclass, + jlong parserPtr, jobject origRes, jobject repRes); + +enum { + RES_NULL_TYPE = 0x0000, + RES_STRING_POOL_TYPE = 0x0001, + RES_TABLE_TYPE = 0x0002, + RES_XML_TYPE = 0x0003, + // Chunk types in RES_XML_TYPE + RES_XML_FIRST_CHUNK_TYPE = 0x0100, + RES_XML_START_NAMESPACE_TYPE= 0x0100, + RES_XML_END_NAMESPACE_TYPE = 0x0101, + RES_XML_START_ELEMENT_TYPE = 0x0102, + RES_XML_END_ELEMENT_TYPE = 0x0103, + RES_XML_CDATA_TYPE = 0x0104, + RES_XML_LAST_CHUNK_TYPE = 0x017f, + // This contains a uint32_t array mapping strings in the string + // pool back to resource identifiers. It is optional. + RES_XML_RESOURCE_MAP_TYPE = 0x0180, + // Chunk types in RES_TABLE_TYPE + RES_TABLE_PACKAGE_TYPE = 0x0200, + RES_TABLE_TYPE_TYPE = 0x0201, + RES_TABLE_TYPE_SPEC_TYPE = 0x0202, + RES_TABLE_LIBRARY_TYPE = 0x0203 +}; + +struct ResXMLTree_node +{ + void *header; + // Line number in original source file at which this element appeared. + uint32_t lineNumber; + // Optional XML comment that was associated with this element; -1 if none. + void *comment; +}; + +class ResXMLTree; + +class ResXMLParser +{ +public: + enum event_code_t { + BAD_DOCUMENT = -1, + START_DOCUMENT = 0, + END_DOCUMENT = 1, + + FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE, + + START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE, + END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE, + START_TAG = RES_XML_START_ELEMENT_TYPE, + END_TAG = RES_XML_END_ELEMENT_TYPE, + TEXT = RES_XML_CDATA_TYPE + }; + +public: + friend class ResXMLTree; + + event_code_t nextNode(); + const ResXMLTree& mTree; + event_code_t mEventCode; + const ResXMLTree_node* mCurNode; + const void* mCurExt; +}; + + +class ResXMLTree : public ResXMLParser +{ +public: + friend class ResXMLParser; + int32_t validateNode(const ResXMLTree_node* node) const; + void* mDynamicRefTable; + int32_t mError; + void* mOwnedData; + const void* mHeader; + size_t mSize; + const uint8_t* mDataEnd; + void* mStrings; + const uint32_t* mResIds; + size_t mNumResIds; + const ResXMLTree_node* mRootNode; + const void* mRootExt; + event_code_t mRootCode; +}; + +struct ResXMLTree_attrExt +{ + // String of the full namespace of this element. + void* ns; + + // String name of this node if it is an ELEMENT; the raw + // character data if this is a CDATA node. + void* name; + + // Byte offset from the start of this structure where the attributes start. + uint16_t attributeStart; + + // Size of the ResXMLTree_attribute structures that follow. + uint16_t attributeSize; + + // Number of attributes associated with an ELEMENT. These are + // available as an array of ResXMLTree_attribute structures + // immediately following this node. + uint16_t attributeCount; + + // Index (1-based) of the "id" attribute. 0 if none. + uint16_t idIndex; + + // Index (1-based) of the "class" attribute. 0 if none. + uint16_t classIndex; + + // Index (1-based) of the "style" attribute. 0 if none. + uint16_t styleIndex; +}; + +struct Res_value +{ + // Number of bytes in this structure. + uint16_t size; + // Always set to 0. + uint8_t res0; + + // Type of the data value. + enum : uint8_t { + // The 'data' is either 0 or 1, specifying this resource is either + // undefined or empty, respectively. + TYPE_NULL = 0x00, + // The 'data' holds a ResTable_ref, a reference to another resource + // table entry. + TYPE_REFERENCE = 0x01, + // The 'data' holds an attribute resource identifier. + TYPE_ATTRIBUTE = 0x02, + // The 'data' holds an index into the containing resource table's + // global value string pool. + TYPE_STRING = 0x03, + // The 'data' holds a single-precision floating point number. + TYPE_FLOAT = 0x04, + // The 'data' holds a complex number encoding a dimension value, + // such as "100in". + TYPE_DIMENSION = 0x05, + // The 'data' holds a complex number encoding a fraction of a + // container. + TYPE_FRACTION = 0x06, + // The 'data' holds a dynamic ResTable_ref, which needs to be + // resolved before it can be used like a TYPE_REFERENCE. + TYPE_DYNAMIC_REFERENCE = 0x07, + // The 'data' holds an attribute resource identifier, which needs to be resolved + // before it can be used like a TYPE_ATTRIBUTE. + TYPE_DYNAMIC_ATTRIBUTE = 0x08, + // Beginning of integer flavors... + TYPE_FIRST_INT = 0x10, + // The 'data' is a raw integer value of the form n..n. + TYPE_INT_DEC = 0x10, + // The 'data' is a raw integer value of the form 0xn..n. + TYPE_INT_HEX = 0x11, + // The 'data' is either 0 or 1, for input "false" or "true" respectively. + TYPE_INT_BOOLEAN = 0x12, + // Beginning of color integer flavors... + TYPE_FIRST_COLOR_INT = 0x1c, + // The 'data' is a raw integer value of the form #aarrggbb. + TYPE_INT_COLOR_ARGB8 = 0x1c, + // The 'data' is a raw integer value of the form #rrggbb. + TYPE_INT_COLOR_RGB8 = 0x1d, + // The 'data' is a raw integer value of the form #argb. + TYPE_INT_COLOR_ARGB4 = 0x1e, + // The 'data' is a raw integer value of the form #rgb. + TYPE_INT_COLOR_RGB4 = 0x1f, + // ...end of integer flavors. + TYPE_LAST_COLOR_INT = 0x1f, + // ...end of integer flavors. + TYPE_LAST_INT = 0x1f + }; + uint8_t dataType; + // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION) + enum { + // Where the unit type information is. This gives us 16 possible + // types, as defined below. + COMPLEX_UNIT_SHIFT = 0, + COMPLEX_UNIT_MASK = 0xf, + // TYPE_DIMENSION: Value is raw pixels. + COMPLEX_UNIT_PX = 0, + // TYPE_DIMENSION: Value is Device Independent Pixels. + COMPLEX_UNIT_DIP = 1, + // TYPE_DIMENSION: Value is a Scaled device independent Pixels. + COMPLEX_UNIT_SP = 2, + // TYPE_DIMENSION: Value is in points. + COMPLEX_UNIT_PT = 3, + // TYPE_DIMENSION: Value is in inches. + COMPLEX_UNIT_IN = 4, + // TYPE_DIMENSION: Value is in millimeters. + COMPLEX_UNIT_MM = 5, + // TYPE_FRACTION: A basic fraction of the overall size. + COMPLEX_UNIT_FRACTION = 0, + // TYPE_FRACTION: A fraction of the parent size. + COMPLEX_UNIT_FRACTION_PARENT = 1, + // Where the radix information is, telling where the decimal place + // appears in the mantissa. This give us 4 possible fixed point + // representations as defined below. + COMPLEX_RADIX_SHIFT = 4, + COMPLEX_RADIX_MASK = 0x3, + // The mantissa is an integral number -- i.e., 0xnnnnnn.0 + COMPLEX_RADIX_23p0 = 0, + // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn + COMPLEX_RADIX_16p7 = 1, + // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn + COMPLEX_RADIX_8p15 = 2, + // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn + COMPLEX_RADIX_0p23 = 3, + // Where the actual value is. This gives us 23 bits of + // precision. The top bit is the sign. + COMPLEX_MANTISSA_SHIFT = 8, + COMPLEX_MANTISSA_MASK = 0xffffff + }; + // Possible data values for TYPE_NULL. + enum { + // The value is not defined. + DATA_NULL_UNDEFINED = 0, + // The value is explicitly defined as empty. + DATA_NULL_EMPTY = 1 + }; + // The data for this item, as interpreted according to dataType. + typedef uint32_t data_type; + data_type data; + void copyFrom_dtoh(const Res_value& src); +}; + +struct ResXMLTree_attribute +{ + // Namespace of this attribute. + void* ns; + + // Name of this attribute. + void* name; + // The original raw string value of this attribute. + void* rawValue; + + // Processesd typed value of this attribute. + struct Res_value typedValue; +}; + +#endif //EDXPOSED_TEMP_RESOURCE_HOOK_H diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java index d6bb5997..a781e699 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java @@ -1,17 +1,18 @@ package com.elderdrivers.riru.edxp; import android.annotation.SuppressLint; +import android.content.res.Resources; +import android.content.res.XResources; import android.os.Build; import android.os.Process; import com.elderdrivers.riru.common.KeepAll; -import com.elderdrivers.riru.edxp.BuildConfig; import com.elderdrivers.riru.edxp.config.InstallerChooser; +import com.elderdrivers.riru.edxp.util.Utils; import com.elderdrivers.riru.edxp.yahfa.core.HookMethodResolver; import com.elderdrivers.riru.edxp.yahfa.entry.Router; import com.elderdrivers.riru.edxp.yahfa.proxy.BlackWhiteListProxy; import com.elderdrivers.riru.edxp.yahfa.proxy.NormalProxy; -import com.elderdrivers.riru.edxp.util.Utils; import java.lang.reflect.Method; import java.util.Arrays; @@ -139,4 +140,8 @@ public class Main implements KeepAll { public static native void resumeAllThreads(long obj); public static native int waitForGcToComplete(long thread); + + public static native boolean initXResourcesNative(); + + public static native void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes); } diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java index 39e8a516..f000ae59 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java @@ -1,5 +1,9 @@ package com.elderdrivers.riru.edxp.yahfa.config; +import android.content.res.Resources; +import android.content.res.XResources; + +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.BaseHookProvider; import com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils; import com.elderdrivers.riru.edxp.yahfa.dexmaker.DynamicBridge; @@ -24,4 +28,9 @@ public class YahfaHookProvider extends BaseHookProvider { public Member findMethodNative(Member hookMethod) { return DexMakerUtils.findMethodNative(hookMethod); } + + @Override + public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { + Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes); + } } diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/Router.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/Router.java index 980ca8f5..5eae8a75 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/Router.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/Router.java @@ -3,6 +3,7 @@ package com.elderdrivers.riru.edxp.yahfa.entry; import android.app.AndroidAppHelper; import android.text.TextUtils; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal; import com.elderdrivers.riru.edxp.util.Utils; import com.elderdrivers.riru.edxp.yahfa.config.YahfaEdxpConfig; @@ -57,6 +58,7 @@ public class Router { } Router.startBootstrapHook(isSystem); XposedInit.initForZygote(isSystem); + Main.initXResourcesNative(); } catch (Throwable t) { Utils.logE("error during Xposed initialization", t); XposedBridge.disableHooks = true; diff --git a/xposed-bridge/src/main/java/android/content/res/XResources.java b/xposed-bridge/src/main/java/android/content/res/XResources.java index c8a4fad9..7d8de8e3 100644 --- a/xposed-bridge/src/main/java/android/content/res/XResources.java +++ b/xposed-bridge/src/main/java/android/content/res/XResources.java @@ -18,6 +18,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal; + import org.xmlpull.v1.XmlPullParser; import java.io.File; @@ -630,28 +632,28 @@ public class XResources extends Resources { } } -// /** @hide */ -// @Override -// public XmlResourceParser getAnimation(int id) throws NotFoundException { -// Object replacement = getReplacement(id); -// if (replacement instanceof XResForwarder) { -// Resources repRes = ((XResForwarder) replacement).getResources(); -// int repId = ((XResForwarder) replacement).getId(); -// -// boolean loadedFromCache = isXmlCached(repRes, repId); -// XmlResourceParser result = repRes.getAnimation(repId); -// -// if (!loadedFromCache) { -// long parseState = (Build.VERSION.SDK_INT >= 21) -// ? getLongField(result, "mParseState") -// : getIntField(result, "mParseState"); -// rewriteXmlReferencesNative(parseState, this, repRes); -// } -// -// return result; -// } -// return super.getAnimation(id); -// } + /** @hide */ + @Override + public XmlResourceParser getAnimation(int id) throws NotFoundException { + Object replacement = getReplacement(id); + if (replacement instanceof XResForwarder) { + Resources repRes = ((XResForwarder) replacement).getResources(); + int repId = ((XResForwarder) replacement).getId(); + + boolean loadedFromCache = isXmlCached(repRes, repId); + XmlResourceParser result = repRes.getAnimation(repId); + + if (!loadedFromCache) { + long parseState = (Build.VERSION.SDK_INT >= 21) + ? getLongField(result, "mParseState") + : getIntField(result, "mParseState"); + rewriteXmlReferencesNative(parseState, this, repRes); + } + + return result; + } + return super.getAnimation(id); + } /** @hide */ @Override @@ -943,76 +945,76 @@ public class XResources extends Resources { return super.getIntArray(id); } -// /** @hide */ -// @Override -// public XmlResourceParser getLayout(int id) throws NotFoundException { -// XmlResourceParser result; -// Object replacement = getReplacement(id); -// if (replacement instanceof XResForwarder) { -// Resources repRes = ((XResForwarder) replacement).getResources(); -// int repId = ((XResForwarder) replacement).getId(); -// -// boolean loadedFromCache = isXmlCached(repRes, repId); -// result = repRes.getLayout(repId); -// -// if (!loadedFromCache) { -// long parseState = (Build.VERSION.SDK_INT >= 21) -// ? getLongField(result, "mParseState") -// : getIntField(result, "mParseState"); -// rewriteXmlReferencesNative(parseState, this, repRes); -// } -// } else { -// result = super.getLayout(id); -// } -// -// // Check whether this layout is hooked -// HashMap> inner; -// synchronized (sLayoutCallbacks) { -// inner = sLayoutCallbacks.get(id); -// } -// if (inner != null) { -// CopyOnWriteSortedSet callbacks; -// synchronized (inner) { -// callbacks = inner.get(mResDir); -// if (callbacks == null && mResDir != null) -// callbacks = inner.get(null); -// } -// if (callbacks != null) { -// String variant = "layout"; -// TypedValue value = (TypedValue) getObjectField(this, "mTmpValue"); -// getValue(id, value, true); -// if (value.type == TypedValue.TYPE_STRING) { -// String[] components = value.string.toString().split("/", 3); -// if (components.length == 3) -// variant = components[1]; -// else -// XposedBridge.log("Unexpected resource path \"" + value.string.toString() -// + "\" for resource id 0x" + Integer.toHexString(id)); -// } else { -// XposedBridge.log(new NotFoundException("Could not find file name for resource id 0x") + Integer.toHexString(id)); -// } -// -// synchronized (sXmlInstanceDetails) { -// synchronized (sResourceNames) { -// HashMap resNamesInner = sResourceNames.get(id); -// if (resNamesInner != null) { -// synchronized (resNamesInner) { -// XMLInstanceDetails details = new XMLInstanceDetails(resNamesInner.get(mResDir), variant, callbacks); -// sXmlInstanceDetails.put(result, details); -// -// // if we were called inside LayoutInflater.parseInclude, store the details for it -// MethodHookParam top = sIncludedLayouts.get().peek(); -// if (top != null) -// top.setObjectExtra(EXTRA_XML_INSTANCE_DETAILS, details); -// } -// } -// } -// } -// } -// } -// -// return result; -// } + /** @hide */ + @Override + public XmlResourceParser getLayout(int id) throws NotFoundException { + XmlResourceParser result; + Object replacement = getReplacement(id); + if (replacement instanceof XResForwarder) { + Resources repRes = ((XResForwarder) replacement).getResources(); + int repId = ((XResForwarder) replacement).getId(); + + boolean loadedFromCache = isXmlCached(repRes, repId); + result = repRes.getLayout(repId); + + if (!loadedFromCache) { + long parseState = (Build.VERSION.SDK_INT >= 21) + ? getLongField(result, "mParseState") + : getIntField(result, "mParseState"); + rewriteXmlReferencesNative(parseState, this, repRes); + } + } else { + result = super.getLayout(id); + } + + // Check whether this layout is hooked + HashMap> inner; + synchronized (sLayoutCallbacks) { + inner = sLayoutCallbacks.get(id); + } + if (inner != null) { + CopyOnWriteSortedSet callbacks; + synchronized (inner) { + callbacks = inner.get(mResDir); + if (callbacks == null && mResDir != null) + callbacks = inner.get(null); + } + if (callbacks != null) { + String variant = "layout"; + TypedValue value = (TypedValue) getObjectField(this, "mTmpValue"); + getValue(id, value, true); + if (value.type == TypedValue.TYPE_STRING) { + String[] components = value.string.toString().split("/", 3); + if (components.length == 3) + variant = components[1]; + else + XposedBridge.log("Unexpected resource path \"" + value.string.toString() + + "\" for resource id 0x" + Integer.toHexString(id)); + } else { + XposedBridge.log(new NotFoundException("Could not find file name for resource id 0x") + Integer.toHexString(id)); + } + + synchronized (sXmlInstanceDetails) { + synchronized (sResourceNames) { + HashMap resNamesInner = sResourceNames.get(id); + if (resNamesInner != null) { + synchronized (resNamesInner) { + XMLInstanceDetails details = new XMLInstanceDetails(resNamesInner.get(mResDir), variant, callbacks); + sXmlInstanceDetails.put(result, details); + + // if we were called inside LayoutInflater.parseInclude, store the details for it + MethodHookParam top = sIncludedLayouts.get().peek(); + if (top != null) + top.setObjectExtra(EXTRA_XML_INSTANCE_DETAILS, details); + } + } + } + } + } + } + + return result; + } /** @hide */ @Override @@ -1100,31 +1102,34 @@ public class XResources extends Resources { return super.getTextArray(id); } -// /** @hide */ -// @Override -// public XmlResourceParser getXml(int id) throws NotFoundException { -// Object replacement = getReplacement(id); -// if (replacement instanceof XResForwarder) { -// Resources repRes = ((XResForwarder) replacement).getResources(); -// int repId = ((XResForwarder) replacement).getId(); -// -// boolean loadedFromCache = isXmlCached(repRes, repId); -// XmlResourceParser result = repRes.getXml(repId); -// -// if (!loadedFromCache) { -// long parseState = (Build.VERSION.SDK_INT >= 21) -// ? getLongField(result, "mParseState") -// : getIntField(result, "mParseState"); -// rewriteXmlReferencesNative(parseState, this, repRes); -// } -// -// return result; -// } -// return super.getXml(id); -// } + /** @hide */ + @Override + public XmlResourceParser getXml(int id) throws NotFoundException { + Object replacement = getReplacement(id); + if (id == 2131034112) { + XposedBridge.log("2131034112: " + replacement); + } + if (replacement instanceof XResForwarder) { + Resources repRes = ((XResForwarder) replacement).getResources(); + int repId = ((XResForwarder) replacement).getId(); + + boolean loadedFromCache = isXmlCached(repRes, repId); + XmlResourceParser result = repRes.getXml(repId); + + if (!loadedFromCache) { + long parseState = (Build.VERSION.SDK_INT >= 21) + ? getLongField(result, "mParseState") + : getIntField(result, "mParseState"); + rewriteXmlReferencesNative(parseState, this, repRes); + } + + return result; + } + return super.getXml(id); + } private static boolean isXmlCached(Resources res, int id) { - int[] mCachedXmlBlockIds = (int[]) getObjectField(res, "mCachedXmlBlockIds"); + int[] mCachedXmlBlockIds = (int[]) getObjectField(getObjectField(res, "mResourcesImpl"), "mCachedXmlBlockCookies"); synchronized (mCachedXmlBlockIds) { for (int cachedId : mCachedXmlBlockIds) { if (cachedId == id) @@ -1134,7 +1139,9 @@ public class XResources extends Resources { return false; } - private static native void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes); + private static void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { + EdXpConfigGlobal.getHookProvider().rewriteXmlReferencesNative(parserPtr, origRes, repRes); + } /** * Used to replace reference IDs in XMLs. diff --git a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java index 95c404a0..322f414a 100644 --- a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java +++ b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java @@ -1,5 +1,8 @@ package com.elderdrivers.riru.edxp.config; +import android.content.res.Resources; +import android.content.res.XResources; + import com.elderdrivers.riru.edxp.hook.HookProvider; import java.lang.reflect.InvocationTargetException; @@ -88,5 +91,10 @@ public class EdXpConfigGlobal { public void deoptMethodNative(Object method) { } + + @Override + public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { + + } }; } diff --git a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java index 119594db..d1759415 100644 --- a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java +++ b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java @@ -1,5 +1,8 @@ package com.elderdrivers.riru.edxp.hook; +import android.content.res.Resources; +import android.content.res.XResources; + import java.lang.reflect.Member; import de.robv.android.xposed.XposedBridge; @@ -21,4 +24,6 @@ public interface HookProvider { Object findMethodNative(Class clazz, String methodName, String methodSig); void deoptMethodNative(Object method); + + void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes); } From 8a2cdbe47e0facf768deb07252d714a07fbe0916 Mon Sep 17 00:00:00 2001 From: solohsu Date: Mon, 22 Apr 2019 18:54:59 +0800 Subject: [PATCH 03/13] Add class ResStringPool --- .../jni/main/native_hook/resource_hook.cpp | 6 ++--- .../jni/main/native_hook/resource_hook.h | 25 ++++++++++++++++--- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/edxp-core/jni/main/native_hook/resource_hook.cpp b/edxp-core/jni/main/native_hook/resource_hook.cpp index 4f485bac..f627b216 100644 --- a/edxp-core/jni/main/native_hook/resource_hook.cpp +++ b/edxp-core/jni/main/native_hook/resource_hook.cpp @@ -20,7 +20,7 @@ void (*ResXMLParser_restart)(void *); int32_t (*ResXMLParser_getAttributeNameID)(void *, int); -char16_t *(*ResStringPool_stringAt)(void *, int32_t, size_t *); +char16_t *(*ResStringPool_stringAt)(const void *, int32_t, size_t *); bool prepareSymbols() { void *fwHandle = dlopen(kLibFwPath, RTLD_LAZY | RTLD_GLOBAL); @@ -51,7 +51,7 @@ bool prepareSymbols() { LOGE("can't get ResXMLParser_getAttributeNameID: %s", dlerror()); return false; } - ResStringPool_stringAt = reinterpret_cast(dlsym( + ResStringPool_stringAt = reinterpret_cast(dlsym( fwHandle, #if defined(__LP64__) "_ZNK7android13ResStringPool8stringAtEmPm" @@ -122,7 +122,7 @@ void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass, // only replace attribute name IDs for app packages if (attrNameID >= 0 && (size_t)attrNameID < mTree.mNumResIds && dtohl(mResIds[attrNameID]) >= 0x7f000000) { size_t attNameLen; - const char16_t* attrName = ResStringPool_stringAt(mTree.mStrings, attrNameID, &attNameLen); + const char16_t* attrName = ResStringPool_stringAt(&(mTree.mStrings), attrNameID, &attNameLen); jint attrResID = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateAttrId, env->NewString((const jchar*)attrName, attNameLen), origRes); if (env->ExceptionCheck()) diff --git a/edxp-core/jni/main/native_hook/resource_hook.h b/edxp-core/jni/main/native_hook/resource_hook.h index 52e91bbd..ef532a18 100644 --- a/edxp-core/jni/main/native_hook/resource_hook.h +++ b/edxp-core/jni/main/native_hook/resource_hook.h @@ -23,7 +23,7 @@ enum { RES_TABLE_TYPE = 0x0002, RES_XML_TYPE = 0x0003, // Chunk types in RES_XML_TYPE - RES_XML_FIRST_CHUNK_TYPE = 0x0100, + RES_XML_FIRST_CHUNK_TYPE = 0x0100, RES_XML_START_NAMESPACE_TYPE= 0x0100, RES_XML_END_NAMESPACE_TYPE = 0x0101, RES_XML_START_ELEMENT_TYPE = 0x0102, @@ -32,9 +32,9 @@ enum { RES_XML_LAST_CHUNK_TYPE = 0x017f, // This contains a uint32_t array mapping strings in the string // pool back to resource identifiers. It is optional. - RES_XML_RESOURCE_MAP_TYPE = 0x0180, + RES_XML_RESOURCE_MAP_TYPE = 0x0180, // Chunk types in RES_TABLE_TYPE - RES_TABLE_PACKAGE_TYPE = 0x0200, + RES_TABLE_PACKAGE_TYPE = 0x0200, RES_TABLE_TYPE_TYPE = 0x0201, RES_TABLE_TYPE_SPEC_TYPE = 0x0202, RES_TABLE_LIBRARY_TYPE = 0x0203 @@ -78,6 +78,23 @@ public: const void* mCurExt; }; +class ResStringPool +{ +public: + int32_t mError; + void* mOwnedData; + const void* mHeader; + size_t mSize; + mutable pthread_mutex_t mDecodeLock; + const uint32_t* mEntries; + const uint32_t* mEntryStyles; + const void* mStrings; + char16_t mutable** mCache; + uint32_t mStringPoolSize; // number of uint16_t + const uint32_t* mStyles; + uint32_t mStylePoolSize; // number of uint32_t +}; + class ResXMLTree : public ResXMLParser { @@ -90,7 +107,7 @@ public: const void* mHeader; size_t mSize; const uint8_t* mDataEnd; - void* mStrings; + ResStringPool mStrings; const uint32_t* mResIds; size_t mNumResIds; const ResXMLTree_node* mRootNode; From 6e6327bec8de05bdce18123d2e8f70646c9add59 Mon Sep 17 00:00:00 2001 From: solohsu Date: Mon, 22 Apr 2019 22:44:52 +0800 Subject: [PATCH 04/13] Correct resources types' offsets --- .../jni/main/native_hook/resource_hook.cpp | 38 +++-- .../jni/main/native_hook/resource_hook.h | 147 +++++++++--------- 2 files changed, 98 insertions(+), 87 deletions(-) diff --git a/edxp-core/jni/main/native_hook/resource_hook.cpp b/edxp-core/jni/main/native_hook/resource_hook.cpp index f627b216..09282f92 100644 --- a/edxp-core/jni/main/native_hook/resource_hook.cpp +++ b/edxp-core/jni/main/native_hook/resource_hook.cpp @@ -97,34 +97,41 @@ jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass) { void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass, jlong parserPtr, jobject origRes, jobject repRes) { - ResXMLParser* parser = (ResXMLParser*)parserPtr; - const ResXMLTree& mTree = parser->mTree; - uint32_t* mResIds = (uint32_t*)mTree.mResIds; - ResXMLTree_attrExt* tag; - int attrCount; - if (parser == NULL) + ResXMLParser *parser = (ResXMLParser *) parserPtr; + + if (parser == nullptr) return; + const ResXMLTree &mTree = parser->mTree; + uint32_t *mResIds = (uint32_t *) mTree.mResIds; + ResXMLTree_attrExt *tag; + int attrCount; + do { switch (ResXMLParser_next(parser)) { case ResXMLParser::START_TAG: - tag = (ResXMLTree_attrExt*)parser->mCurExt; + tag = (ResXMLTree_attrExt *) parser->mCurExt; attrCount = dtohs(tag->attributeCount); for (int idx = 0; idx < attrCount; idx++) { - ResXMLTree_attribute* attr = (ResXMLTree_attribute*) - (((const uint8_t*)tag) + ResXMLTree_attribute *attr = (ResXMLTree_attribute *) + (((const uint8_t *) tag) + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); + + (dtohs(tag->attributeSize) * idx)); // find resource IDs for attribute names int32_t attrNameID = ResXMLParser_getAttributeNameID(parser, idx); // only replace attribute name IDs for app packages - if (attrNameID >= 0 && (size_t)attrNameID < mTree.mNumResIds && dtohl(mResIds[attrNameID]) >= 0x7f000000) { + if (attrNameID >= 0 && (size_t) attrNameID < mTree.mNumResIds && + dtohl(mResIds[attrNameID]) >= 0x7f000000) { size_t attNameLen; - const char16_t* attrName = ResStringPool_stringAt(&(mTree.mStrings), attrNameID, &attNameLen); - jint attrResID = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateAttrId, - env->NewString((const jchar*)attrName, attNameLen), origRes); + const char16_t *attrName = ResStringPool_stringAt(&(mTree.mStrings), + attrNameID, &attNameLen); + jint attrResID = env->CallStaticIntMethod(classXResources, + methodXResourcesTranslateAttrId, + env->NewString( + (const jchar *) attrName, + attNameLen), origRes); if (env->ExceptionCheck()) goto leave; @@ -139,7 +146,8 @@ void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass, if (oldValue < 0x7f000000) continue; - jint newValue = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateResId, + jint newValue = env->CallStaticIntMethod(classXResources, + methodXResourcesTranslateResId, oldValue, origRes, repRes); if (env->ExceptionCheck()) goto leave; diff --git a/edxp-core/jni/main/native_hook/resource_hook.h b/edxp-core/jni/main/native_hook/resource_hook.h index ef532a18..8208f302 100644 --- a/edxp-core/jni/main/native_hook/resource_hook.h +++ b/edxp-core/jni/main/native_hook/resource_hook.h @@ -13,35 +13,37 @@ static constexpr const char *kLibFwPath = "/system/lib64/libandroidfw.so"; static constexpr const char *kLibFwPath = "/system/lib/libandroidfw.so"; #endif -jboolean XposedBridge_initXResourcesNative(JNIEnv* env, jclass); -void XResources_rewriteXmlReferencesNative(JNIEnv* env, jclass, +jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass); + +void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass, jlong parserPtr, jobject origRes, jobject repRes); +typedef int32_t status_t; + enum { - RES_NULL_TYPE = 0x0000, - RES_STRING_POOL_TYPE = 0x0001, - RES_TABLE_TYPE = 0x0002, - RES_XML_TYPE = 0x0003, + RES_NULL_TYPE = 0x0000, + RES_STRING_POOL_TYPE = 0x0001, + RES_TABLE_TYPE = 0x0002, + RES_XML_TYPE = 0x0003, // Chunk types in RES_XML_TYPE - RES_XML_FIRST_CHUNK_TYPE = 0x0100, - RES_XML_START_NAMESPACE_TYPE= 0x0100, - RES_XML_END_NAMESPACE_TYPE = 0x0101, - RES_XML_START_ELEMENT_TYPE = 0x0102, - RES_XML_END_ELEMENT_TYPE = 0x0103, - RES_XML_CDATA_TYPE = 0x0104, - RES_XML_LAST_CHUNK_TYPE = 0x017f, + RES_XML_FIRST_CHUNK_TYPE = 0x0100, + RES_XML_START_NAMESPACE_TYPE = 0x0100, + RES_XML_END_NAMESPACE_TYPE = 0x0101, + RES_XML_START_ELEMENT_TYPE = 0x0102, + RES_XML_END_ELEMENT_TYPE = 0x0103, + RES_XML_CDATA_TYPE = 0x0104, + RES_XML_LAST_CHUNK_TYPE = 0x017f, // This contains a uint32_t array mapping strings in the string // pool back to resource identifiers. It is optional. - RES_XML_RESOURCE_MAP_TYPE = 0x0180, + RES_XML_RESOURCE_MAP_TYPE = 0x0180, // Chunk types in RES_TABLE_TYPE - RES_TABLE_PACKAGE_TYPE = 0x0200, - RES_TABLE_TYPE_TYPE = 0x0201, - RES_TABLE_TYPE_SPEC_TYPE = 0x0202, - RES_TABLE_LIBRARY_TYPE = 0x0203 + RES_TABLE_PACKAGE_TYPE = 0x0200, + RES_TABLE_TYPE_TYPE = 0x0201, + RES_TABLE_TYPE_SPEC_TYPE = 0x0202, + RES_TABLE_LIBRARY_TYPE = 0x0203 }; -struct ResXMLTree_node -{ +struct ResXMLTree_node { void *header; // Line number in original source file at which this element appeared. uint32_t lineNumber; @@ -51,8 +53,8 @@ struct ResXMLTree_node class ResXMLTree; -class ResXMLParser -{ +class ResXMLParser { + public: enum event_code_t { BAD_DOCUMENT = -1, @@ -68,61 +70,63 @@ public: TEXT = RES_XML_CDATA_TYPE }; -public: - friend class ResXMLTree; - - event_code_t nextNode(); - const ResXMLTree& mTree; - event_code_t mEventCode; - const ResXMLTree_node* mCurNode; - const void* mCurExt; + const ResXMLTree &mTree; + event_code_t mEventCode; + const ResXMLTree_node *mCurNode; + const void *mCurExt; }; -class ResStringPool -{ +class ResStringPool { + public: - int32_t mError; - void* mOwnedData; - const void* mHeader; - size_t mSize; - mutable pthread_mutex_t mDecodeLock; - const uint32_t* mEntries; - const uint32_t* mEntryStyles; - const void* mStrings; - char16_t mutable** mCache; - uint32_t mStringPoolSize; // number of uint16_t - const uint32_t* mStyles; - uint32_t mStylePoolSize; // number of uint32_t + status_t mError; + void *mOwnedData; + const void *mHeader; + size_t mSize; + mutable pthread_mutex_t mDecodeLock; + const uint32_t *mEntries; + const uint32_t *mEntryStyles; + const void *mStrings; + char16_t mutable **mCache; + uint32_t mStringPoolSize; // number of uint16_t + const uint32_t *mStyles; + uint32_t mStylePoolSize; // number of uint32_t }; -class ResXMLTree : public ResXMLParser -{ +class ResXMLTree : public ResXMLParser { + public: - friend class ResXMLParser; - int32_t validateNode(const ResXMLTree_node* node) const; - void* mDynamicRefTable; - int32_t mError; - void* mOwnedData; - const void* mHeader; - size_t mSize; - const uint8_t* mDataEnd; - ResStringPool mStrings; - const uint32_t* mResIds; - size_t mNumResIds; - const ResXMLTree_node* mRootNode; - const void* mRootExt; - event_code_t mRootCode; + void *mDynamicRefTable; + status_t mError; + void *mOwnedData; + const void *mHeader; + size_t mSize; + const uint8_t *mDataEnd; + ResStringPool mStrings; + const uint32_t *mResIds; + size_t mNumResIds; + const ResXMLTree_node *mRootNode; + const void *mRootExt; + event_code_t mRootCode; }; -struct ResXMLTree_attrExt -{ +struct ResStringPool_ref { + + // Index into the string pool table (uint32_t-offset from the indices + // immediately after ResStringPool_header) at which to find the location + // of the string data in the pool. + uint32_t index; +}; + +struct ResXMLTree_attrExt { + // String of the full namespace of this element. - void* ns; + struct ResStringPool_ref ns; // String name of this node if it is an ELEMENT; the raw // character data if this is a CDATA node. - void* name; + struct ResStringPool_ref name; // Byte offset from the start of this structure where the attributes start. uint16_t attributeStart; @@ -145,8 +149,8 @@ struct ResXMLTree_attrExt uint16_t styleIndex; }; -struct Res_value -{ +struct Res_value { + // Number of bytes in this structure. uint16_t size; // Always set to 0. @@ -253,18 +257,17 @@ struct Res_value // The data for this item, as interpreted according to dataType. typedef uint32_t data_type; data_type data; - void copyFrom_dtoh(const Res_value& src); }; -struct ResXMLTree_attribute -{ +struct ResXMLTree_attribute { // Namespace of this attribute. - void* ns; + struct ResStringPool_ref ns; // Name of this attribute. - void* name; + struct ResStringPool_ref name; + // The original raw string value of this attribute. - void* rawValue; + struct ResStringPool_ref rawValue; // Processesd typed value of this attribute. struct Res_value typedValue; From 39307990868b63c07822d6ce4dd3583f1e2c8d25 Mon Sep 17 00:00:00 2001 From: solohsu Date: Mon, 22 Apr 2019 22:54:27 +0800 Subject: [PATCH 05/13] Disable resources hooking when initialization failed --- .../com/elderdrivers/riru/edxp/config/BaseHookProvider.java | 5 +++++ .../riru/edxp/yahfa/config/YahfaHookProvider.java | 5 +++++ .../com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java | 5 +++++ .../java/com/elderdrivers/riru/edxp/hook/HookProvider.java | 2 ++ .../src/main/java/de/robv/android/xposed/XposedInit.java | 6 ++++++ 5 files changed, 23 insertions(+) diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java index 599fd613..019c13c9 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/BaseHookProvider.java @@ -36,6 +36,11 @@ public abstract class BaseHookProvider implements HookProvider { } + @Override + public boolean initXResourcesNative() { + return false; + } + @Override public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java index f000ae59..90b693b9 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java @@ -29,6 +29,11 @@ public class YahfaHookProvider extends BaseHookProvider { return DexMakerUtils.findMethodNative(hookMethod); } + @Override + public boolean initXResourcesNative() { + return Main.initXResourcesNative(); + } + @Override public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes); diff --git a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java index 322f414a..6ae7370a 100644 --- a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java +++ b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java @@ -92,6 +92,11 @@ public class EdXpConfigGlobal { } + @Override + public boolean initXResourcesNative() { + return false; + } + @Override public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { diff --git a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java index d1759415..a8fccd78 100644 --- a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java +++ b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java @@ -25,5 +25,7 @@ public interface HookProvider { void deoptMethodNative(Object method); + boolean initXResourcesNative(); + void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes); } diff --git a/xposed-bridge/src/main/java/de/robv/android/xposed/XposedInit.java b/xposed-bridge/src/main/java/de/robv/android/xposed/XposedInit.java index ca87553b..4a2d5b87 100644 --- a/xposed-bridge/src/main/java/de/robv/android/xposed/XposedInit.java +++ b/xposed-bridge/src/main/java/de/robv/android/xposed/XposedInit.java @@ -105,6 +105,12 @@ public final class XposedInit { return; } + if (!EdXpConfigGlobal.getHookProvider().initXResourcesNative()) { + Log.e(TAG, "Cannot hook resources"); + disableResources = true; + return; + } + /* * getTopLevelResources(a) * -> getTopLevelResources(b) From 4ee0db8734365a6274fd1af484e5df32e8e553fd Mon Sep 17 00:00:00 2001 From: solohsu Date: Mon, 22 Apr 2019 23:59:19 +0800 Subject: [PATCH 06/13] Bump version --- edxp-core/build.gradle | 2 +- edxp-core/edconfig.tpl | 2 +- edxp-core/template_override/common/util_functions.sh | 2 +- edxp-core/template_override/config.sh | 2 +- edxp-core/template_override/module.prop | 4 ++-- edxp-core/template_override/riru_module.prop | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/edxp-core/build.gradle b/edxp-core/build.gradle index 0b8ab926..665b66ad 100644 --- a/edxp-core/build.gradle +++ b/edxp-core/build.gradle @@ -2,7 +2,7 @@ import org.gradle.internal.os.OperatingSystem apply plugin: 'com.android.library' -version "v0.3.1.8_beta-SNAPSHOT" +version "v0.4.0.0_beta-SNAPSHOT" ext { module_name = "EdXposed" diff --git a/edxp-core/edconfig.tpl b/edxp-core/edconfig.tpl index 5fe6d093..9c3912ce 100644 --- a/edxp-core/edconfig.tpl +++ b/edxp-core/edconfig.tpl @@ -1,4 +1,4 @@ -version=90.0-0.3.1.8-beta-SNAPSHOT ($backend) +version=90.0-0.4.0.0-beta-SNAPSHOT ($backend) arch=arm64 minsdk=23 maxsdk=28 diff --git a/edxp-core/template_override/common/util_functions.sh b/edxp-core/template_override/common/util_functions.sh index 180310da..70d4f5d9 100644 --- a/edxp-core/template_override/common/util_functions.sh +++ b/edxp-core/template_override/common/util_functions.sh @@ -1,6 +1,6 @@ #!/system/bin/sh -EDXP_VERSION="0.3.1.8_beta-SNAPSHOT (3180)" +EDXP_VERSION="0.4.0.0_beta-SNAPSHOT (4000)" ANDROID_SDK=`getprop ro.build.version.sdk` BUILD_DESC=`getprop ro.build.description` PRODUCT=`getprop ro.build.product` diff --git a/edxp-core/template_override/config.sh b/edxp-core/template_override/config.sh index 9d02c8cd..cf929c97 100644 --- a/edxp-core/template_override/config.sh +++ b/edxp-core/template_override/config.sh @@ -41,7 +41,7 @@ LATESTARTSERVICE=false print_modname() { ui_print "************************************" - ui_print " Riru - Ed Xposed v0.3.1.8 " + ui_print " Riru - Ed Xposed v0.4.0.0 " ui_print "************************************" } diff --git a/edxp-core/template_override/module.prop b/edxp-core/template_override/module.prop index fae2ae55..3fd678cc 100644 --- a/edxp-core/template_override/module.prop +++ b/edxp-core/template_override/module.prop @@ -1,7 +1,7 @@ id=riru_edxposed name=Riru - Ed Xposed -version=v0.3.1.8_beta-SNAPSHOT -versionCode=3180 +version=v0.4.0.0_beta-SNAPSHOT +versionCode=4000 author=solohsu & MlgmXyysd description=Magisk version of Xposed. Require Riru - Core installed. minMagisk=17000 diff --git a/edxp-core/template_override/riru_module.prop b/edxp-core/template_override/riru_module.prop index d45abdd1..0928a8a5 100644 --- a/edxp-core/template_override/riru_module.prop +++ b/edxp-core/template_override/riru_module.prop @@ -1,5 +1,5 @@ name=Ed Xposed -version=v0.3.1.8_beta-SNAPSHOT -versionCode=3180 +version=v0.4.0.0_beta-SNAPSHOT +versionCode=4000 author=solohsu & MlgmXyysd description=Magisk version of Xposed. Require Riru - Core installed. From bc8b2bc16bafe58c414a7fe9bc426fd22cce70fc Mon Sep 17 00:00:00 2001 From: solohsu Date: Tue, 23 Apr 2019 00:18:06 +0800 Subject: [PATCH 07/13] Add resources hooking support for all variants --- .../main/java/com/elderdrivers/riru/edxp/Main.java | 6 ++++++ .../edxp/sandhook/config/SandHookProvider.java | 13 +++++++++++++ .../main/java/com/elderdrivers/riru/edxp/Main.java | 6 ++++++ .../riru/edxp/whale/config/WhaleHookProvider.java | 14 ++++++++++++++ .../elderdrivers/riru/edxp/yahfa/entry/Router.java | 1 - 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java index ba3214c2..ef87ebb1 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java @@ -1,6 +1,8 @@ package com.elderdrivers.riru.edxp; import android.annotation.SuppressLint; +import android.content.res.Resources; +import android.content.res.XResources; import android.os.Build; import android.os.Process; @@ -147,4 +149,8 @@ public class Main implements KeepAll { public static native void resumeAllThreads(long obj); public static native int waitForGcToComplete(long thread); + + public static native boolean initXResourcesNative(); + + public static native void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes); } diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java index df543516..ce6d94f9 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java @@ -1,7 +1,10 @@ package com.elderdrivers.riru.edxp.sandhook.config; +import android.content.res.Resources; +import android.content.res.XResources; import android.util.Log; +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.BaseHookProvider; import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter; import com.elderdrivers.riru.edxp.sandhook.dexmaker.DynamicBridge; @@ -53,4 +56,14 @@ public class SandHookProvider extends BaseHookProvider { public long getMethodId(Member member) { return 0; } + + @Override + public boolean initXResourcesNative() { + return Main.initXResourcesNative(); + } + + @Override + public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { + Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes); + } } diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/Main.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/Main.java index 2bf72fb4..3f1019be 100644 --- a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/Main.java +++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/Main.java @@ -1,6 +1,8 @@ package com.elderdrivers.riru.edxp; import android.annotation.SuppressLint; +import android.content.res.Resources; +import android.content.res.XResources; import android.os.Build; import android.os.Process; @@ -138,4 +140,8 @@ public class Main implements KeepAll { public static native void resumeAllThreads(long obj); public static native int waitForGcToComplete(long thread); + + public static native boolean initXResourcesNative(); + + public static native void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes); } diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java index 571b5cb8..c56bdadc 100644 --- a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java +++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java @@ -1,5 +1,9 @@ package com.elderdrivers.riru.edxp.whale.config; +import android.content.res.Resources; +import android.content.res.XResources; + +import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.BaseHookProvider; import com.lody.whale.WhaleRuntime; @@ -43,4 +47,14 @@ public class WhaleHookProvider extends BaseHookProvider { public long getMethodId(Member member) { return WhaleRuntime.getMethodSlot(member); } + + @Override + public boolean initXResourcesNative() { + return Main.initXResourcesNative(); + } + + @Override + public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { + Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes); + } } diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/Router.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/Router.java index 5eae8a75..57493cab 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/Router.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/Router.java @@ -58,7 +58,6 @@ public class Router { } Router.startBootstrapHook(isSystem); XposedInit.initForZygote(isSystem); - Main.initXResourcesNative(); } catch (Throwable t) { Utils.logE("error during Xposed initialization", t); XposedBridge.disableHooks = true; From ee0995528a7afd0d4dd01a01fd4c2fa5cd7ebec2 Mon Sep 17 00:00:00 2001 From: solohsu Date: Tue, 23 Apr 2019 00:18:28 +0800 Subject: [PATCH 08/13] Minify all variant builds --- edxp-sandhook/build.gradle | 4 ++-- edxp-whale/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/edxp-sandhook/build.gradle b/edxp-sandhook/build.gradle index 9ee9c429..10c01637 100644 --- a/edxp-sandhook/build.gradle +++ b/edxp-sandhook/build.gradle @@ -13,7 +13,7 @@ android { buildTypes { release { - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } @@ -58,7 +58,7 @@ afterEvaluate { task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") { dependsOn tasks.getByPath(":edxp-common:copyCommonProperties") - from "${buildDir}/intermediates/dex/${variantNameLowered}/mergeDex${variantNameCapped}/out/" + from "${buildDir}/intermediates/transforms/dexMerger/${variantNameLowered}/0/" destinationDir file(myTemplatePath + "system/framework/") baseName "edxp" doLast { diff --git a/edxp-whale/build.gradle b/edxp-whale/build.gradle index 5f41eadd..3d8d17d1 100644 --- a/edxp-whale/build.gradle +++ b/edxp-whale/build.gradle @@ -13,7 +13,7 @@ android { buildTypes { release { - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } @@ -56,7 +56,7 @@ afterEvaluate { task("makeAndCopy${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") { dependsOn tasks.getByPath(":edxp-common:copyCommonProperties") - from "${buildDir}/intermediates/dex/${variantNameLowered}/mergeDex${variantNameCapped}/out/" + from "${buildDir}/intermediates/transforms/dexMerger/${variantNameLowered}/0/" destinationDir file(myTemplatePath + "system/framework/") baseName "edxp" doLast { From ae22d9839a8b61a7d10128f1a1fd6f1649d46087 Mon Sep 17 00:00:00 2001 From: solohsu Date: Tue, 23 Apr 2019 00:18:06 +0800 Subject: [PATCH 09/13] Add resources hooking support for all variants --- .../riru/edxp/sandhook/config/SandHookProvider.java | 10 ++++++++++ .../riru/edxp/whale/config/WhaleHookProvider.java | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java index ce6d94f9..9cfc9a6f 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java @@ -66,4 +66,14 @@ public class SandHookProvider extends BaseHookProvider { public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes); } + + @Override + public boolean initXResourcesNative() { + return Main.initXResourcesNative(); + } + + @Override + public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { + Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes); + } } diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java index c56bdadc..6c2dea0d 100644 --- a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java +++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java @@ -57,4 +57,14 @@ public class WhaleHookProvider extends BaseHookProvider { public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes); } + + @Override + public boolean initXResourcesNative() { + return Main.initXResourcesNative(); + } + + @Override + public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { + Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes); + } } From ebd4d00ce216b2c6783d4b967a808d2925cd37b1 Mon Sep 17 00:00:00 2001 From: solohsu Date: Wed, 24 Apr 2019 11:17:47 +0800 Subject: [PATCH 10/13] Fix compile error --- .../sandhook/config/SandHookProvider.java | 10 ---------- hiddenapi-stubs/libs/framework-stub.jar | Bin 15348 -> 15348 bytes 2 files changed, 10 deletions(-) diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java index 87e3b743..b8279053 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java @@ -70,14 +70,4 @@ public class SandHookProvider extends BaseHookProvider { public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes); } - - @Override - public boolean initXResourcesNative() { - return Main.initXResourcesNative(); - } - - @Override - public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) { - Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes); - } } diff --git a/hiddenapi-stubs/libs/framework-stub.jar b/hiddenapi-stubs/libs/framework-stub.jar index b1d3a0fb55a4d3f9d54c79159a43bc35caf6e62e..e68daf9364acdec05b8f191010aab374cd5673f7 100644 GIT binary patch delta 739 zcmexT{-vBZz?+$ci-CcIgF!WF#zbB<=IOhqPc-vlo*X%2VzUaETBQx5Hs09J1fnO; zVg?IOKFF*E;%|P(ynqqRn8?}yW=s}gR|TuIVb=spWUvc^wQjEC+zC-(#jOigk;QEQ zRxy`58e+z89wxBTP5gxrl_dg(U==F_c0p963T=oB3G%J;bQ1nqdD>(4f-;7TCc;fFR_C7|CbfSYzJ$I JTc%sb0swWu4h;YR delta 739 zcmYL{xl3bF6vkijbP|mcO+<~cP|Pwum%J@+OI&bpaEmi8*r;G51hG&M(y0&|LBVic zm;upYEQaayfud6kgZ>MOjij&;!KJYfEi~`D#oXfk&OP@#-#Pd4o>EV#;;+4IULnIc zoy>}NBU2n=Ytri(r3Xj{fZq)k_Ame1*_?)aX9M6rma}L+u)8({nfyhYr6gsJAQ*Sl zTQrjnuN}dB!7)`Bt|lm^Ts#i8U86WKUyJOJ9=K;=O6E!gnExs{pOP|APu&8Qv;@-#$Z4n+ikna~SQ%DJb3^wtxn;LL zxm8PkgadAt`TtnfHAy%D&+G6bwK}iIeTj_|VWn^?1T)+@6?8R5kyvb8LBe!5uhMJo zL}Zj)MB2bB-cKaH0&#-Z!DW5`4}ZmrphwWagfImD6b7Mt79=pM`$@N0jW{Xxf`{T~ z@J)XaZD3ZO06U^zaK0X;y>6o;G_MRJ|4pGY-Yfmsx2g)5 zQt?ZXGxZbpg<1rRYjJSQ@RLoALwu_BfgXJnoYAMjtd8d-KBFD`F{2aQGlswygL=2e J=qy{Y@po#Q^o{@k From 9e68041901797d8b61b685007dd55eac493ee556 Mon Sep 17 00:00:00 2001 From: solohsu Date: Wed, 24 Apr 2019 11:24:48 +0800 Subject: [PATCH 11/13] Bump version --- edxp-core/build.gradle | 2 +- edxp-core/edconfig.tpl | 2 +- edxp-core/template_override/common/util_functions.sh | 2 +- edxp-core/template_override/config.sh | 2 +- edxp-core/template_override/module.prop | 4 ++-- edxp-core/template_override/riru_module.prop | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/edxp-core/build.gradle b/edxp-core/build.gradle index 665b66ad..cf759492 100644 --- a/edxp-core/build.gradle +++ b/edxp-core/build.gradle @@ -2,7 +2,7 @@ import org.gradle.internal.os.OperatingSystem apply plugin: 'com.android.library' -version "v0.4.0.0_beta-SNAPSHOT" +version "v0.4.0.1_beta-SNAPSHOT" ext { module_name = "EdXposed" diff --git a/edxp-core/edconfig.tpl b/edxp-core/edconfig.tpl index 9c3912ce..95f840e1 100644 --- a/edxp-core/edconfig.tpl +++ b/edxp-core/edconfig.tpl @@ -1,4 +1,4 @@ -version=90.0-0.4.0.0-beta-SNAPSHOT ($backend) +version=90.0-0.4.0.1-beta-SNAPSHOT ($backend) arch=arm64 minsdk=23 maxsdk=28 diff --git a/edxp-core/template_override/common/util_functions.sh b/edxp-core/template_override/common/util_functions.sh index 70d4f5d9..068bd15e 100644 --- a/edxp-core/template_override/common/util_functions.sh +++ b/edxp-core/template_override/common/util_functions.sh @@ -1,6 +1,6 @@ #!/system/bin/sh -EDXP_VERSION="0.4.0.0_beta-SNAPSHOT (4000)" +EDXP_VERSION="0.4.0.1_beta-SNAPSHOT (4010)" ANDROID_SDK=`getprop ro.build.version.sdk` BUILD_DESC=`getprop ro.build.description` PRODUCT=`getprop ro.build.product` diff --git a/edxp-core/template_override/config.sh b/edxp-core/template_override/config.sh index cf929c97..83406ddb 100644 --- a/edxp-core/template_override/config.sh +++ b/edxp-core/template_override/config.sh @@ -41,7 +41,7 @@ LATESTARTSERVICE=false print_modname() { ui_print "************************************" - ui_print " Riru - Ed Xposed v0.4.0.0 " + ui_print " Riru - Ed Xposed v0.4.0.1 " ui_print "************************************" } diff --git a/edxp-core/template_override/module.prop b/edxp-core/template_override/module.prop index 3fd678cc..9473fddb 100644 --- a/edxp-core/template_override/module.prop +++ b/edxp-core/template_override/module.prop @@ -1,7 +1,7 @@ id=riru_edxposed name=Riru - Ed Xposed -version=v0.4.0.0_beta-SNAPSHOT -versionCode=4000 +version=v0.4.0.1_beta-SNAPSHOT +versionCode=4010 author=solohsu & MlgmXyysd description=Magisk version of Xposed. Require Riru - Core installed. minMagisk=17000 diff --git a/edxp-core/template_override/riru_module.prop b/edxp-core/template_override/riru_module.prop index 0928a8a5..25152672 100644 --- a/edxp-core/template_override/riru_module.prop +++ b/edxp-core/template_override/riru_module.prop @@ -1,5 +1,5 @@ name=Ed Xposed -version=v0.4.0.0_beta-SNAPSHOT -versionCode=4000 +version=v0.4.0.1_beta-SNAPSHOT +versionCode=4010 author=solohsu & MlgmXyysd description=Magisk version of Xposed. Require Riru - Core installed. From e5e5ccf3b1e1e9e0a877f4eeb353cdc094da4891 Mon Sep 17 00:00:00 2001 From: solohsu Date: Wed, 24 Apr 2019 17:11:22 +0800 Subject: [PATCH 12/13] Clean up code --- .../src/main/java/android/content/res/XResources.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/xposed-bridge/src/main/java/android/content/res/XResources.java b/xposed-bridge/src/main/java/android/content/res/XResources.java index 7d8de8e3..412a7dc6 100644 --- a/xposed-bridge/src/main/java/android/content/res/XResources.java +++ b/xposed-bridge/src/main/java/android/content/res/XResources.java @@ -1106,9 +1106,6 @@ public class XResources extends Resources { @Override public XmlResourceParser getXml(int id) throws NotFoundException { Object replacement = getReplacement(id); - if (id == 2131034112) { - XposedBridge.log("2131034112: " + replacement); - } if (replacement instanceof XResForwarder) { Resources repRes = ((XResForwarder) replacement).getResources(); int repId = ((XResForwarder) replacement).getId(); From 41c7b42393bb856eca0afe1233e041c8fa0337cd Mon Sep 17 00:00:00 2001 From: solohsu Date: Wed, 24 Apr 2019 17:56:44 +0800 Subject: [PATCH 13/13] SandHook: update hooklib --- edxp-sandhook/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edxp-sandhook/build.gradle b/edxp-sandhook/build.gradle index 10c01637..415ecdfc 100644 --- a/edxp-sandhook/build.gradle +++ b/edxp-sandhook/build.gradle @@ -24,7 +24,7 @@ dependencies { compileOnly files("${hiddenApiStubJarFilePath}") implementation project(':edxp-common') implementation project(':xposed-bridge') - implementation 'com.swift.sandhook:hooklib:3.5.6' + implementation 'com.swift.sandhook:hooklib:3.5.8' compileOnly project(':dexmaker') }