From f38e991af6de0d94d6dd3bf380421faad3c040c0 Mon Sep 17 00:00:00 2001 From: kiwi-bo Date: Thu, 22 Aug 2019 15:43:49 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20dvm=E7=8E=AF=E5=A2=83=E5=85=BC?= =?UTF-8?q?=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加适配dvm环境的相关代码,并测试通过 --- .gitignore | 3 + whale/CMakeLists.txt | 20 +- whale/src/android/android_build.h | 22 +- whale/src/android/art/art_jni_trampoline.cc | 4 +- whale/src/android/art/art_method.cc | 2 +- whale/src/android/art/art_method.h | 4 +- whale/src/android/art/art_runtime.cc | 18 +- whale/src/android/art/art_runtime.h | 18 +- whale/src/android/art/art_symbol_resolver.h | 6 +- whale/src/android/dvm/dvm_hook_param.h | 28 ++ whale/src/android/dvm/dvm_jni_trampoline.cc | 261 +++++++++++ whale/src/android/dvm/dvm_jni_trampoline.h | 16 + whale/src/android/dvm/dvm_method.cc | 14 + whale/src/android/dvm/dvm_method.h | 46 ++ whale/src/android/dvm/dvm_runtime.cc | 436 ++++++++++++++++++ whale/src/android/dvm/dvm_runtime.h | 177 +++++++ whale/src/android/dvm/dvm_symbol_resolver.cc | 30 ++ whale/src/android/dvm/dvm_symbol_resolver.h | 36 ++ whale/src/android/{art => }/java_types.cc | 7 +- whale/src/android/{art => }/java_types.h | 4 +- whale/src/android/jni_helper.h | 21 +- whale/src/android/{art => }/modifiers.h | 6 +- whale/src/android/native_bridge.h | 4 +- whale/src/android/{art => }/native_on_load.cc | 90 +++- whale/src/android/{art => }/native_on_load.h | 2 +- .../{art => }/scoped_thread_state_change.cc | 48 +- .../{art => }/scoped_thread_state_change.h | 7 - .../android/{art => }/well_known_classes.cc | 17 +- .../android/{art => }/well_known_classes.h | 12 +- 29 files changed, 1250 insertions(+), 109 deletions(-) create mode 100644 whale/src/android/dvm/dvm_hook_param.h create mode 100644 whale/src/android/dvm/dvm_jni_trampoline.cc create mode 100644 whale/src/android/dvm/dvm_jni_trampoline.h create mode 100644 whale/src/android/dvm/dvm_method.cc create mode 100644 whale/src/android/dvm/dvm_method.h create mode 100644 whale/src/android/dvm/dvm_runtime.cc create mode 100644 whale/src/android/dvm/dvm_runtime.h create mode 100644 whale/src/android/dvm/dvm_symbol_resolver.cc create mode 100644 whale/src/android/dvm/dvm_symbol_resolver.h rename whale/src/android/{art => }/java_types.cc (87%) rename whale/src/android/{art => }/java_types.h (95%) rename whale/src/android/{art => }/modifiers.h (98%) rename whale/src/android/{art => }/native_on_load.cc (52%) rename whale/src/android/{art => }/native_on_load.h (87%) rename whale/src/android/{art => }/scoped_thread_state_change.cc (56%) rename whale/src/android/{art => }/scoped_thread_state_change.h (93%) rename whale/src/android/{art => }/well_known_classes.cc (95%) rename whale/src/android/{art => }/well_known_classes.h (79%) diff --git a/.gitignore b/.gitignore index 3b5b9b8..b3befe1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake +/node_modules +package.json +package-lock.json \ No newline at end of file diff --git a/whale/CMakeLists.txt b/whale/CMakeLists.txt index 91f7c5b..2b51c95 100644 --- a/whale/CMakeLists.txt +++ b/whale/CMakeLists.txt @@ -64,17 +64,27 @@ set(WHALE_DARWIN_SOURCES src/dbi/darwin/macho_import_hook.cc ) +set(WHALE_ANDROID_COMMON + src/android/native_on_load.cc + src/android/java_types.cc + src/android/well_known_classes.cc + src/android/scoped_thread_state_change.cc +) + set(WHALE_ANDROID_ART - src/android/art/native_on_load.cc src/android/art/art_runtime.cc src/android/art/art_symbol_resolver.cc - src/android/art/java_types.cc - src/android/art/well_known_classes.cc src/android/art/art_method.cc - src/android/art/scoped_thread_state_change.cc src/android/art/art_jni_trampoline.cc ) +set(WHALE_ANDROID_DVM + src/android/dvm/dvm_runtime.cc + src/android/dvm/dvm_symbol_resolver.cc + src/android/dvm/dvm_method.cc + src/android/dvm/dvm_jni_trampoline.cc + ) + set(WHALE_AARCH32 src/dbi/arm/decoder_arm.cc src/dbi/arm/decoder_thumb.cc @@ -151,7 +161,7 @@ endif () if (PLATFORM STREQUAL "Android") - set(WHALE_SOURCES ${WHALE_SOURCES} ${WHALE_ANDROID_ART}) + set(WHALE_SOURCES ${WHALE_SOURCES} ${WHALE_ANDROID_ART} ${WHALE_ANDROID_DVM} ${WHALE_ANDROID_COMMON}) if (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm") diff --git a/whale/src/android/android_build.h b/whale/src/android/android_build.h index 5d20d6a..3d2afee 100644 --- a/whale/src/android/android_build.h +++ b/whale/src/android/android_build.h @@ -4,6 +4,9 @@ #include #include #include +#include +#include "base/logging.h" + #define ANDROID_ICE_CREAM_SANDWICH 14 #define ANDROID_ICE_CREAM_SANDWICH_MR1 15 @@ -21,10 +24,27 @@ #define ANDROID_O_MR1 27 #define ANDROID_P 28 + + +extern bool g_isArt; + static inline int32_t GetAndroidApiLevel() { - char prop_value[PROP_VALUE_MAX]; + char prop_value[PROP_VALUE_MAX] = {0x0}; __system_property_get("ro.build.version.sdk", prop_value); return atoi(prop_value); } +static inline bool isArt(JNIEnv *env) { + jclass System = env->FindClass("java/lang/System"); + jmethodID System_getProperty = env->GetStaticMethodID(System, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"); + + jstring vm_version_name = env->NewStringUTF("java.vm.version"); + jstring vm_version_value = (jstring)(env->CallStaticObjectMethod(System, System_getProperty, vm_version_name)); + + char *cvm_version_value = (char *)env->GetStringUTFChars(vm_version_value, NULL); + LOG(ERROR)<<"[ArtRuntime::OnLoad] cvm_version_value=" << cvm_version_value; + double version = atof(cvm_version_value); + g_isArt = version >= 2 ? true : false; + return g_isArt; +} #endif // WHALE_ANDROID_ANDROID_BUILD_H_ diff --git a/whale/src/android/art/art_jni_trampoline.cc b/whale/src/android/art/art_jni_trampoline.cc index f0f1a82..1c7056c 100644 --- a/whale/src/android/art/art_jni_trampoline.cc +++ b/whale/src/android/art/art_jni_trampoline.cc @@ -1,8 +1,8 @@ #include #include #include "android/art/art_jni_trampoline.h" -#include "android/art/java_types.h" -#include "android/art/well_known_classes.h" +#include "android/java_types.h" +#include "android/well_known_classes.h" #include "android/art/art_runtime.h" #include "platform/memory.h" #include "base/macros.h" diff --git a/whale/src/android/art/art_method.cc b/whale/src/android/art/art_method.cc index df18aae..8da18e6 100644 --- a/whale/src/android/art/art_method.cc +++ b/whale/src/android/art/art_method.cc @@ -1,5 +1,5 @@ #include "android/art/art_method.h" -#include "well_known_classes.h" +#include "android/well_known_classes.h" namespace whale { namespace art { diff --git a/whale/src/android/art/art_method.h b/whale/src/android/art/art_method.h index 6ceadbc..8276473 100644 --- a/whale/src/android/art/art_method.h +++ b/whale/src/android/art/art_method.h @@ -4,7 +4,7 @@ #include #include #include "android/art/art_runtime.h" -#include "android/art/modifiers.h" +#include "android/modifiers.h" #include "base/cxx_helper.h" #include "base/primitive_types.h" @@ -135,7 +135,7 @@ class ArtMethod final { jmethodID jni_method_; // Convenient for quick invocation ArtMethodOffsets *offset_; - ResolvedSymbols *symbols_; + ArtResolvedSymbols *symbols_; }; diff --git a/whale/src/android/art/art_runtime.cc b/whale/src/android/art/art_runtime.cc index 424550f..c36e9e6 100644 --- a/whale/src/android/art/art_runtime.cc +++ b/whale/src/android/art/art_runtime.cc @@ -2,14 +2,14 @@ #include "whale.h" #include "android/android_build.h" #include "android/art/art_runtime.h" -#include "android/art/modifiers.h" -#include "android/art/native_on_load.h" +#include "android/modifiers.h" +#include "android/native_on_load.h" #include "android/art/art_method.h" #include "android/art/art_symbol_resolver.h" -#include "android/art/scoped_thread_state_change.h" +#include "android/scoped_thread_state_change.h" #include "android/art/art_jni_trampoline.h" -#include "android/art/well_known_classes.h" -#include "android/art/java_types.h" +#include "android/well_known_classes.h" +#include "android/java_types.h" #include "platform/memory.h" #include "base/logging.h" #include "base/singleton.h" @@ -36,6 +36,7 @@ bool ArtRuntime::OnLoad(JavaVM *vm, JNIEnv *env, jclass java_class) { LOG(ERROR) << "Failed to find " #field "."; \ return false; \ } + LOG(ERROR)<<"[ArtRuntime::OnLoad] start call ArtRuntime::OnLoad"; if ((kRuntimeISA == InstructionSet::kArm || kRuntimeISA == InstructionSet::kArm64) && IsFileInMemory("libhoudini.so")) { @@ -60,6 +61,7 @@ bool ArtRuntime::OnLoad(JavaVM *vm, JNIEnv *env, jclass java_class) { LOG(ERROR) << "Unable to read data from libart.so."; return false; } + LOG(ERROR)<<"[ArtRuntime::OnLoad] start call art_symbol_resolver_.Resolve"; if (!art_symbol_resolver_.Resolve(art_elf_image_, api_level_)) { // The log will all output from ArtSymbolResolver. return false; @@ -210,7 +212,7 @@ ArtRuntime::HookMethod(JNIEnv *env, jclass decl_class, jobject hooked_java_metho param->origin_access_flags = hooked_method.GetAccessFlags(); jobject origin_java_method = hooked_method.Clone(env, param->origin_access_flags); - ResolvedSymbols *symbols = GetSymbols(); + ArtResolvedSymbols *symbols = GetSymbols(); if (symbols->ProfileSaver_ForceProcessProfiles) { symbols->ProfileSaver_ForceProcessProfiles(); } @@ -374,7 +376,7 @@ void ArtRuntime::SetObjectClassUnsafe(JNIEnv *env, jobject obj, jclass cl) { } jobject ArtRuntime::CloneToSubclass(JNIEnv *env, jobject obj, jclass sub_class) { - ResolvedSymbols *symbols = GetSymbols(); + ArtResolvedSymbols *symbols = GetSymbols(); ArtThread *thread = GetCurrentArtThread(); ptr_t art_object = symbols->Thread_DecodeJObject(thread, obj); ptr_t art_clone_object = CloneArtObject(art_object); @@ -461,7 +463,7 @@ ALWAYS_INLINE bool ArtRuntime::EnforceDisableHiddenAPIPolicyImpl() { } ptr_t ArtRuntime::CloneArtObject(ptr_t art_object) { - ResolvedSymbols *symbols = GetSymbols(); + ArtResolvedSymbols *symbols = GetSymbols(); if (symbols->Object_Clone) { return symbols->Object_Clone(art_object, GetCurrentArtThread()); } diff --git a/whale/src/android/art/art_runtime.h b/whale/src/android/art/art_runtime.h index 55f4773..bc621b1 100644 --- a/whale/src/android/art/art_runtime.h +++ b/whale/src/android/art/art_runtime.h @@ -13,20 +13,6 @@ #include "base/macros.h" #include "base/primitive_types.h" -#if defined(__LP64__) -static constexpr const char *kAndroidLibDir = "/system/lib64/"; -static constexpr const char *kLibNativeBridgePath = "/system/lib64/libnativebridge.so"; -static constexpr const char *kLibArtPath = "/system/lib64/libart.so"; -static constexpr const char *kLibAocPath = "/system/lib64/libaoc.so"; -static constexpr const char *kLibHoudiniArtPath = "/system/lib64/arm64/libart.so"; -#else -static constexpr const char *kAndroidLibDir = "/system/lib/"; -static constexpr const char *kLibArtPath = "/system/lib/libart.so"; -static constexpr const char *kLibAocPath = "/system/lib/libaoc.so"; -static constexpr const char *kLibHoudiniArtPath = "/system/lib/arm/libart.so"; -#endif - - namespace whale { namespace art { @@ -60,6 +46,8 @@ class ArtRuntime final { public: friend class ArtMethod; + bool isArt = false; + static ArtRuntime *Get(); ArtRuntime() {} @@ -89,7 +77,7 @@ class ArtRuntime final { return &class_linker_objects_; } - ResolvedSymbols *GetSymbols() { + ArtResolvedSymbols *GetSymbols() { return art_symbol_resolver_.GetSymbols(); } diff --git a/whale/src/android/art/art_symbol_resolver.h b/whale/src/android/art/art_symbol_resolver.h index da8a860..360dbd6 100644 --- a/whale/src/android/art/art_symbol_resolver.h +++ b/whale/src/android/art/art_symbol_resolver.h @@ -8,7 +8,7 @@ namespace whale { namespace art { -struct ResolvedSymbols { +struct ArtResolvedSymbols { const char *(*Art_GetMethodShorty)(JNIEnv *, jmethodID); void (*Dbg_SuspendVM)(); @@ -40,12 +40,12 @@ class ArtSymbolResolver { bool Resolve(void *elf_image, s4 api_level); - ResolvedSymbols *GetSymbols() { + ArtResolvedSymbols *GetSymbols() { return &symbols_; }; private: - ResolvedSymbols symbols_; + ArtResolvedSymbols symbols_; }; } // namespace art diff --git a/whale/src/android/dvm/dvm_hook_param.h b/whale/src/android/dvm/dvm_hook_param.h new file mode 100644 index 0000000..9e6cce0 --- /dev/null +++ b/whale/src/android/dvm/dvm_hook_param.h @@ -0,0 +1,28 @@ +#ifndef WHALE_ANDROID_DVM_INTERCEPT_PARAM_H_ +#define WHALE_ANDROID_DVM_INTERCEPT_PARAM_H_ + +#include +#include "base/primitive_types.h" +#include "ffi_cxx.h" + +namespace whale { +namespace dvm { + +struct DvmHookParam final { + bool is_static_; //标志位,标志函数是否为静态函数 + const char *shorty_; //函数描述符缩写 + jobject addition_info_; + u4 origin_access_flags; + jobject origin_method_; + jobject hooked_method_; + volatile ptr_t decl_class_; + jobject class_Loader_; // + jmethodID hooked_native_method_; + jmethodID origin_native_method_; + FFIClosure *jni_closure_; +}; + +} // namespace dvm +} // namespace whale + +#endif // WHALE_ANDROID_DVM_INTERCEPT_PARAM_H_ diff --git a/whale/src/android/dvm/dvm_jni_trampoline.cc b/whale/src/android/dvm/dvm_jni_trampoline.cc new file mode 100644 index 0000000..58cf722 --- /dev/null +++ b/whale/src/android/dvm/dvm_jni_trampoline.cc @@ -0,0 +1,261 @@ +#include +#include +#include "android/dvm/dvm_jni_trampoline.h" +#include "android/java_types.h" +#include "android/well_known_classes.h" +#include "android/dvm/dvm_runtime.h" +#include "platform/memory.h" +#include "base/macros.h" +#include "ffi_cxx.h" + +namespace whale { +namespace dvm { + +static void UnBoxValue(JNIEnv *env, jvalue *jv, jobject obj, char type) { + if (obj == nullptr) { + jv->l = obj; + return; + } + switch (type) { + case 'I': + jv->i = Types::FromInteger(env, obj); + break; + case 'Z': + jv->z = Types::FromBoolean(env, obj); + break; + case 'J': + jv->j = Types::FromLong(env, obj); + break; + case 'F': + jv->f = Types::FromFloat(env, obj); + break; + case 'B': + jv->b = Types::FromByte(env, obj); + break; + case 'D': + jv->d = Types::FromDouble(env, obj); + break; + case 'S': + jv->s = Types::FromShort(env, obj); + break; + case 'C': + jv->c = Types::FromCharacter(env, obj); + break; + default: + jv->l = obj; + } +} + +template +static RetType +InvokeJavaBridge(JNIEnv *env, DvmHookParam *param, jobject this_object, + jobjectArray arguments) { + jobject ret = DvmRuntime::Get()->InvokeHookedMethodBridge( + env, + param, + this_object, + arguments + ); + jvalue val; + UnBoxValue(env, &val, ret, param->shorty_[0]); + return ForceCast(val); +} + +static void InvokeVoidJavaBridge(JNIEnv *env, DvmHookParam *param, jobject this_object, + jobjectArray arguments) { + DvmRuntime::Get()->InvokeHookedMethodBridge( + env, + param, + this_object, + arguments + ); +} + +class QuickArgumentBuilder { + public: + QuickArgumentBuilder(JNIEnv *env, size_t len) : env_(env), index_(0) { + array_ = env->NewObjectArray( + static_cast(len), + WellKnownClasses::java_lang_Object, + nullptr + ); + } + +#define APPEND_DEF(name, type) \ + void Append##name(type value) { \ + env_->SetObjectArrayElement(array_, index_++, \ + Types::To##name(env_, value)); \ + } \ + + + APPEND_DEF(Integer, jint) + + APPEND_DEF(Boolean, jboolean) + + APPEND_DEF(Byte, jbyte) + + APPEND_DEF(Character, jchar) + + APPEND_DEF(Short, jshort) + + APPEND_DEF(Float, jfloat) + + APPEND_DEF(Double, jdouble) + + APPEND_DEF(Long, jlong) + + APPEND_DEF(Object, jobject) + + +#undef APPEND_DEF + + jobjectArray GetArray() { + return array_; + } + + private: + JNIEnv *env_; + jobjectArray array_; + int index_; +}; + +FFIType FFIGetJniParameter(char shorty) { + switch (shorty) { + case 'Z': + return FFIType::kFFITypeU1; + case 'B': + return FFIType::kFFITypeS1; + case 'C': + return FFIType::kFFITypeU2; + case 'S': + return FFIType::kFFITypeS2; + case 'I': + return FFIType::kFFITypeS4; + case 'J': + return FFIType::kFFITypeS8; + case 'F': + return FFIType::kFFITypeFloat; + case 'D': + return FFIType::kFFITypeDouble; + case 'L': + return FFIType::kFFITypePointer; + case 'V': + return FFIType::kFFITypeVoid; + default: + LOG(FATAL) << "unhandled shorty type: " << shorty; + UNREACHABLE(); + } +} + +void FFIJniDispatcher(FFIClosure *closure, void *resp, void **args, void *userdata) { +#define FFI_ARG(name, type) \ + builder.Append##name(*reinterpret_cast(args[i])); + + DvmHookParam *param = reinterpret_cast(userdata); + const char *argument = param->shorty_ + 1; + unsigned int argument_len = (unsigned int) strlen(argument); + JNIEnv *env = *reinterpret_cast(args[0]); + jobject this_object = nullptr; + if (!param->is_static_) { + this_object = *reinterpret_cast(args[1]); + } + // skip first two arguments + args += 2; + QuickArgumentBuilder builder(env, argument_len); + + for (int i = 0; i < argument_len; ++i) { + switch (argument[i]) { + case 'Z': + FFI_ARG(Boolean, jboolean); + break; + case 'B': + FFI_ARG(Byte, jbyte); + break; + case 'C': + FFI_ARG(Character, jchar); + break; + case 'S': + FFI_ARG(Short, jshort); + break; + case 'I': + FFI_ARG(Integer, jint); + break; + case 'J': + FFI_ARG(Long, jlong); + break; + case 'F': + FFI_ARG(Float, jfloat); + break; + case 'D': + FFI_ARG(Double, jdouble); + break; + case 'L': + FFI_ARG(Object, jobject); + break; + default: + LOG(FATAL) << "unhandled shorty type: " << argument[i]; + UNREACHABLE(); + } + } +#define INVOKE(type) \ + *reinterpret_cast(resp) = InvokeJavaBridge(env, param, this_object, \ + builder.GetArray()); + + switch (param->shorty_[0]) { + case 'Z': + INVOKE(jboolean); + break; + case 'B': + INVOKE(jbyte); + break; + case 'C': + INVOKE(jchar); + break; + case 'S': + INVOKE(jshort); + break; + case 'I': + INVOKE(jint); + break; + case 'J': + INVOKE(jlong); + break; + case 'F': + INVOKE(jfloat); + break; + case 'D': + INVOKE(jdouble); + break; + case 'L': + INVOKE(jobject); + break; + case 'V': + InvokeVoidJavaBridge(env, param, this_object, builder.GetArray()); + break; + default: + LOG(FATAL) << "unhandled shorty type: " << param->shorty_[0]; + UNREACHABLE(); + } +#undef INVOKE +#undef FFI_ARG +} + + +void BuildJniClosure(DvmHookParam *param) { + const char *argument = param->shorty_ + 1; + unsigned int java_argument_len = (unsigned int) strlen(argument); + unsigned int jni_argument_len = java_argument_len + 2; + FFICallInterface *cif = new FFICallInterface( + FFIGetJniParameter(param->shorty_[0]) + ); + cif->Parameter(FFIType::kFFITypePointer); // JNIEnv * + cif->Parameter(FFIType::kFFITypePointer); // jclass or jobject + for (int i = 2; i < jni_argument_len; ++i) { + cif->Parameter(FFIGetJniParameter(argument[i - 2])); + } + cif->FinalizeCif(); + param->jni_closure_ = cif->CreateClosure(param, FFIJniDispatcher); +} + +} // namespace dvm +} // namespace whale diff --git a/whale/src/android/dvm/dvm_jni_trampoline.h b/whale/src/android/dvm/dvm_jni_trampoline.h new file mode 100644 index 0000000..d64d47b --- /dev/null +++ b/whale/src/android/dvm/dvm_jni_trampoline.h @@ -0,0 +1,16 @@ +#ifndef WHALE_ANDROID_DVM_JNI_TRAMPOLINE_H_ +#define WHALE_ANDROID_DVM_JNI_TRAMPOLINE_H_ + +#include "android/dvm/dvm_hook_param.h" + +namespace whale { +namespace dvm { + + +void BuildJniClosure(DvmHookParam *param); + + +} // namespace dvm +} // namespace whale + +#endif // WHALE_ANDROID_DVM_JNI_TRAMPOLINE_H_ diff --git a/whale/src/android/dvm/dvm_method.cc b/whale/src/android/dvm/dvm_method.cc new file mode 100644 index 0000000..ef14ccb --- /dev/null +++ b/whale/src/android/dvm/dvm_method.cc @@ -0,0 +1,14 @@ +#include "android/dvm/dvm_method.h" +#include "android/well_known_classes.h" + +namespace whale { +namespace dvm { + +jmethodID DvmMethod::Clone(JNIEnv *env, u4 access_flags) { + jmethodID jni_clone_method = reinterpret_cast(malloc(sizeof(Method))); + memcpy(jni_clone_method, jni_method_, sizeof(Method)); + hex_dump(jni_clone_method, sizeof(Method)); + return jni_clone_method; +} +} // namespace dvm +} // namespace whale diff --git a/whale/src/android/dvm/dvm_method.h b/whale/src/android/dvm/dvm_method.h new file mode 100644 index 0000000..9c83b31 --- /dev/null +++ b/whale/src/android/dvm/dvm_method.h @@ -0,0 +1,46 @@ +#ifndef WHALE_ANDROID_DVM_DVM_METHOD_H_ +#define WHALE_ANDROID_DVM_DVM_METHOD_H_ + +#include +#include +#include "android/dvm/dvm_runtime.h" +#include "android/modifiers.h" +#include "base/cxx_helper.h" +#include "base/primitive_types.h" + +namespace whale { +namespace dvm { + +class DvmMethod final { + public: + explicit DvmMethod(jmethodID method) : jni_method_(method) { + DvmRuntime *runtime = DvmRuntime::Get(); + offset_ = runtime->GetDvmMethodOffsets(); + symbols_ = runtime->GetSymbols(); + } + + jmethodID getJmethod(){ + return jni_method_; + } + + /* + * Notice: This is a GcRoot reference. + */ + ptr_t GetDeclaringClass() { + return MemberOf(jni_method_, 0); + } + + + jmethodID Clone(JNIEnv *env, u4 access_flags); + + private: + jmethodID jni_method_; + DvmMethodOffsets *offset_; + DvmResolvedSymbols *symbols_; +}; + + +} // namespace dvm +} // namespace whale + +#endif // WHALE_ANDROID_DVM_DVM_METHOD_H_ diff --git a/whale/src/android/dvm/dvm_runtime.cc b/whale/src/android/dvm/dvm_runtime.cc new file mode 100644 index 0000000..7f3dc24 --- /dev/null +++ b/whale/src/android/dvm/dvm_runtime.cc @@ -0,0 +1,436 @@ +#include +#include "whale.h" +#include "android/android_build.h" +#include "android/dvm/dvm_runtime.h" +#include "android/modifiers.h" +#include "android/native_on_load.h" +#include "android/dvm/dvm_method.h" +#include "android/dvm/dvm_symbol_resolver.h" +#include "android/scoped_thread_state_change.h" +#include "android/dvm/dvm_jni_trampoline.h" +#include "android/well_known_classes.h" +#include "android/java_types.h" +#include "platform/memory.h" +#include "base/logging.h" +#include "base/singleton.h" +#include "base/cxx_helper.h" + +namespace whale { +namespace dvm { + + DvmRuntime *DvmRuntime::Get() { + static DvmRuntime instance; + return &instance; + } + + + + + +void PreLoadRequiredStuff(JNIEnv *env) { + Types::Load(env); + WellKnownClasses::Load(env); + ScopedNoGCDaemons::Load(env); +} + + +u4 dvmPlatformInvokeHints(const char *shorty) +{ + const char* sig = shorty; + int padFlags, jniHints; + char sigByte; + int stackOffset, padMask; + + stackOffset = padFlags = 0; + padMask = 0x00000001; + + /* Skip past the return type */ + sig++; + + while (true) { + sigByte = *(sig++); + + if (sigByte == '\0') + break; + + if (sigByte == 'D' || sigByte == 'J') { + if ((stackOffset & 1) != 0) { + padFlags |= padMask; + stackOffset++; + padMask <<= 1; + } + stackOffset += 2; + padMask <<= 2; + } else { + stackOffset++; + padMask <<= 1; + } + } + + jniHints = 0; + + if (stackOffset > DALVIK_JNI_COUNT_SHIFT) { + /* too big for "fast" version */ + jniHints = DALVIK_JNI_NO_ARG_INFO; + } else { + assert((padFlags & (0xffffffff << DALVIK_JNI_COUNT_SHIFT)) == 0); + stackOffset -= 2; // r2/r3 holds first two items + if (stackOffset < 0) + stackOffset = 0; + jniHints |= ((stackOffset+1) / 2) << DALVIK_JNI_COUNT_SHIFT; + jniHints |= padFlags; + } + + return jniHints; +} + + +static int computeJniArgInfo(const char* shorty) +{ + int returnType, jniArgInfo; + u4 hints; + + /* The first shorty character is the return type. */ + switch (*(shorty)) { + case 'V': + returnType = DALVIK_JNI_RETURN_VOID; + break; + case 'F': + returnType = DALVIK_JNI_RETURN_FLOAT; + break; + case 'D': + returnType = DALVIK_JNI_RETURN_DOUBLE; + break; + case 'J': + returnType = DALVIK_JNI_RETURN_S8; + break; + case 'Z': + case 'B': + returnType = DALVIK_JNI_RETURN_S1; + break; + case 'C': + returnType = DALVIK_JNI_RETURN_U2; + break; + case 'S': + returnType = DALVIK_JNI_RETURN_S2; + break; + default: + returnType = DALVIK_JNI_RETURN_S4; + break; + } + + jniArgInfo = returnType << DALVIK_JNI_RETURN_SHIFT; + + hints = dvmPlatformInvokeHints(shorty); + + if (hints & DALVIK_JNI_NO_ARG_INFO) { + jniArgInfo |= DALVIK_JNI_NO_ARG_INFO; + jniArgInfo |= hints; + } + + return jniArgInfo; +} + + +bool DvmRuntime::OnLoad(JavaVM *vm, JNIEnv *env, jclass java_class) { +#define CHECK_FIELD(field, value) \ + if ((field) == (value)) { \ + LOG(ERROR) << "Failed to find " #field "."; \ + return false; \ + } + + if ((kRuntimeISA == InstructionSet::kArm + || kRuntimeISA == InstructionSet::kArm64) + && IsFileInMemory("libhoudini.so")) { + LOG(INFO) << '[' << getpid() << ']' << " Unable to launch on houdini environment."; + return false; + } + vm_ = vm; + java_class_ = reinterpret_cast(env->NewGlobalRef(java_class)); + bridge_method_ = env->GetStaticMethodID( + java_class, + "handleHookedMethod", + "(Ljava/lang/reflect/Member;JLjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;" + ); + + if (JNIExceptionCheck(env)) { + return false; + } + api_level_ = GetAndroidApiLevel(); + PreLoadRequiredStuff(env); + const char *dvm_path = kLibDvmPath; + + dvm_elf_image_ = WDynamicLibOpen(dvm_path); + if (dvm_elf_image_ == nullptr) { + LOG(ERROR) << "Unable to read data from libdvm.so."; + return false; + } + if (!dvm_symbol_resolver_.Resolve(dvm_elf_image_, api_level_)) { + // The log will all output from DvmSymbolResolver. + return false; + } + + jmethodID reserved0 = env->GetStaticMethodID(java_class, kMethodReserved0, "()V"); + Method* reserved0_ = (Method *)reserved0; + jniBridge_ = reserved0_->nativeFunc; + + + pthread_mutex_init(&mutex, nullptr); + EnforceDisableHiddenAPIPolicy(); + return true; +#undef CHECK_OFFSET +} + + +/* +*decl_class 被hook函数所在类 +*hooked_java_method 被hook函数 +*/ +jlong +DvmRuntime::HookMethod(JNIEnv *env, jclass decl_class, jobject hooked_java_method, + jobject addition_info) { + ScopedSuspendAll suspend_all; + jmethodID hooked_jni_method = env->FromReflectedMethod(hooked_java_method);//从java的反射函数映射成JNI的jmethodID结构 + DvmMethod hooked_method(hooked_jni_method); + Method* hooked_jni_ = (Method *)hooked_jni_method; + + auto *param = new DvmHookParam(); + param->shorty_ = hooked_jni_->shorty; + param->is_static_ = ((hooked_jni_->accessFlags & kAccStatic) != 0); + param->origin_access_flags = hooked_jni_->accessFlags; + jmethodID origin_java_method = hooked_method.Clone(env, param->origin_access_flags);//复制原始函数的对象 + + u4 access_flags = hooked_jni_->accessFlags; + access_flags |= kAccNative; + hooked_jni_->accessFlags = access_flags; + hooked_jni_->jniArgInfo = computeJniArgInfo(param->shorty_);//设置这个属性以后,返回值才是正确的 + + param->origin_native_method_ = origin_java_method;//被hook函数的克隆对象生成的jmethodID + param->hooked_native_method_ = hooked_jni_method;//被hook函数的原始引用生成的jmethodID + param->addition_info_ = env->NewGlobalRef(addition_info); + param->hooked_method_ = env->NewGlobalRef(hooked_java_method);//被hook函数原始引用 + + hex_dump(hooked_jni_, sizeof(Method)); + printMethodInfo((Method *)hooked_method.getJmethod()); + + BuildJniClosure(param); + + hooked_jni_->insns = (u2 *)param->jni_closure_->GetCode();//dvm环境下,需要将nativeFunc地址赋值给dexCodeItem + hooked_jni_->nativeFunc = jniBridge_;//jni函数的入口是一个固定值,从JNI_Onload获取初始值,稍后再赋值 + hooked_jni_->registersSize = hooked_jni_->insSize; + hooked_jni_->outsSize = 0x0; + hooked_jni_->noRef = true; + param->decl_class_ = hooked_method.GetDeclaringClass(); + hooked_method_map_.insert(std::make_pair(hooked_jni_method, param)); + hex_dump(hooked_jni_, sizeof(Method)); + printMethodInfo((Method *)hooked_jni_); + return reinterpret_cast(param); +} + +jobject +DvmRuntime::InvokeOriginalMethod(jlong slot, jobject this_object, jobjectArray args) { + JNIEnv *env = GetJniEnv(); + auto *param = reinterpret_cast(slot); + if (slot <= 0) { + env->ThrowNew( + WellKnownClasses::java_lang_IllegalArgumentException, + "Failed to resolve slot." + ); + return nullptr; + } + + void * tmp = malloc(sizeof(Method)); + memcpy(tmp, param->hooked_native_method_, sizeof(Method)); + memcpy(param->hooked_native_method_, param->origin_native_method_, sizeof(Method)); + printMethodInfo((Method *)param->hooked_native_method_); + jobject ret = env->CallNonvirtualObjectMethod( + param->hooked_method_, + WellKnownClasses::java_lang_reflect_Method, + WellKnownClasses::java_lang_reflect_Method_invoke, + this_object, + args + ); + memcpy(param->hooked_native_method_, tmp, sizeof(Method)); + printMethodInfo((Method *)param->hooked_native_method_); + return ret; +} + +#if defined(__aarch64__) +# define __get_tls() ({ void** __val; __asm__("mrs %0, tpidr_el0" : "=r"(__val)); __val; }) +#elif defined(__arm__) +# define __get_tls() ({ void** __val; __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__val)); __val; }) +#elif defined(__i386__) +# define __get_tls() ({ void** __val; __asm__("movl %%gs:0, %0" : "=r"(__val)); __val; }) +#elif defined(__x86_64__) +# define __get_tls() ({ void** __val; __asm__("mov %%fs:0, %0" : "=r"(__val)); __val; }) +#else +#error unsupported architecture +#endif + +DvmThread *DvmRuntime::GetCurrentDvmThread() { + if (WellKnownClasses::java_lang_Thread_nativePeer) { + JNIEnv *env = GetJniEnv(); + jobject current = env->CallStaticObjectMethod( + WellKnownClasses::java_lang_Thread, + WellKnownClasses::java_lang_Thread_currentThread + ); + return reinterpret_cast( + env->GetLongField(current, WellKnownClasses::java_lang_Thread_nativePeer) + ); + } + return reinterpret_cast(__get_tls()[7/*TLS_SLOT_ART_THREAD_SELF*/]); +} + +jobject +DvmRuntime::InvokeHookedMethodBridge(JNIEnv *env, DvmHookParam *param, jobject receiver, + jobjectArray array) { + return env->CallStaticObjectMethod(java_class_, bridge_method_, + param->hooked_method_, reinterpret_cast(param), + param->addition_info_, receiver, array); +} + +jlong DvmRuntime::GetMethodSlot(JNIEnv *env, jclass cl, jobject method_obj) { + if (method_obj == nullptr) { + env->ThrowNew( + WellKnownClasses::java_lang_IllegalArgumentException, + "Method param == null" + ); + return 0; + } + jmethodID jni_method = env->FromReflectedMethod(method_obj); + auto entry = hooked_method_map_.find(jni_method); + if (entry == hooked_method_map_.end()) { + env->ThrowNew( + WellKnownClasses::java_lang_IllegalArgumentException, + "Failed to find slot." + ); + return 0; + } + return reinterpret_cast(entry->second); +} + +void DvmRuntime::EnsureClassInitialized(JNIEnv *env, jclass cl) { + // This invocation will ensure the target class has been initialized also. + ScopedLocalRef unused(env, env->AllocObject(cl)); + JNIExceptionClear(env); +} + +void DvmRuntime::SetObjectClass(JNIEnv *env, jobject obj, jclass cl) { + SetObjectClassUnsafe(env, obj, cl); +} + +void DvmRuntime::SetObjectClassUnsafe(JNIEnv *env, jobject obj, jclass cl) { + jfieldID java_lang_Class_shadow$_klass_ = env->GetFieldID( + WellKnownClasses::java_lang_Object, + "shadow$_klass_", + "Ljava/lang/Class;" + ); + env->SetObjectField(obj, java_lang_Class_shadow$_klass_, cl); +} + +jobject DvmRuntime::CloneToSubclass(JNIEnv *env, jobject obj, jclass sub_class) { + DvmResolvedSymbols *symbols = GetSymbols(); + DvmThread *thread = GetCurrentDvmThread(); + return nullptr; +} + +void DvmRuntime::RemoveFinalFlag(JNIEnv *env, jclass java_class) { + jfieldID java_lang_Class_accessFlags = env->GetFieldID( + WellKnownClasses::java_lang_Class, + "accessFlags", + "I" + ); + jint access_flags = env->GetIntField(java_class, java_lang_Class_accessFlags); + env->SetIntField(java_class, java_lang_Class_accessFlags, access_flags & ~kAccFinal); +} + +bool DvmRuntime::EnforceDisableHiddenAPIPolicy() { + if (GetAndroidApiLevel() < ANDROID_O_MR1) { + return true; + } + static Singleton enforced([&](bool *result) { + *result = EnforceDisableHiddenAPIPolicyImpl(); + }); + return enforced.Get(); +} + +bool OnInvokeHiddenAPI() { + return false; +} + +/** + * NOTICE: + * After Android Q(10.0), GetMemberActionImpl has been renamed to ShouldDenyAccessToMemberImpl, + * But we don't know the symbols until it's published. + */ +ALWAYS_INLINE bool DvmRuntime::EnforceDisableHiddenAPIPolicyImpl() { + JNIEnv *env = GetJniEnv(); + jfieldID java_lang_Class_shadow$_klass_ = env->GetFieldID( + WellKnownClasses::java_lang_Object, + "shadow$_klass_", + "Ljava/lang/Class;" + ); + JNIExceptionClear(env); + if (java_lang_Class_shadow$_klass_ != nullptr) { + return true; + } + void *symbol = nullptr; + + // Android P : Preview 1 ~ 4 version + symbol = WDynamicLibSymbol( + dvm_elf_image_, + "_ZN3dvm9hiddenapi25ShouldBlockAccessToMemberINS_8DvmFieldEEEbPT_PNS_6ThreadENSt3__18functionIFbS6_EEENS0_12AccessMethodE" + ); + if (symbol) { + WInlineHookFunction(symbol, reinterpret_cast(OnInvokeHiddenAPI), nullptr); + } + symbol = WDynamicLibSymbol( + dvm_elf_image_, + "_ZN3dvm9hiddenapi25ShouldBlockAccessToMemberINS_9DvmMethodEEEbPT_PNS_6ThreadENSt3__18functionIFbS6_EEENS0_12AccessMethodE" + ); + + if (symbol) { + WInlineHookFunction(symbol, reinterpret_cast(OnInvokeHiddenAPI), nullptr); + return true; + } + // Android P : Release version + symbol = WDynamicLibSymbol( + dvm_elf_image_, + "_ZN3dvm9hiddenapi6detail19GetMemberActionImplINS_8DvmFieldEEENS0_6ActionEPT_NS_20HiddenApiAccessFlags7ApiListES4_NS0_12AccessMethodE" + ); + if (symbol) { + WInlineHookFunction(symbol, reinterpret_cast(OnInvokeHiddenAPI), nullptr); + } + symbol = WDynamicLibSymbol( + dvm_elf_image_, + "_ZN3dvm9hiddenapi6detail19GetMemberActionImplINS_9DvmMethodEEENS0_6ActionEPT_NS_20HiddenApiAccessFlags7ApiListES4_NS0_12AccessMethodE" + ); + if (symbol) { + WInlineHookFunction(symbol, reinterpret_cast(OnInvokeHiddenAPI), nullptr); + } + return symbol != nullptr; +} + + +int (*old_ToDexPc)(void *thiz, void *a2, unsigned int a3, int a4); +int new_ToDexPc(void *thiz, void *a2, unsigned int a3, int a4) { + return old_ToDexPc(thiz, a2, a3, 0); +} + +bool is_hooked = false; +void DvmRuntime::FixBugN() { + if (is_hooked) + return; + void *symbol = nullptr; + symbol = WDynamicLibSymbol( + dvm_elf_image_, + "_ZNK3dvm20OatQuickMethodHeader7ToDexPcEPNS_9DvmMethodEjb" + ); + if (symbol) { + WInlineHookFunction(symbol, reinterpret_cast(new_ToDexPc), reinterpret_cast(&old_ToDexPc)); + } + is_hooked = true; +} + +} // namespace dvm +} // namespace whale diff --git a/whale/src/android/dvm/dvm_runtime.h b/whale/src/android/dvm/dvm_runtime.h new file mode 100644 index 0000000..52866d6 --- /dev/null +++ b/whale/src/android/dvm/dvm_runtime.h @@ -0,0 +1,177 @@ +#ifndef WHALE_ANDROID_DVM_INTERCEPTOR_H_ +#define WHALE_ANDROID_DVM_INTERCEPTOR_H_ + +#include +#include +#include "platform/linux/elf_image.h" +#include "platform/linux/process_map.h" +#include "dbi/instruction_set.h" +#include "android/dvm/dvm_symbol_resolver.h" +#include "android/dvm/dvm_hook_param.h" +#include "android/native_bridge.h" +#include "android/jni_helper.h" +#include "base/macros.h" +#include "base/primitive_types.h" + +namespace whale { +namespace dvm { + +enum DalvikJniReturnType { + DALVIK_JNI_RETURN_VOID = 0, /* must be zero */ + DALVIK_JNI_RETURN_FLOAT = 1, + DALVIK_JNI_RETURN_DOUBLE = 2, + DALVIK_JNI_RETURN_S8 = 3, + DALVIK_JNI_RETURN_S4 = 4, + DALVIK_JNI_RETURN_S2 = 5, + DALVIK_JNI_RETURN_U2 = 6, + DALVIK_JNI_RETURN_S1 = 7 +}; + +#define DALVIK_JNI_NO_ARG_INFO 0x80000000 +#define DALVIK_JNI_RETURN_MASK 0x70000000 +#define DALVIK_JNI_RETURN_SHIFT 28 +#define DALVIK_JNI_COUNT_MASK 0x0f000000 +#define DALVIK_JNI_COUNT_SHIFT 24 + + + +class DvmThread; + +struct Method { + void* clazz; + u4 accessFlags; + u2 methodIndex; + u2 registersSize; /* ins + locals */ + u2 outsSize; + u2 insSize; + const char* name; + u8 prototype; + const char* shorty; + const u2* insns; + int jniArgInfo; + void* nativeFunc; + bool fastJni; + bool noRef; + bool shouldTrace; + const void* registerMap; + bool inProfile; +} __attribute__((packed)); + + +struct DvmMethodOffsets final { + size_t method_size_; + offset_t jni_code_offset_;//jni入口函数的偏移地址 + offset_t access_flags_offset_;//访问符号标志位 + offset_t shorty_offset_;//函数简短描述符偏移地址 + offset_t dex_code_item_offset_offset_;//bytecode偏移地址 dvm虚拟机,为什么将jni的入口设置在这里 + offset_t jniArgInfo_offset_;//jniArgInfo的偏移地址 + offset_t noRef_offset_; + offset_t dex_method_index_offset_; + offset_t method_index_offset_; +}; + +struct RuntimeObjects final { + ptr_t OPTION runtime_; + ptr_t OPTION heap_; + ptr_t OPTION thread_list_; + ptr_t OPTION class_linker_; + ptr_t OPTION intern_table_; +}; + +struct ClassLinkerObjects { + ptr_t quick_generic_jni_trampoline_; +}; + +class DvmRuntime final { + public: + friend class DvmMethod; + + bool isArt = false; + + static DvmRuntime *Get(); + + DvmRuntime() {} + + bool OnLoad(JavaVM *vm, JNIEnv *env, jclass java_class); + + jlong HookMethod(JNIEnv *env, jclass decl_class, jobject hooked_java_method, + jobject addition_info); + + JNIEnv *GetJniEnv() { + JNIEnv *env = nullptr; + jint ret = vm_->AttachCurrentThread(&env, nullptr); + DCHECK_EQ(JNI_OK, ret); + return env; + } + + DvmMethodOffsets *GetDvmMethodOffsets() { + return &method_offset_; + } + + + RuntimeObjects *GetRuntimeObjects() { + return &runtime_objects_; + } + + DvmResolvedSymbols *GetSymbols() { + return dvm_symbol_resolver_.GetSymbols(); + } + + DvmThread *GetCurrentDvmThread(); + + void EnsureClassInitialized(JNIEnv *env, jclass cl); + + + jobject + InvokeHookedMethodBridge(JNIEnv *env, DvmHookParam *param, jobject receiver, + jobjectArray array); + + jobject + InvokeOriginalMethod(jlong slot, jobject this_object, jobjectArray args); + + jlong GetMethodSlot(JNIEnv *env, jclass cl, jobject method_obj); + + ALWAYS_INLINE void VisitInterceptParams(std::function visitor) { + for (auto &entry : hooked_method_map_) { + visitor(entry.second); + } + } + + void SetObjectClass(JNIEnv *env, jobject obj, jclass cl); + + void SetObjectClassUnsafe(JNIEnv *env, jobject obj, jclass cl); + + jobject CloneToSubclass(JNIEnv *env, jobject obj, jclass sub_class); + + void RemoveFinalFlag(JNIEnv *env, jclass java_class); + + bool EnforceDisableHiddenAPIPolicy(); + + ptr_t CloneDvmObject(ptr_t art_object); + + void FixBugN(); + + private: + void *jniBridge_; + JavaVM *vm_; + jclass java_class_; + jmethodID bridge_method_; + s4 api_level_; + void *dvm_elf_image_; + NativeBridgeCallbacks OPTION *android_bridge_callbacks_; + DvmSymbolResolver dvm_symbol_resolver_; + RuntimeObjects runtime_objects_; + DvmMethodOffsets method_offset_; + std::map hooked_method_map_; + pthread_mutex_t mutex; + + bool EnforceDisableHiddenAPIPolicyImpl(); + + DISALLOW_COPY_AND_ASSIGN(DvmRuntime); +}; + + +} // namespace dvm +} // namespace whale + +#endif // WHALE_ANDROID_DVM_INTERCEPTOR_H_ diff --git a/whale/src/android/dvm/dvm_symbol_resolver.cc b/whale/src/android/dvm/dvm_symbol_resolver.cc new file mode 100644 index 0000000..f5e3fdd --- /dev/null +++ b/whale/src/android/dvm/dvm_symbol_resolver.cc @@ -0,0 +1,30 @@ +#include "whale.h" +#include "android/dvm/dvm_symbol_resolver.h" +#include "android/android_build.h" +#include "android/dvm/dvm_runtime.h" + +#define SYMBOL static constexpr const char * + +namespace whale { +namespace dvm { +// dvmDbgSuspendVM(bool isEvent) +SYMBOL kDbg_SuspendVM = "_Z15dvmDbgSuspendVMb"; +// dvmDbgResumeVM() +SYMBOL kDbg_ResumeVM = "_Z14dvmDbgResumeVMv"; +bool DvmSymbolResolver::Resolve(void *elf_image, s4 api_level) { +#define FIND_SYMBOL(symbol, decl, ret) \ + if ((decl = reinterpret_cast(WDynamicLibSymbol(elf_image, symbol))) == nullptr) { \ + if (ret) { \ + LOG(ERROR) << "Failed to resolve symbol : " << #symbol; \ + return false; \ + } \ + } + + FIND_SYMBOL(kDbg_SuspendVM, symbols_.Dbg_SuspendVM, false); + FIND_SYMBOL(kDbg_ResumeVM, symbols_.Dbg_ResumeVM, false); + return true; +#undef FIND_SYMBOL +} + +} // namespace dvm +} // namespace whale diff --git a/whale/src/android/dvm/dvm_symbol_resolver.h b/whale/src/android/dvm/dvm_symbol_resolver.h new file mode 100644 index 0000000..5d47370 --- /dev/null +++ b/whale/src/android/dvm/dvm_symbol_resolver.h @@ -0,0 +1,36 @@ +#ifndef WHALE_ANDROID_DVM_SYMBOL_RESOLVER_H_ +#define WHALE_ANDROID_DVM_SYMBOL_RESOLVER_H_ + +#include +#include "platform/linux/elf_image.h" +#include "base/primitive_types.h" + +namespace whale { +namespace dvm { + +struct DvmResolvedSymbols { + + void (*Dbg_SuspendVM)(bool isEvent); + + void (*Dbg_ResumeVM)(); + +}; + +class DvmSymbolResolver { + public: + DvmSymbolResolver() = default; + + bool Resolve(void *elf_image, s4 api_level); + + DvmResolvedSymbols *GetSymbols() { + return &symbols_; + }; + + private: + DvmResolvedSymbols symbols_; +}; + +} // namespace dvm +} // namespace whale + +#endif // WHALE_ANDROID_DVM_SYMBOL_RESOLVER_H_ diff --git a/whale/src/android/art/java_types.cc b/whale/src/android/java_types.cc similarity index 87% rename from whale/src/android/art/java_types.cc rename to whale/src/android/java_types.cc index 9d83d4b..81b9b00 100644 --- a/whale/src/android/art/java_types.cc +++ b/whale/src/android/java_types.cc @@ -1,7 +1,6 @@ -#include "android/art/java_types.h" +#include "android/java_types.h" namespace whale { -namespace art { #define EXPORT_LANG_ClASS(c) jclass Types::java_lang_##c; jmethodID Types::java_lang_##c##_init; jmethodID Types::java_value_##c; @@ -18,7 +17,8 @@ EXPORT_LANG_ClASS(Character); void Types::Load(JNIEnv *env) { jclass clazz; - env->PushLocalFrame(16); + env->PushLocalFrame(16);//这使得PushLocalFrame函数可以释放其使用的框架中所有已分配的本地引用。当该函数被调用时, + //本地引用的最低数量将在本框架中被创建。该函数如果执行成功则返回0,如果由于错误抛出一个OutOfMemoryException,则返回一个负值。 #define LOAD_CLASS(c, s) clazz = env->FindClass(s); c = reinterpret_cast(env->NewWeakGlobalRef(clazz)) #define LOAD_LANG_CLASS(c, s) LOAD_CLASS(java_lang_##c, "java/lang/" #c); java_lang_##c##_init = env->GetMethodID(java_lang_##c, "", s) @@ -95,5 +95,4 @@ jobject Types::FromObject(JNIEnv *env, jobject obj) { #undef LANG_UNBOX_V #undef LANG_UNBOX -} // namespace art } // namespace whale diff --git a/whale/src/android/art/java_types.h b/whale/src/android/java_types.h similarity index 95% rename from whale/src/android/art/java_types.h rename to whale/src/android/java_types.h index 830a58f..f68482d 100644 --- a/whale/src/android/art/java_types.h +++ b/whale/src/android/java_types.h @@ -2,11 +2,10 @@ #define WHALE_ANDROID_ART_JAVA_TYPES_H_ #include -#include "android/art/art_runtime.h" + namespace whale { -namespace art { struct Types { #define LANG_ClASS(c) static jclass java_lang_##c; static jmethodID java_lang_##c##_init; static jmethodID java_value_##c; @@ -55,7 +54,6 @@ struct Types { #undef LANG_UNBOX_DEF }; -} // namespace art } // namespace whale #endif // WHALE_ANDROID_ART_JAVA_TYPES_H_ diff --git a/whale/src/android/jni_helper.h b/whale/src/android/jni_helper.h index b2c6545..b06f082 100644 --- a/whale/src/android/jni_helper.h +++ b/whale/src/android/jni_helper.h @@ -4,6 +4,25 @@ #include #include +namespace whale { +#if defined(__LP64__) +static constexpr const char *kAndroidLibDir = "/system/lib64/"; +static constexpr const char *kLibNativeBridgePath = "/system/lib64/libnativebridge.so"; +static constexpr const char *kLibArtPath = "/system/lib64/libart.so"; +static constexpr const char *kLibDvmPath = "/system/lib/libdvm.so"; +static constexpr const char *kLibAocPath = "/system/lib64/libaoc.so"; +static constexpr const char *kLibHoudiniArtPath = "/system/lib64/arm64/libart.so"; +static constexpr const char *kLibHoudiniDvmPath = "/system/lib/arm/libdvm.so"; +#else +static constexpr const char *kAndroidLibDir = "/system/lib/"; +static constexpr const char *kLibArtPath = "/system/lib/libart.so"; +static constexpr const char *kLibDvmPath = "/system/lib/libdvm.so"; +static constexpr const char *kLibAocPath = "/system/lib/libaoc.so"; +static constexpr const char *kLibHoudiniArtPath = "/system/lib/arm/libart.so"; +static constexpr const char *kLibHoudiniDvmPath = "/system/lib/arm/libdvm.so"; +#endif + + #define NATIVE_METHOD(className, functionName, signature) \ { #functionName, signature, reinterpret_cast(className ## _ ## functionName) } @@ -69,5 +88,5 @@ class ScopedLocalRef { DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); }; - +} #endif // WHALE_ANDROID_ART_JNI_HELPER_H_ diff --git a/whale/src/android/art/modifiers.h b/whale/src/android/modifiers.h similarity index 98% rename from whale/src/android/art/modifiers.h rename to whale/src/android/modifiers.h index 8320104..bad8993 100644 --- a/whale/src/android/art/modifiers.h +++ b/whale/src/android/modifiers.h @@ -4,8 +4,6 @@ #include "base/primitive_types.h" namespace whale { -namespace art { - static constexpr u4 kAccPublic = 0x0001; // class, field, method, ic static constexpr u4 kAccPrivate = 0x0002; // field, method, ic static constexpr u4 kAccProtected = 0x0004; // field, method, ic @@ -52,7 +50,5 @@ static constexpr uint32_t kAccDirectFlags = kAccStatic | kAccPrivate | kAccConst static constexpr uint32_t kAccPublicApi = 0x10000000; // field, method static constexpr uint32_t kAccHiddenapiBits = 0x30000000; // field, method -} // namespace art -} // namespace whale - +} // namespace whale #endif // WHALE_ANDROID_ART_MODIFIERS_H_ diff --git a/whale/src/android/native_bridge.h b/whale/src/android/native_bridge.h index 385de37..4dfa320 100644 --- a/whale/src/android/native_bridge.h +++ b/whale/src/android/native_bridge.h @@ -4,7 +4,7 @@ #include #include #include - +namespace whale { struct NativeBridgeRuntimeCallbacks; struct NativeBridgeRuntimeValues; @@ -126,6 +126,6 @@ struct NativeBridgeCallbacks { // Otherwise, a pointer to the signal handler. NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal); }; - +} // namespace whale #endif // WHALE_ANDROID_NATIVE_BRIDGE_H_ diff --git a/whale/src/android/art/native_on_load.cc b/whale/src/android/native_on_load.cc similarity index 52% rename from whale/src/android/art/native_on_load.cc rename to whale/src/android/native_on_load.cc index f587d55..3acf7c9 100644 --- a/whale/src/android/art/native_on_load.cc +++ b/whale/src/android/native_on_load.cc @@ -1,5 +1,7 @@ #include "android/jni_helper.h" #include "android/art/art_runtime.h" +#include "android/dvm/dvm_runtime.h" +#include "android/android_build.h" #include "base/logging.h" #define CLASS_NAME "com/lody/whale/WhaleRuntime" @@ -9,6 +11,8 @@ #endif +bool g_isArt = false; + extern "C" OPEN_API void WhaleRuntime_reserved0(JNI_START) {} extern "C" OPEN_API void WhaleRuntime_reserved1(JNI_START) {} @@ -16,44 +20,86 @@ extern "C" OPEN_API void WhaleRuntime_reserved1(JNI_START) {} static jlong WhaleRuntime_hookMethodNative(JNI_START, jclass decl_class, jobject method_obj, jobject addition_info) { - auto runtime = whale::art::ArtRuntime::Get(); - return runtime->HookMethod(env, decl_class, method_obj, addition_info); + if(g_isArt) + { + auto runtime = whale::art::ArtRuntime::Get(); + return runtime->HookMethod(env, decl_class, method_obj, addition_info); + }else{ + auto runtime = whale::dvm::DvmRuntime::Get(); + return runtime->HookMethod(env, decl_class, method_obj, addition_info); + } + } static jobject WhaleRuntime_invokeOriginalMethodNative(JNI_START, jlong slot, jobject this_object, jobjectArray args) { - auto runtime = whale::art::ArtRuntime::Get(); - return runtime->InvokeOriginalMethod(slot, this_object, args); + if(g_isArt){ + auto runtime = whale::art::ArtRuntime::Get(); + return runtime->InvokeOriginalMethod(slot, this_object, args); + }else{ + auto runtime = whale::dvm::DvmRuntime::Get(); + return runtime->InvokeOriginalMethod(slot, this_object, args); + } } static jlong WhaleRuntime_getMethodSlot(JNI_START, jclass decl_class, jobject method_obj) { - auto runtime = whale::art::ArtRuntime::Get(); - return runtime->GetMethodSlot(env, decl_class, method_obj); + if(g_isArt){ + auto runtime = whale::art::ArtRuntime::Get(); + return runtime->GetMethodSlot(env, decl_class, method_obj); + }else{ + auto runtime = whale::dvm::DvmRuntime::Get(); + return runtime->GetMethodSlot(env, decl_class, method_obj); + } } static void WhaleRuntime_setObjectClassNative(JNI_START, jobject obj, jclass parent_class) { - auto runtime = whale::art::ArtRuntime::Get(); - return runtime->SetObjectClass(env, obj, parent_class); + if(g_isArt){ + auto runtime = whale::art::ArtRuntime::Get(); + return runtime->SetObjectClass(env, obj, parent_class); + }else{ + auto runtime = whale::dvm::DvmRuntime::Get(); + return runtime->SetObjectClass(env, obj, parent_class); + } + } static jobject WhaleRuntime_cloneToSubclassNative(JNI_START, jobject obj, jclass sub_class) { - auto runtime = whale::art::ArtRuntime::Get(); - return runtime->CloneToSubclass(env, obj, sub_class); + if(g_isArt){ + auto runtime = whale::art::ArtRuntime::Get(); + return runtime->CloneToSubclass(env, obj, sub_class); + }else{ + auto runtime = whale::dvm::DvmRuntime::Get(); + return runtime->CloneToSubclass(env, obj, sub_class); + } + } static void WhaleRuntime_removeFinalFlagNative(JNI_START, jclass java_class) { + if(g_isArt){ + auto runtime = whale::art::ArtRuntime::Get(); + runtime->RemoveFinalFlag(env, java_class); + }else{ + auto runtime = whale::dvm::DvmRuntime::Get(); + runtime->RemoveFinalFlag(env, java_class); + } + auto runtime = whale::art::ArtRuntime::Get(); runtime->RemoveFinalFlag(env, java_class); } void WhaleRuntime_enforceDisableHiddenAPIPolicy(JNI_START) { - auto runtime = whale::art::ArtRuntime::Get(); - runtime->EnforceDisableHiddenAPIPolicy(); + if(g_isArt){ + auto runtime = whale::art::ArtRuntime::Get(); + runtime->EnforceDisableHiddenAPIPolicy(); + }else{ + auto runtime = whale::dvm::DvmRuntime::Get(); + runtime->EnforceDisableHiddenAPIPolicy(); + } } @@ -87,10 +133,22 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { LOG(ERROR) << "RegisterNatives failed for " << CLASS_NAME; return JNI_ERR; } - auto runtime = whale::art::ArtRuntime::Get(); - if (!runtime->OnLoad(vm, env, cl)) { - LOG(ERROR) << "Runtime setup failed"; - return JNI_ERR; + bool cur_is_Art = isArt(env); + LOG(ERROR) << "cur_is_Art=" << cur_is_Art; + if(cur_is_Art){ + auto runtime = whale::art::ArtRuntime::Get(); + runtime->isArt = cur_is_Art; + if (!runtime->OnLoad(vm, env, cl)) { + LOG(ERROR) << "Runtime setup failed"; + return JNI_ERR; + } + }else{ + auto runtime = whale::dvm::DvmRuntime::Get(); + runtime->isArt = cur_is_Art; + if (!runtime->OnLoad(vm, env, cl)) { + LOG(ERROR) << "Runtime setup failed"; + return JNI_ERR; + } } return JNI_VERSION_1_4; } diff --git a/whale/src/android/art/native_on_load.h b/whale/src/android/native_on_load.h similarity index 87% rename from whale/src/android/art/native_on_load.h rename to whale/src/android/native_on_load.h index 4b2a453..70a300a 100644 --- a/whale/src/android/art/native_on_load.h +++ b/whale/src/android/native_on_load.h @@ -11,7 +11,7 @@ constexpr const char *kMethodReserved1 = "reserved1"; */ extern "C" { -void WhaleRuntime_reserved0(JNIEnv *env, jclass cl); +void WhaleRuntime_reserved0(JNIEnv *env, jclass cl, jint a, jint b); void WhaleRuntime_reserved1(JNIEnv *env, jclass cl); diff --git a/whale/src/android/art/scoped_thread_state_change.cc b/whale/src/android/scoped_thread_state_change.cc similarity index 56% rename from whale/src/android/art/scoped_thread_state_change.cc rename to whale/src/android/scoped_thread_state_change.cc index 32a502a..913c2a0 100644 --- a/whale/src/android/art/scoped_thread_state_change.cc +++ b/whale/src/android/scoped_thread_state_change.cc @@ -1,10 +1,11 @@ #include "android/jni_helper.h" #include "android/android_build.h" -#include "android/art/scoped_thread_state_change.h" -#include "art_runtime.h" +#include "android/scoped_thread_state_change.h" +#include "android/art/art_runtime.h" +#include "android/dvm/dvm_runtime.h" + namespace whale { -namespace art { static volatile long kNoGCDaemonsGuard = 0; @@ -33,6 +34,7 @@ void ScopedNoGCDaemons::Load(JNIEnv *env) { } java_lang_Daemons_stop = env->GetStaticMethodID(java_lang_Daemons, "stop", "()V"); JNIExceptionClear(env); + LOG(ERROR) << "[ScopedNoGCDaemons::Load] ScopedNoGCDaemons::Load over"; } ScopedNoGCDaemons::ScopedNoGCDaemons(JNIEnv *env) : env_(env) { @@ -55,22 +57,40 @@ ScopedNoGCDaemons::~ScopedNoGCDaemons() { } ScopedSuspendAll::ScopedSuspendAll() { - ResolvedSymbols *symbols = art::ArtRuntime::Get()->GetSymbols(); - if (symbols->Dbg_SuspendVM && symbols->Dbg_ResumeVM) { - symbols->Dbg_SuspendVM(); - } else { - LOG(WARNING) << "Suspend VM API is unavailable."; + if(g_isArt){ + LOG(ERROR) << "[ScopedSuspendAll::ScopedSuspendAll] isArt"; + whale::art::ArtResolvedSymbols *symbols = whale::art::ArtRuntime::Get()->GetSymbols(); + if (symbols->Dbg_SuspendVM && symbols->Dbg_ResumeVM) { + symbols->Dbg_SuspendVM(); + } else { + LOG(WARNING) << "Suspend art VM API is unavailable."; + } + }else{ + LOG(ERROR) << "[ScopedSuspendAll::ScopedSuspendAll] isDvm"; + whale::dvm::DvmResolvedSymbols *symbols = whale::dvm::DvmRuntime::Get()->GetSymbols(); + if (symbols->Dbg_SuspendVM && symbols->Dbg_ResumeVM) { + symbols->Dbg_SuspendVM(false); + } else { + LOG(WARNING) << "Suspend dvm VM API is unavailable."; + } } + } ScopedSuspendAll::~ScopedSuspendAll() { - ResolvedSymbols *symbols = art::ArtRuntime::Get()->GetSymbols(); - if (symbols->Dbg_SuspendVM && symbols->Dbg_ResumeVM) { - symbols->Dbg_ResumeVM(); + if(g_isArt){ + LOG(ERROR) << "[ScopedSuspendAll::~ScopedSuspendAll] m_isArt"; + whale::art::ArtResolvedSymbols *art_symbols = whale::art::ArtRuntime::Get()->GetSymbols(); + if (art_symbols->Dbg_SuspendVM && art_symbols->Dbg_ResumeVM) { + art_symbols->Dbg_ResumeVM(); + } + }else{ + LOG(ERROR) << "[ScopedSuspendAll::~ScopedSuspendAll] m_isDvm"; + whale::dvm::DvmResolvedSymbols *dvm_symbols = whale::dvm::DvmRuntime::Get()->GetSymbols(); + if (dvm_symbols->Dbg_SuspendVM && dvm_symbols->Dbg_ResumeVM) { + dvm_symbols->Dbg_ResumeVM(); + } } } - - -} // namespace art } // namespace whale diff --git a/whale/src/android/art/scoped_thread_state_change.h b/whale/src/android/scoped_thread_state_change.h similarity index 93% rename from whale/src/android/art/scoped_thread_state_change.h rename to whale/src/android/scoped_thread_state_change.h index 9fe2bc1..e26b2b0 100644 --- a/whale/src/android/art/scoped_thread_state_change.h +++ b/whale/src/android/scoped_thread_state_change.h @@ -2,10 +2,7 @@ #define WHALE_ANDROID_ART__SCOPED_THREAD_STATE_CHANGE_H_ #include - namespace whale { -namespace art { - class ScopedNoGCDaemons { static jclass java_lang_Daemons; static jmethodID java_lang_Daemons_start; @@ -28,10 +25,6 @@ class ScopedSuspendAll { ~ScopedSuspendAll(); }; - - -} // namespace art } // namespace whale - #endif // WHALE_ANDROID_ART__SCOPED_THREAD_STATE_CHANGE_H_ diff --git a/whale/src/android/art/well_known_classes.cc b/whale/src/android/well_known_classes.cc similarity index 95% rename from whale/src/android/art/well_known_classes.cc rename to whale/src/android/well_known_classes.cc index 7b4ffc6..7b43291 100644 --- a/whale/src/android/art/well_known_classes.cc +++ b/whale/src/android/well_known_classes.cc @@ -1,11 +1,9 @@ -#include "android/art/well_known_classes.h" +#include "android/well_known_classes.h" #include "android/jni_helper.h" #include "base/logging.h" +#include "android_build.h" #include "base/stringprintf.h" - namespace whale { -namespace art { - jclass WellKnownClasses::java_lang_Object; jclass WellKnownClasses::java_lang_reflect_Method; jclass WellKnownClasses::java_lang_Class; @@ -92,10 +90,9 @@ void WellKnownClasses::Load(JNIEnv *env) { false, "setAccessible", "(Z)V"); - - java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J", true); + if(isArt(env)) + { + java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J", true); + } } - - -} // namespace art -} // namespace whale +} // namespace whale \ No newline at end of file diff --git a/whale/src/android/art/well_known_classes.h b/whale/src/android/well_known_classes.h similarity index 79% rename from whale/src/android/art/well_known_classes.h rename to whale/src/android/well_known_classes.h index 14a1729..43001bc 100644 --- a/whale/src/android/art/well_known_classes.h +++ b/whale/src/android/well_known_classes.h @@ -1,11 +1,8 @@ -#ifndef WHALE_ANDROID_ART_WELL_KNOWN_CLASSES_H_ -#define WHALE_ANDROID_ART_WELL_KNOWN_CLASSES_H_ +#ifndef WHALE_ANDROID_WELL_KNOWN_CLASSES_H_ +#define WHALE_ANDROID_WELL_KNOWN_CLASSES_H_ #include - namespace whale { -namespace art { - struct WellKnownClasses { static void Load(JNIEnv *env); @@ -24,8 +21,5 @@ struct WellKnownClasses { static jfieldID java_lang_Thread_nativePeer; }; - -} // namespace art } // namespace whale - -#endif // WHALE_ANDROID_ART_WELL_KNOWN_CLASSES_H_ +#endif // WHALE_ANDROID_WELL_KNOWN_CLASSES_H_ From f588a1ab111e7c117f3c807108b9ceb7cb4c8657 Mon Sep 17 00:00:00 2001 From: kiwi-bo Date: Thu, 22 Aug 2019 17:57:55 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(dvm=E5=85=BC=E5=AE=B9=E6=80=A7bug?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D):=20=E5=87=BD=E6=95=B0=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E4=B8=AD=E5=8C=85=E5=90=AB=E5=AF=B9=E8=B1=A1=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E5=B0=86noRef=E8=AE=BE=E7=BD=AE=E4=B8=BAfals?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- whale/src/android/dvm/dvm_runtime.cc | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/whale/src/android/dvm/dvm_runtime.cc b/whale/src/android/dvm/dvm_runtime.cc index 7f3dc24..cb4a720 100644 --- a/whale/src/android/dvm/dvm_runtime.cc +++ b/whale/src/android/dvm/dvm_runtime.cc @@ -219,7 +219,23 @@ DvmRuntime::HookMethod(JNIEnv *env, jclass decl_class, jobject hooked_java_metho hooked_jni_->nativeFunc = jniBridge_;//jni函数的入口是一个固定值,从JNI_Onload获取初始值,稍后再赋值 hooked_jni_->registersSize = hooked_jni_->insSize; hooked_jni_->outsSize = 0x0; - hooked_jni_->noRef = true; + + /* + * JNI: true if this method has no reference arguments. This lets the JNI + * bridge avoid scanning the shorty for direct pointers that need to be + * converted to local references. + * + * TODO: replace this with a list of indexes of the reference arguments. + */ + hooked_jni_->noRef = true; + const char* cp = hooked_jni_->shorty; + while (*++cp != '\0') { // Pre-increment to skip return type. + if (*cp == 'L') { + hooked_jni_->noRef = false; + break; + } + } + param->decl_class_ = hooked_method.GetDeclaringClass(); hooked_method_map_.insert(std::make_pair(hooked_jni_method, param)); hex_dump(hooked_jni_, sizeof(Method));