Android 5.x support (#7)

This commit is contained in:
LoveSy 2022-03-12 16:10:29 +08:00 committed by GitHub
parent c97c87b815
commit 6d40254a3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 199 additions and 64 deletions

View File

@ -53,6 +53,18 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- api-level: 21
target: default
arch: x86_64
- api-level: 21
target: default
arch: x86
- api-level: 22
target: default
arch: x86_64
- api-level: 22
target: default
arch: x86
- api-level: 23 - api-level: 23
target: default target: default
arch: x86_64 arch: x86_64

View File

@ -1,7 +1,7 @@
# LSPlant # LSPlant
![](https://img.shields.io/badge/license-LGPL--3.0-orange.svg) ![](https://img.shields.io/badge/license-LGPL--3.0-orange.svg)
![](https://img.shields.io/badge/Android-6.0%20--%2013-blue.svg) ![](https://img.shields.io/badge/Android-5.0%20--%2013-blue.svg)
![](https://img.shields.io/badge/arch-armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86--64-brightgreen.svg) ![](https://img.shields.io/badge/arch-armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86--64-brightgreen.svg)
![](https://github.com/LSPosed/LSPlant/actions/workflows/build.yml/badge.svg?branch=master&event=push) ![](https://github.com/LSPosed/LSPlant/actions/workflows/build.yml/badge.svg?branch=master&event=push)
![](https://img.shields.io/maven-central/v/org.lsposed.lsplant/lsplant.svg) ![](https://img.shields.io/maven-central/v/org.lsposed.lsplant/lsplant.svg)
@ -12,7 +12,7 @@ This project is part of LSPosed framework under GNU Lesser General Public Licens
## Features ## Features
+ Support Android 6.0 - 13 (API level 23 - 33) + Support Android 5.0 - 13 (API level 21 - 33)
+ Support armeabi-v7a, arm64-v8a, x86, x86-64 + Support armeabi-v7a, arm64-v8a, x86, x86-64
+ Support customized inline hook framework and ART symbol resolver + Support customized inline hook framework and ART symbol resolver

View File

@ -1,5 +1,5 @@
val androidTargetSdkVersion by extra(32) val androidTargetSdkVersion by extra(32)
val androidMinSdkVersion by extra(23) val androidMinSdkVersion by extra(21)
val androidBuildToolsVersion by extra("32.0.0") val androidBuildToolsVersion by extra("32.0.0")
val androidCompileSdkVersion by extra(32) val androidCompileSdkVersion by extra(32)
val androidNdkVersion by extra("23.1.7779620") val androidNdkVersion by extra("23.1.7779620")

View File

@ -13,6 +13,7 @@ Cpp11BracedListStyle: true
Standard: Latest Standard: Latest
# IndentAccessModifiers: false # IndentAccessModifiers: false
IndentCaseLabels: false IndentCaseLabels: false
BreakStringLiterals: false
IndentExternBlock: false IndentExternBlock: false
AccessModifierOffset: -4 AccessModifierOffset: -4
# EmptyLineBeforeAccessModifier: true # EmptyLineBeforeAccessModifier: true

View File

@ -35,10 +35,25 @@ class Instrumentation {
backup(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code); backup(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code);
} }
}); });
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art15instrumentation15Instrumentation17UpdateMethodsCodeEPNS_6mirror9ArtMethodEPKvS6_b",
void, UpdateMethodsCodeWithProtableCode,
(Instrumentation * thiz, ArtMethod *art_method, const void *quick_code,
const void *portable_code, bool have_portable_code),
{
backup(thiz, MaybeUseBackupMethod(art_method, quick_code), quick_code, portable_code,
have_portable_code);
});
public: public:
static bool Init(const HookHandler &handler) { static bool Init(const HookHandler &handler) {
int sdk_int = GetAndroidApiLevel(); int sdk_int = GetAndroidApiLevel();
if (sdk_int < __ANDROID_API_M__) [[unlikely]] {
if (!HookSyms(handler, UpdateMethodsCodeWithProtableCode)) {
return false;
}
return true;
}
if (!HookSyms(handler, UpdateMethodsCode)) { if (!HookSyms(handler, UpdateMethodsCode)) {
return false; return false;
} }

View File

@ -127,27 +127,35 @@ public:
} }
static art::ArtMethod *FromReflectedMethod(JNIEnv *env, jobject method) { static art::ArtMethod *FromReflectedMethod(JNIEnv *env, jobject method) {
return reinterpret_cast<art::ArtMethod *>(JNI_GetLongField(env, method, art_method_field)); if (art_method_field) [[likely]] {
return reinterpret_cast<art::ArtMethod *>(
JNI_GetLongField(env, method, art_method_field));
} else {
return reinterpret_cast<art::ArtMethod *>(env->FromReflectedMethod(method));
}
} }
static bool Init(JNIEnv *env, const HookHandler handler) { static bool Init(JNIEnv *env, const HookHandler handler) {
auto sdk_int = GetAndroidApiLevel(); auto sdk_int = GetAndroidApiLevel();
jclass executable = nullptr; ScopedLocalRef<jclass> executable{env, nullptr};
if (sdk_int >= __ANDROID_API_O__) { if (sdk_int >= __ANDROID_API_O__) {
executable = JNI_NewGlobalRef(env, JNI_FindClass(env, "java/lang/reflect/Executable")); executable = JNI_FindClass(env, "java/lang/reflect/Executable");
} else if (sdk_int >= __ANDROID_API_M__) {
executable = JNI_FindClass(env, "java/lang/reflect/AbstractMethod");
} else { } else {
executable = executable = JNI_FindClass(env, "java/lang/reflect/ArtMethod");
JNI_NewGlobalRef(env, JNI_FindClass(env, "java/lang/reflect/AbstractMethod"));
} }
if (!executable) { if (!executable) {
LOGE("Failed to found Executable/AbstractMethod"); LOGE("Failed to found Executable/AbstractMethod/ArtMethod");
return false; return false;
} }
if (art_method_field = JNI_GetFieldID(env, executable, "artMethod", "J"); if (sdk_int >= __ANDROID_API_M__) [[likely]] {
!art_method_field) { if (art_method_field = JNI_GetFieldID(env, executable, "artMethod", "J");
LOGE("Failed to find artMethod field"); !art_method_field) {
return false; LOGE("Failed to find artMethod field");
return false;
}
} }
auto throwable = JNI_FindClass(env, "java/lang/Throwable"); auto throwable = JNI_FindClass(env, "java/lang/Throwable");
@ -172,32 +180,59 @@ public:
art_method_size = reinterpret_cast<uintptr_t>(second) - reinterpret_cast<uintptr_t>(first); art_method_size = reinterpret_cast<uintptr_t>(second) - reinterpret_cast<uintptr_t>(first);
LOGD("ArtMethod size: %zu", art_method_size); LOGD("ArtMethod size: %zu", art_method_size);
if (RoundUpTo(4 * 9, kPointerSize) + kPointerSize * 3 < art_method_size) { if (RoundUpTo(4 * 9, kPointerSize) + kPointerSize * 3 < art_method_size) [[unlikely]] {
LOGW("ArtMethod size exceeds maximum assume. There may be something wrong."); if (sdk_int >= __ANDROID_API_M__) {
LOGW("ArtMethod size exceeds maximum assume. There may be something wrong.");
}
} }
entry_point_offset = art_method_size - kPointerSize; entry_point_offset = art_method_size - kPointerSize;
LOGD("ArtMethod::entrypoint offset: %zu", entry_point_offset);
data_offset = entry_point_offset - kPointerSize; data_offset = entry_point_offset - kPointerSize;
LOGD("ArtMethod::data offset: %zu", data_offset);
if (auto access_flags_field = JNI_GetFieldID(env, executable, "accessFlags", "I"); if (sdk_int >= __ANDROID_API_M__) [[likely]] {
access_flags_field) { if (auto access_flags_field = JNI_GetFieldID(env, executable, "accessFlags", "I");
uint32_t real_flags = JNI_GetIntField(env, first_ctor, access_flags_field); access_flags_field) {
for (size_t i = 0; i < art_method_size; i += sizeof(uint32_t)) { uint32_t real_flags = JNI_GetIntField(env, first_ctor, access_flags_field);
if (*reinterpret_cast<uint32_t *>(reinterpret_cast<uintptr_t>(first) + i) == for (size_t i = 0; i < art_method_size; i += sizeof(uint32_t)) {
real_flags) { if (*reinterpret_cast<uint32_t *>(reinterpret_cast<uintptr_t>(first) + i) ==
access_flags_offset = i; real_flags) {
LOGD("ArtMethod::access_flags offset: %zu", access_flags_offset); access_flags_offset = i;
break; break;
}
} }
} }
if (access_flags_offset == 0) {
LOGW("Failed to find accessFlags field. Fallback to 4.");
access_flags_offset = 4U;
}
} else {
auto art_field = JNI_FindClass(env, "java/lang/reflect/ArtField");
auto field = JNI_FindClass(env, "java/lang/reflect/Field");
auto art_field_field =
JNI_GetFieldID(env, field, "artField", "Ljava/lang/reflect/ArtField;");
auto field_offset = JNI_GetFieldID(env, art_field, "offset", "I");
auto get_offset_from_art_method = [&](const char *name, const char *sig) {
return JNI_GetIntField(
env,
JNI_GetObjectField(
env,
env->ToReflectedField(executable,
JNI_GetFieldID(env, executable, name, sig), false),
art_field_field),
field_offset);
};
access_flags_offset = get_offset_from_art_method("accessFlags", "I");
if (sdk_int == __ANDROID_API_L__) {
entry_point_offset =
get_offset_from_art_method("entryPointFromQuickCompiledCode", "J");
interpreter_entry_point_offset =
get_offset_from_art_method("entryPointFromInterpreter", "J");
data_offset = get_offset_from_art_method("entryPointFromJni", "J");
}
} }
if (access_flags_offset == 0) { LOGD("ArtMethod::entrypoint offset: %zu", entry_point_offset);
LOGW("Failed to find accessFlags field. Fallback to 4."); LOGD("ArtMethod::data offset: %zu", data_offset);
access_flags_offset = 4U; LOGD("ArtMethod::access_flags offset: %zu", access_flags_offset);
}
if (sdk_int < __ANDROID_API_R__) { if (sdk_int < __ANDROID_API_R__) {
kAccPreCompiled = 0; kAccPreCompiled = 0;
@ -207,14 +242,16 @@ public:
if (sdk_int < __ANDROID_API_Q__) kAccFastInterpreterToInterpreterInvoke = 0; if (sdk_int < __ANDROID_API_Q__) kAccFastInterpreterToInterpreterInvoke = 0;
if (!RETRIEVE_FUNC_SYMBOL(GetMethodShorty, if (!RETRIEVE_FUNC_SYMBOL(GetMethodShorty,
"_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID")) { "_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID") &&
!RETRIEVE_FUNC_SYMBOL(GetMethodShorty,
"_ZN3art15GetMethodShortyEP7_JNIEnvP10_jmethodID")) {
LOGE("Failed to find GetMethodShorty"); LOGE("Failed to find GetMethodShorty");
return false; return false;
} }
if (!RETRIEVE_FUNC_SYMBOL(PrettyMethod, "_ZN3art9ArtMethod12PrettyMethodEPS0_b")) { !RETRIEVE_FUNC_SYMBOL(PrettyMethod, "_ZN3art9ArtMethod12PrettyMethodEPS0_b") &&
RETRIEVE_FUNC_SYMBOL(PrettyMethod, "_ZN3art12PrettyMethodEPNS_9ArtMethodEb"); !RETRIEVE_FUNC_SYMBOL(PrettyMethod, "_ZN3art12PrettyMethodEPNS_9ArtMethodEb") &&
} !RETRIEVE_FUNC_SYMBOL(PrettyMethod, "_ZN3art12PrettyMethodEPNS_6mirror9ArtMethodEb");
if (sdk_int <= __ANDROID_API_O__) [[unlikely]] { if (sdk_int <= __ANDROID_API_O__) [[unlikely]] {
auto abstract_method_error = JNI_FindClass(env, "java/lang/AbstractMethodError"); auto abstract_method_error = JNI_FindClass(env, "java/lang/AbstractMethodError");
@ -246,12 +283,14 @@ public:
if (sdk_int <= __ANDROID_API_N__) { if (sdk_int <= __ANDROID_API_N__) {
kAccCompileDontBother = 0; kAccCompileDontBother = 0;
} }
if (sdk_int == __ANDROID_API_M__) [[unlikely]] { if (sdk_int <= __ANDROID_API_M__) [[unlikely]] {
if (!RETRIEVE_FUNC_SYMBOL(art_interpreter_to_compiled_code_bridge, if (!RETRIEVE_FUNC_SYMBOL(art_interpreter_to_compiled_code_bridge,
"artInterpreterToCompiledCodeBridge")) { "artInterpreterToCompiledCodeBridge")) {
return false; return false;
} }
interpreter_entry_point_offset = entry_point_offset - 2 * kPointerSize; if (sdk_int >= __ANDROID_API_L_MR1__) {
interpreter_entry_point_offset = entry_point_offset - 2 * kPointerSize;
}
} }
return true; return true;

View File

@ -2,6 +2,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector>
#include "common.hpp" #include "common.hpp"
@ -18,26 +19,74 @@ class DexFile {
return OpenMemorySym(dex_file, size, location, location_checksum, mem_map, oat_dex_file, return OpenMemorySym(dex_file, size, location, location_checksum, mem_map, oat_dex_file,
error_msg); error_msg);
} }
if (error_msg) *error_msg = "null sym";
return nullptr; return nullptr;
} }
public: CREATE_FUNC_SYMBOL_ENTRY(const DexFile*, OpenMemoryRaw, const uint8_t* dex_file, size_t size,
static std::unique_ptr<DexFile> OpenMemory(const void* dex_file, size_t size, const std::string& location, uint32_t location_checksum, void* mem_map,
std::string location, std::string* error_msg) { const void* oat_dex_file, std::string* error_msg) {
return OpenMemory(reinterpret_cast<const uint8_t*>(dex_file), size, location, if (OpenMemoryRawSym) [[likely]] {
reinterpret_cast<const Header*>(dex_file)->checksum_, nullptr, nullptr, return OpenMemoryRawSym(dex_file, size, location, location_checksum, mem_map,
error_msg); oat_dex_file, error_msg);
}
if (error_msg) *error_msg = "null sym";
return nullptr;
} }
jobject ToJavaDexFile(JNIEnv* env) { CREATE_FUNC_SYMBOL_ENTRY(const DexFile*, OpenMemoryWithoutOdex, const uint8_t* dex_file,
size_t size, const std::string& location, uint32_t location_checksum,
void* mem_map, std::string* error_msg) {
if (OpenMemoryWithoutOdexSym) [[likely]] {
return OpenMemoryWithoutOdexSym(dex_file, size, location, location_checksum, mem_map,
error_msg);
}
if (error_msg) *error_msg = "null sym";
return nullptr;
}
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, push_back, std::vector<const DexFile*>* thiz,
const DexFile** dex_file) {
push_backSym(thiz, dex_file);
}
public:
static const DexFile* OpenMemory(const void* dex_file, size_t size, std::string location,
std::string* error_msg) {
if (OpenMemorySym) [[likely]] {
return OpenMemory(reinterpret_cast<const uint8_t*>(dex_file), size, location,
reinterpret_cast<const Header*>(dex_file)->checksum_, nullptr,
nullptr, error_msg)
.release();
} else if (OpenMemoryRawSym) {
return OpenMemoryRaw(reinterpret_cast<const uint8_t*>(dex_file), size, location,
reinterpret_cast<const Header*>(dex_file)->checksum_, nullptr,
nullptr, error_msg);
} else if (OpenMemoryWithoutOdexSym) {
return OpenMemoryWithoutOdex(reinterpret_cast<const uint8_t*>(dex_file), size, location,
reinterpret_cast<const Header*>(dex_file)->checksum_,
nullptr, error_msg);
} else {
if (error_msg) *error_msg = "no sym";
return nullptr;
}
}
jobject ToJavaDexFile(JNIEnv* env) const {
auto java_dex_file = env->AllocObject(dex_file_class); auto java_dex_file = env->AllocObject(dex_file_class);
auto cookie = JNI_NewLongArray(env, dex_file_start_index + 1); auto cookie = JNI_NewLongArray(env, dex_file_start_index + 1);
cookie[oat_file_index] = 0; if (dex_file_start_index != size_t(-1)) [[likely]] {
cookie[dex_file_start_index] = reinterpret_cast<jlong>(this); cookie[oat_file_index] = 0;
cookie.commit(); cookie[dex_file_start_index] = reinterpret_cast<jlong>(this);
JNI_SetObjectField(env, java_dex_file, cookie_field, cookie); cookie.commit();
if (internal_cookie_field) { JNI_SetObjectField(env, java_dex_file, cookie_field, cookie);
JNI_SetObjectField(env, java_dex_file, internal_cookie_field, cookie); if (internal_cookie_field) {
JNI_SetObjectField(env, java_dex_file, internal_cookie_field, cookie);
}
} else {
JNI_SetLongField(
env, java_dex_file, cookie_field,
static_cast<jlong>(reinterpret_cast<uintptr_t>(new std::vector{this})));
} }
JNI_SetObjectField(env, java_dex_file, file_name_field, JNI_NewStringUTF(env, "")); JNI_SetObjectField(env, java_dex_file, file_name_field, JNI_NewStringUTF(env, ""));
return java_dex_file; return java_dex_file;
@ -53,15 +102,32 @@ public:
LP_SELECT("_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_" LP_SELECT("_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_", "traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_",
"_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_" "_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"))) "traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_")) &&
[[unlikely]] { !RETRIEVE_FUNC_SYMBOL(
OpenMemoryRaw,
LP_SELECT("_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_7OatFileEPS9_",
"_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_7OatFileEPS9_")) &&
!RETRIEVE_FUNC_SYMBOL(
OpenMemoryWithoutOdex,
LP_SELECT("_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPS9_",
"_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIcNS3_11char_"
"traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPS9_"))) [[unlikely]] {
LOGE("Failed to find OpenMemory");
return false; return false;
} }
dex_file_class = JNI_NewGlobalRef(env, JNI_FindClass(env, "dalvik/system/DexFile")); dex_file_class = JNI_NewGlobalRef(env, JNI_FindClass(env, "dalvik/system/DexFile"));
if (!dex_file_class) { if (!dex_file_class) {
return false; return false;
} }
cookie_field = JNI_GetFieldID(env, dex_file_class, "mCookie", "Ljava/lang/Object;"); if (sdk_int >= __ANDROID_API_M__) {
cookie_field = JNI_GetFieldID(env, dex_file_class, "mCookie", "Ljava/lang/Object;");
} else {
cookie_field = JNI_GetFieldID(env, dex_file_class, "mCookie", "J");
dex_file_start_index = -1;
}
if (!cookie_field) { if (!cookie_field) {
return false; return false;
} }

