Add support for resources hooking. (2/2)

This commit is contained in:
solohsu 2019-04-22 16:12:18 +08:00
parent 711c589088
commit bf9b270775
13 changed files with 633 additions and 119 deletions

View File

@ -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) {
}
}

View File

@ -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 \

View File

@ -0,0 +1,43 @@
//
// Created by solo on 2019/3/24.
//
#ifndef EDXPOSED_TEMP_BYTEORDER_H
#define EDXPOSED_TEMP_BYTEORDER_H
#include <cstdint>
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

View File

@ -7,6 +7,7 @@
#include <dlfcn.h>
#include <inject/config_manager.h>
#include <native_hook/native_hook.h>
#include <native_hook/resource_hook.h>
#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 {

View File

@ -4,6 +4,8 @@
#include <jni.h>
#include <unistd.h>
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
extern jobject gInjectDexClassLoader;
void loadDexAndInit(JNIEnv *env, const char *dexPath);

View File

@ -0,0 +1,161 @@
//
// Created by solo on 2019/3/24.
//
#include <jni.h>
#include <include/ByteOrder.h>
#include <include/logging.h>
#include <dlfcn.h>
#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<int32_t (*)(void *)>(dlsym(fwHandle,
"_ZN7android12ResXMLParser4nextEv"));
if (!ResXMLParser_next) {
LOGE("can't get ResXMLParser_next: %s", dlerror());
return false;
}
ResXMLParser_restart = reinterpret_cast<void (*)(void *)>(dlsym(fwHandle,
"_ZN7android12ResXMLParser7restartEv"));
if (!ResXMLParser_restart) {
LOGE("can't get ResXMLParser_restart: %s", dlerror());
return false;
}
ResXMLParser_getAttributeNameID = reinterpret_cast<int32_t (*)(void *, int)>(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<char16_t *(*)(void *, int32_t, size_t *)>(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<jclass>(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);
}

View File

@ -0,0 +1,256 @@
//
// Created by solo on 2019/3/24.
//
#ifndef EDXPOSED_TEMP_RESOURCE_HOOK_H
#define EDXPOSED_TEMP_RESOURCE_HOOK_H
#include <cstdint>
#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

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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<String, CopyOnWriteSortedSet<XC_LayoutInflated>> inner;
// synchronized (sLayoutCallbacks) {
// inner = sLayoutCallbacks.get(id);
// }
// if (inner != null) {
// CopyOnWriteSortedSet<XC_LayoutInflated> 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<String, ResourceNames> 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<String, CopyOnWriteSortedSet<XC_LayoutInflated>> inner;
synchronized (sLayoutCallbacks) {
inner = sLayoutCallbacks.get(id);
}
if (inner != null) {
CopyOnWriteSortedSet<XC_LayoutInflated> 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<String, ResourceNames> 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.

View File

@ -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) {
}
};
}

View File

@ -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);
}