Merge pull request #221 from ElderDrivers/resources-hook
Add resources hook
This commit is contained in:
commit
edda665778
|
|
@ -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,14 @@ public abstract class BaseHookProvider implements HookProvider {
|
|||
public void deoptMethodNative(Object method) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initXResourcesNative() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.1_beta-SNAPSHOT"
|
||||
|
||||
ext {
|
||||
module_name = "EdXposed"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
version=90.0-0.3.1.8-beta-SNAPSHOT ($backend)
|
||||
version=90.0-0.4.0.1-beta-SNAPSHOT ($backend)
|
||||
arch=arm64
|
||||
minsdk=23
|
||||
maxsdk=28
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,169 @@
|
|||
//
|
||||
// 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)(const 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 *(*)(const 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;
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
//
|
||||
// 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);
|
||||
|
||||
typedef int32_t status_t;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
const ResXMLTree &mTree;
|
||||
event_code_t mEventCode;
|
||||
const ResXMLTree_node *mCurNode;
|
||||
const void *mCurExt;
|
||||
};
|
||||
|
||||
class ResStringPool {
|
||||
|
||||
public:
|
||||
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 {
|
||||
|
||||
public:
|
||||
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 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.
|
||||
struct ResStringPool_ref ns;
|
||||
|
||||
// String name of this node if it is an ELEMENT; the raw
|
||||
// character data if this is a CDATA node.
|
||||
struct ResStringPool_ref 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;
|
||||
};
|
||||
|
||||
struct ResXMLTree_attribute {
|
||||
// Namespace of this attribute.
|
||||
struct ResStringPool_ref ns;
|
||||
|
||||
// Name of this attribute.
|
||||
struct ResStringPool_ref name;
|
||||
|
||||
// The original raw string value of this attribute.
|
||||
struct ResStringPool_ref rawValue;
|
||||
|
||||
// Processesd typed value of this attribute.
|
||||
struct Res_value typedValue;
|
||||
};
|
||||
|
||||
#endif //EDXPOSED_TEMP_RESOURCE_HOOK_H
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/system/bin/sh
|
||||
|
||||
EDXP_VERSION="0.3.1.8_beta-SNAPSHOT (3180)"
|
||||
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`
|
||||
|
|
|
|||
|
|
@ -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.1 "
|
||||
ui_print "************************************"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
id=riru_edxposed
|
||||
name=Riru - Ed Xposed
|
||||
version=v0.3.1.8_beta-SNAPSHOT
|
||||
versionCode=3180
|
||||
version=v0.4.0.1_beta-SNAPSHOT
|
||||
versionCode=4010
|
||||
author=solohsu & MlgmXyysd
|
||||
description=Magisk version of Xposed. Require Riru - Core installed.
|
||||
minMagisk=17000
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
name=Ed Xposed
|
||||
version=v0.3.1.8_beta-SNAPSHOT
|
||||
versionCode=3180
|
||||
version=v0.4.0.1_beta-SNAPSHOT
|
||||
versionCode=4010
|
||||
author=solohsu & MlgmXyysd
|
||||
description=Magisk version of Xposed. Require Riru - Core installed.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ android {
|
|||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
|
@ -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')
|
||||
}
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
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;
|
||||
|
|
@ -58,4 +60,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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
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;
|
||||
|
|
@ -54,4 +57,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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
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;
|
||||
|
|
@ -35,4 +38,14 @@ public class YahfaHookProvider extends BaseHookProvider {
|
|||
public void deoptMethodNative(Object method) {
|
||||
Main.deoptMethodNative(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initXResourcesNative() {
|
||||
return Main.initXResourcesNative();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) {
|
||||
Main.rewriteXmlReferencesNative(parserPtr, origRes, repRes);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
package android.content.res;
|
||||
|
||||
public class ResourcesImpl {
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -34,8 +36,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 +52,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<HashMap<String, Object>> sReplacements = new SparseArray<>();
|
||||
private static final SparseArray<HashMap<String, ResourceNames>> sResourceNames = new SparseArray<>();
|
||||
|
||||
|
|
@ -80,11 +80,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 +176,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;
|
||||
|
|
@ -1118,7 +1126,7 @@ public class XResources extends XResourcesSuperClass {
|
|||
}
|
||||
|
||||
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)
|
||||
|
|
@ -1128,7 +1136,9 @@ public class XResources extends XResourcesSuperClass {
|
|||
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.
|
||||
|
|
@ -1253,12 +1263,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) {
|
||||
|
|
|
|||
|
|
@ -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,15 @@ public class EdXpConfigGlobal {
|
|||
public void deoptMethodNative(Object method) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initXResourcesNative() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,8 @@ public interface HookProvider {
|
|||
Object findMethodNative(Class clazz, String methodName, String methodSig);
|
||||
|
||||
void deoptMethodNative(Object method);
|
||||
|
||||
boolean initXResourcesNative();
|
||||
|
||||
void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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,220 @@ 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;
|
||||
}
|
||||
|
||||
if (!EdXpConfigGlobal.getHookProvider().initXResourcesNative()) {
|
||||
Log.e(TAG, "Cannot hook 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<Object> 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<WeakReference<Resources>> resourceReferences;
|
||||
if (activityToken != null) {
|
||||
Object activityResources = callMethod(param.thisObject, "getOrCreateActivityResourcesStructLocked", activityToken);
|
||||
resourceReferences = (ArrayList<WeakReference<Resources>>) getObjectField(activityResources, "activityResources");
|
||||
} else {
|
||||
resourceReferences = (ArrayList<WeakReference<Resources>>) 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<Object, WeakReference<Resources>> mActiveResources =
|
||||
(Map<Object, WeakReference<Resources>>) getObjectField(param.thisObject, "mActiveResources");
|
||||
Object lockObject = (Build.VERSION.SDK_INT <= 18)
|
||||
? getObjectField(param.thisObject, "mPackages") : param.thisObject;
|
||||
|
||||
synchronized (lockObject) {
|
||||
WeakReference<Resources> existing = mActiveResources.put(key, new WeakReference<Resources>(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<Method> 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() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue