From aa98da59da02073f7ba47bfb8ad0c20b6e8493ee Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sat, 20 Mar 2021 01:23:01 +0800 Subject: [PATCH] [core] Fix resource hook on S (#377) --- .../framework/androidfw/resource_types.h | 162 +++++++++++++----- .../main/cpp/main/src/jni/resources_hook.cpp | 40 +++-- 2 files changed, 142 insertions(+), 60 deletions(-) diff --git a/core/src/main/cpp/main/include/framework/androidfw/resource_types.h b/core/src/main/cpp/main/include/framework/androidfw/resource_types.h index f1707fd9..3fe45aa1 100644 --- a/core/src/main/cpp/main/include/framework/androidfw/resource_types.h +++ b/core/src/main/cpp/main/include/framework/androidfw/resource_types.h @@ -20,6 +20,7 @@ #pragma once +#include #include // @ApiSensitive(Level.MIDDLE) @@ -27,13 +28,52 @@ namespace android { typedef int32_t status_t; + + template + struct unexpected { + E val_; + }; + + template + struct expected { + using value_type = T; + using error_type = E; + using unexpected_type = unexpected; + std::variant var_; + + constexpr bool has_value() const noexcept { return var_.index() == 0; } + + constexpr const T &value() const &{ return std::get(var_); } + + constexpr T &value() &{ return std::get(var_); } + + constexpr const T *operator->() const { return std::addressof(value()); } + + constexpr T *operator->() { return std::addressof(value()); } + }; + + enum class IOError { + // Used when reading a file residing on an IncFs file-system times out. + PAGES_MISSING = -1, + }; + + template + struct BasicStringPiece { + const TChar *data_; + size_t length_; + }; + + using NullOrIOError = std::variant; + + using StringPiece16 = BasicStringPiece; + 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_FIRST_CHUNK_TYPE = 0x0100, RES_XML_START_NAMESPACE_TYPE = 0x0100, RES_XML_END_NAMESPACE_TYPE = 0x0101, RES_XML_START_ELEMENT_TYPE = 0x0102, @@ -42,9 +82,9 @@ namespace android { 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 @@ -98,6 +138,46 @@ namespace android { uint32_t mStringPoolSize; // number of uint16_t const uint32_t *mStyles; uint32_t mStylePoolSize; // number of uint32_t + + using stringAtRet = expected; + + CREATE_MEM_FUNC_SYMBOL_ENTRY(stringAtRet, stringAtS, void *thiz, size_t idx) { + if (stringAtSSym) { + return stringAtSSym(thiz, idx); + } + return {.var_ = unexpected{.val_ = std::nullopt}}; + + }; + + CREATE_MEM_FUNC_SYMBOL_ENTRY(const char16_t*, stringAt, void *thiz, size_t idx, + size_t *u16len) { + if (stringAtSym) { + return stringAtSym(thiz, idx, u16len); + } else { + *u16len = 0u; + return nullptr; + } + }; + + StringPiece16 stringAt(size_t idx) const { + if (stringAtSym) { + size_t len; + const char16_t *str = stringAt(const_cast(this), idx, &len); + return {str, len}; + } else if (stringAtSSym) { + auto str = stringAtS(const_cast(this), idx); + if (str.has_value()) { + return {str->data_, str->length_}; + } + } + return {nullptr, 0u}; + } + + static bool setup(void* handle) { + RETRIEVE_MEM_FUNC_SYMBOL(stringAt, LP_SELECT("_ZNK7android13ResStringPool8stringAtEjPj", "_ZNK7android13ResStringPool8stringAtEmPm")); + RETRIEVE_MEM_FUNC_SYMBOL(stringAtS, LP_SELECT("_ZNK7android13ResStringPool8stringAtEj", "_ZNK7android13ResStringPool8stringAtEm")); + return !stringAtSym || !stringAtSSym; + } }; @@ -167,99 +247,99 @@ namespace android { enum : uint8_t { // The 'data' is either 0 or 1, specifying this resource is either // undefined or empty, respectively. - TYPE_NULL = 0x00, + TYPE_NULL = 0x00, // The 'data' holds a ResTable_ref, a reference to another resource // table entry. - TYPE_REFERENCE = 0x01, + TYPE_REFERENCE = 0x01, // The 'data' holds an attribute resource identifier. - TYPE_ATTRIBUTE = 0x02, + TYPE_ATTRIBUTE = 0x02, // The 'data' holds an index into the containing resource table's // global value string pool. - TYPE_STRING = 0x03, + TYPE_STRING = 0x03, // The 'data' holds a single-precision floating point number. - TYPE_FLOAT = 0x04, + TYPE_FLOAT = 0x04, // The 'data' holds a complex number encoding a dimension value, // such as "100in". - TYPE_DIMENSION = 0x05, + TYPE_DIMENSION = 0x05, // The 'data' holds a complex number encoding a fraction of a // container. - TYPE_FRACTION = 0x06, + 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, + 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, + TYPE_DYNAMIC_ATTRIBUTE = 0x08, // Beginning of integer flavors... - TYPE_FIRST_INT = 0x10, + TYPE_FIRST_INT = 0x10, // The 'data' is a raw integer value of the form n..n. - TYPE_INT_DEC = 0x10, + TYPE_INT_DEC = 0x10, // The 'data' is a raw integer value of the form 0xn..n. - TYPE_INT_HEX = 0x11, + TYPE_INT_HEX = 0x11, // The 'data' is either 0 or 1, for input "false" or "true" respectively. - TYPE_INT_BOOLEAN = 0x12, + TYPE_INT_BOOLEAN = 0x12, // Beginning of color integer flavors... - TYPE_FIRST_COLOR_INT = 0x1c, + TYPE_FIRST_COLOR_INT = 0x1c, // The 'data' is a raw integer value of the form #aarrggbb. - TYPE_INT_COLOR_ARGB8 = 0x1c, + TYPE_INT_COLOR_ARGB8 = 0x1c, // The 'data' is a raw integer value of the form #rrggbb. - TYPE_INT_COLOR_RGB8 = 0x1d, + TYPE_INT_COLOR_RGB8 = 0x1d, // The 'data' is a raw integer value of the form #argb. - TYPE_INT_COLOR_ARGB4 = 0x1e, + TYPE_INT_COLOR_ARGB4 = 0x1e, // The 'data' is a raw integer value of the form #rgb. - TYPE_INT_COLOR_RGB4 = 0x1f, + TYPE_INT_COLOR_RGB4 = 0x1f, // ...end of integer flavors. - TYPE_LAST_COLOR_INT = 0x1f, + TYPE_LAST_COLOR_INT = 0x1f, // ...end of integer flavors. - TYPE_LAST_INT = 0x1f + 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_SHIFT = 0, COMPLEX_UNIT_MASK = 0xf, // TYPE_DIMENSION: Value is raw pixels. - COMPLEX_UNIT_PX = 0, + COMPLEX_UNIT_PX = 0, // TYPE_DIMENSION: Value is Device Independent Pixels. - COMPLEX_UNIT_DIP = 1, + COMPLEX_UNIT_DIP = 1, // TYPE_DIMENSION: Value is a Scaled device independent Pixels. - COMPLEX_UNIT_SP = 2, + COMPLEX_UNIT_SP = 2, // TYPE_DIMENSION: Value is in points. - COMPLEX_UNIT_PT = 3, + COMPLEX_UNIT_PT = 3, // TYPE_DIMENSION: Value is in inches. - COMPLEX_UNIT_IN = 4, + COMPLEX_UNIT_IN = 4, // TYPE_DIMENSION: Value is in millimeters. - COMPLEX_UNIT_MM = 5, + COMPLEX_UNIT_MM = 5, // TYPE_FRACTION: A basic fraction of the overall size. - COMPLEX_UNIT_FRACTION = 0, + COMPLEX_UNIT_FRACTION = 0, // TYPE_FRACTION: A fraction of the parent size. - COMPLEX_UNIT_FRACTION_PARENT = 1, + 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_SHIFT = 4, COMPLEX_RADIX_MASK = 0x3, // The mantissa is an integral number -- i.e., 0xnnnnnn.0 - COMPLEX_RADIX_23p0 = 0, + COMPLEX_RADIX_23p0 = 0, // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn - COMPLEX_RADIX_16p7 = 1, + COMPLEX_RADIX_16p7 = 1, // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn - COMPLEX_RADIX_8p15 = 2, + COMPLEX_RADIX_8p15 = 2, // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn - COMPLEX_RADIX_0p23 = 3, + 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_SHIFT = 8, COMPLEX_MANTISSA_MASK = 0xffffff }; // Possible data values for TYPE_NULL. enum { // The value is not defined. - DATA_NULL_UNDEFINED = 0, + DATA_NULL_UNDEFINED = 0, // The value is explicitly defined as empty. - DATA_NULL_EMPTY = 1 + DATA_NULL_EMPTY = 1 }; // The data for this item, as interpreted according to dataType. typedef uint32_t data_type; @@ -280,4 +360,4 @@ namespace android { struct Res_value typedValue; }; -} \ No newline at end of file +} diff --git a/core/src/main/cpp/main/src/jni/resources_hook.cpp b/core/src/main/cpp/main/src/jni/resources_hook.cpp index 3f823077..75a95e41 100644 --- a/core/src/main/cpp/main/src/jni/resources_hook.cpp +++ b/core/src/main/cpp/main/src/jni/resources_hook.cpp @@ -46,7 +46,6 @@ namespace lspd { static TYPE_NEXT ResXMLParser_next = nullptr; static TYPE_RESTART ResXMLParser_restart = nullptr; static TYPE_GET_ATTR_NAME_ID ResXMLParser_getAttributeNameID = nullptr; - static TYPE_STRING_AT ResStringPool_stringAt = nullptr; static bool PrepareSymbols() { ScopedDlHandle fw_handle(kLibFwPath.c_str()); @@ -66,13 +65,12 @@ namespace lspd { "_ZNK7android12ResXMLParser18getAttributeNameIDEm")))) { return false; } - return (ResStringPool_stringAt = fw_handle.DlSym( - LP_SELECT("_ZNK7android13ResStringPool8stringAtEjPj", - "_ZNK7android13ResStringPool8stringAtEmPm"))) != nullptr; + return android::ResStringPool::setup(fw_handle.Get()); } LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, initXResourcesNative) { - classXResources = Context::GetInstance()->FindClassFromCurrentLoader(env, kXResourcesClassName); + classXResources = Context::GetInstance()->FindClassFromCurrentLoader(env, + kXResourcesClassName); if (!classXResources) { LOGE("Error while loading XResources class '%s':", kXResourcesClassName); return JNI_FALSE; @@ -109,9 +107,11 @@ namespace lspd { return JNI_FALSE; } - LSP_DEF_NATIVE_METHOD(jobject, ResourcesHook, buildDummyClassLoader, jobject parent, jobject resource_super_class, jobject typed_array_super_class) { + LSP_DEF_NATIVE_METHOD(jobject, ResourcesHook, buildDummyClassLoader, jobject parent, + jobject resource_super_class, jobject typed_array_super_class) { using namespace startop::dex; - static auto in_memory_classloader = (jclass)env->NewGlobalRef(env->FindClass( "dalvik/system/InMemoryDexClassLoader")); + static auto in_memory_classloader = (jclass) env->NewGlobalRef( + env->FindClass("dalvik/system/InMemoryDexClassLoader")); static jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); DexBuilder dex_file; @@ -120,17 +120,19 @@ namespace lspd { auto current_thread = art::Thread::Current(); ClassBuilder xresource_builder{ dex_file.MakeClass("xposed.dummy.XResourcesSuperClass")}; - xresource_builder.setSuperClass(TypeDescriptor::FromDescriptor(art::mirror::Class(current_thread.DecodeJObject(resource_super_class)).GetDescriptor(&storage))); + xresource_builder.setSuperClass(TypeDescriptor::FromDescriptor(art::mirror::Class( + current_thread.DecodeJObject(resource_super_class)).GetDescriptor(&storage))); ClassBuilder xtypearray_builder{ dex_file.MakeClass("xposed.dummy.XTypedArraySuperClass")}; - xtypearray_builder.setSuperClass(TypeDescriptor::FromDescriptor(art::mirror::Class(current_thread.DecodeJObject(typed_array_super_class)).GetDescriptor(&storage))); + xtypearray_builder.setSuperClass(TypeDescriptor::FromDescriptor(art::mirror::Class( + current_thread.DecodeJObject(typed_array_super_class)).GetDescriptor(&storage))); slicer::MemView image{dex_file.CreateImage()}; - auto dex_buffer = env->NewDirectByteBuffer(const_cast(image.ptr()), image.size()); + auto dex_buffer = env->NewDirectByteBuffer(const_cast(image.ptr()), image.size()); return JNI_NewObject(env, in_memory_classloader, initMid, - dex_buffer, parent); + dex_buffer, parent); } LSP_DEF_NATIVE_METHOD(void, ResourcesHook, rewriteXmlReferencesNative, @@ -161,15 +163,13 @@ namespace lspd { // 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); + auto attrName = mTree.mStrings.stringAt(attrNameID); jint attrResID = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateAttrId, env->NewString( - (const jchar *) attrName, - attNameLen), origRes); + (const jchar *) attrName.data_, + attrName.length_), + origRes); if (env->ExceptionCheck()) goto leave; @@ -209,8 +209,10 @@ namespace lspd { static JNINativeMethod gMethods[] = { LSP_NATIVE_METHOD(ResourcesHook, initXResourcesNative, "()Z"), LSP_NATIVE_METHOD(ResourcesHook, removeFinalFlagNative, "(Ljava/lang/Class;)Z"), - LSP_NATIVE_METHOD(ResourcesHook, buildDummyClassLoader, "(Ljava/lang/ClassLoader;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/ClassLoader;"), - LSP_NATIVE_METHOD(ResourcesHook, rewriteXmlReferencesNative,"(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V") + LSP_NATIVE_METHOD(ResourcesHook, buildDummyClassLoader, + "(Ljava/lang/ClassLoader;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/ClassLoader;"), + LSP_NATIVE_METHOD(ResourcesHook, rewriteXmlReferencesNative, + "(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V") }; void RegisterResourcesHook(JNIEnv *env) {