View File

@ -228,23 +228,23 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) {
return false; return false;
} }
if (!Class::Init(env, handler)) { if (!Class::Init(env, handler)) {
LOGE("failed to init mirror class"); LOGE("Failed to init mirror class");
return false; return false;
} }
if (!Instrumentation::Init(handler)) { if (!Instrumentation::Init(handler)) {
LOGE("failed to init instrumentation"); LOGE("Failed to init instrumentation");
return false; return false;
} }
if (!ScopedSuspendAll::Init(handler)) { if (!ScopedSuspendAll::Init(handler)) {
LOGE("failed to init scoped suspend all"); LOGE("Failed to init scoped suspend all");
return false; return false;
} }
if (!ScopedGCCriticalSection::Init(handler)) { if (!ScopedGCCriticalSection::Init(handler)) {
LOGE("failed to init scoped gc critical section"); LOGE("Failed to init scoped gc critical section");
return false; return false;
} }
if (!JitCodeCache::Init(handler)) { if (!JitCodeCache::Init(handler)) {
LOGE("failed to init jit code cache"); LOGE("Failed to init jit code cache");
return false; return false;
} }
if (!DexFile::Init(env, handler)) { if (!DexFile::Init(env, handler)) {
@ -365,12 +365,14 @@ std::tuple<jclass, jfieldID, jmethodID, jmethodID> BuildDex(JNIEnv *env, jobject
memcpy(target, image.ptr(), image.size()); memcpy(target, image.ptr(), image.size());
mprotect(target, image.size(), PROT_READ); mprotect(target, image.size(), PROT_READ);
std::string err_msg; std::string err_msg;
auto *dex = DexFile::OpenMemory( const auto *dex = DexFile::OpenMemory(
target, image.size(), target, image.size(), generated_source_name.empty() ? "lsplant" : generated_source_name,
generated_source_name.empty() ? "lsplant" : generated_source_name, &err_msg) &err_msg);
.release(); if (!dex) {
auto java_dex_file = WrapScope(env, dex->ToJavaDexFile(env)); LOGE("Failed to open memory dex: %s", err_msg.data());
if (java_dex_file) { }
auto java_dex_file = WrapScope(env, dex ? dex->ToJavaDexFile(env) : jobject{nullptr});
if (dex && java_dex_file) {
auto p = JNI_NewObject(env, path_class_loader, path_class_loader_init, auto p = JNI_NewObject(env, path_class_loader, path_class_loader_init,
JNI_NewStringUTF(env, ""), class_loader); JNI_NewStringUTF(env, ""), class_loader);
target_class = JNI_Cast<jclass>(JNI_CallObjectMethod( target_class = JNI_Cast<jclass>(JNI_CallObjectMethod(