diff --git a/.gitmodules b/.gitmodules index 5f60ba22..4dcb4781 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "blockmiui"] path = blockmiui url = https://github.com/577fkj/blockmiui.git +[submodule "app/src/main/cpp/dex_builder"] + path = app/src/main/cpp/dex_builder + url = https://github.com/LSPosed/DexBuilder diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e2e14006..ca48cb36 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,6 +14,14 @@ android { targetSdk = 32 versionCode = 40 versionName = "1.3.9" + externalNativeBuild { + cmake { + targets("DexBuilder") + abiFilters("arm64-v8a") + cppFlags("-std=c++17") + cFlags("-std=gnu99") + } + } } buildTypes { @@ -53,6 +61,15 @@ android { "Simplicity_Tools_Xposed-${versionName}-${name}.apk" } } + androidResources { + noCompress("libDexBuilder.so") + } + externalNativeBuild { + cmake { + path("src/main/cpp/CMakeLists.txt") + } + } + ndkVersion = "23.1.7779620" } dependencies { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 6102543f..12bb0427 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -36,5 +36,7 @@ public static void throw*(...); } +-keep class me.iacn.biliroaming.utils.DexHelper { *; } + -allowaccessmodification -overloadaggressively \ No newline at end of file diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000..9c7d7553 --- /dev/null +++ b/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.18.1) + + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_C_STANDARD 11) + +add_subdirectory(dex_builder) + +add_library( + DexBuilder + SHARED + native.cpp + biliroaming.cc + ) +target_include_directories(DexBuilder PUBLIC dex_builder/include) + +find_library( + log-lib + log) + +target_link_libraries( + DexBuilder + ${log-lib} + dex_builder_static + ) \ No newline at end of file diff --git a/app/src/main/cpp/biliroaming.cc b/app/src/main/cpp/biliroaming.cc new file mode 100644 index 00000000..8628d406 --- /dev/null +++ b/app/src/main/cpp/biliroaming.cc @@ -0,0 +1,669 @@ +#include +#include +#include + +#define LOG_TAG "QAuxv" +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__) +#define PLOGE(fmt, args...) \ + LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) + +namespace { +jfieldID token_field; +jfieldID class_loader_field; +jmethodID load_class_method; +// jmethodID get_declared_method; +// jmethodID get_declared_field; +jmethodID get_name_method; +jmethodID get_declaring_class_method; +jmethodID get_parameters_method; +jmethodID get_class_name_method; +jfieldID path_list_field; +jfieldID element_field; +jfieldID dex_file_field; +jfieldID cookie_field; + +jclass LoadClass(JNIEnv *env, jobject class_loader, + std::string_view descriptor) { + if (descriptor.empty()) + return nullptr; + if (descriptor[0] != 'L') + return nullptr; + std::string name(descriptor.substr(1, descriptor.length() - 2)); + std::replace(name.begin(), name.end(), '/', '.'); + auto java_name = env->NewStringUTF(name.data()); + auto res = (jclass)env->CallObjectMethod(class_loader, load_class_method, + java_name, false); + return res; +} + +std::string GetClassDescriptor(JNIEnv *env, jclass clazz) { + auto java_name = (jstring)env->CallObjectMethod(clazz, get_class_name_method); + auto name = env->GetStringUTFChars(java_name, nullptr); + std::string descriptor = name; + std::replace(descriptor.begin(), descriptor.end(), '.', '/'); + if (descriptor[0] != '[') { + if (descriptor == "boolean") { + descriptor = "Z"; + } else if (descriptor == "byte") { + descriptor = "B"; + } else if (descriptor == "short") { + descriptor = "S"; + } else if (descriptor == "char") { + descriptor = "C"; + } else if (descriptor == "int") { + descriptor = "I"; + } else if (descriptor == "long") { + descriptor = "J"; + } else if (descriptor == "float") { + descriptor = "F"; + } else if (descriptor == "double") { + descriptor = "D"; + } + descriptor = "L" + std::string(name) + ";"; + } + env->ReleaseStringUTFChars(java_name, name); + return descriptor; +} +} // namespace + +struct MyDexFile { + const void *begin_{}; + size_t size_{}; + const uint8_t *const data_begin_{}; + const size_t data_size_{}; + + virtual ~MyDexFile() = default; +}; + +extern "C" JNIEXPORT jlongArray JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_findMethodUsingString( + JNIEnv *env, jobject thiz, jstring str, jboolean match_prefix, + jlong return_type, jshort parameter_count, jstring parameter_shorty, + jlong declaring_class, jlongArray parameter_types, + jlongArray contains_parameter_types, jintArray dex_priority, + jboolean find_first) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return nullptr; + if (!str) + return nullptr; + auto str_ = env->GetStringUTFChars(str, nullptr); + auto parameter_shorty_ = + parameter_shorty ? env->GetStringUTFChars(parameter_shorty, nullptr) + : nullptr; + std::vector dex_priority_; + jint *dex_priority_elements = nullptr; + if (dex_priority) { + dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); + dex_priority_.assign(dex_priority_elements, + dex_priority_elements + + env->GetArrayLength(dex_priority)); + } + + std::vector parameter_types_; + jlong *parameter_types_elements = nullptr; + if (parameter_types) { + parameter_types_elements = + env->GetLongArrayElements(parameter_types, nullptr); + parameter_types_.assign(parameter_types_elements, + parameter_types_elements + + env->GetArrayLength(parameter_types)); + } + + std::vector contains_parameter_types_; + jlong *contains_parameter_types_elements = nullptr; + if (contains_parameter_types) { + contains_parameter_types_elements = + env->GetLongArrayElements(contains_parameter_types, nullptr); + contains_parameter_types_.assign( + contains_parameter_types_elements, + contains_parameter_types_elements + + env->GetArrayLength(contains_parameter_types)); + } + + auto out = helper->FindMethodUsingString( + str_, match_prefix, return_type, parameter_count, parameter_shorty_ ? parameter_shorty_ : "", + declaring_class, contains_parameter_types_, contains_parameter_types_, + dex_priority_, find_first); + + env->ReleaseStringUTFChars(str, str_); + if (parameter_shorty_) + env->ReleaseStringUTFChars(parameter_shorty, parameter_shorty_); + if (dex_priority_elements) + env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, + JNI_ABORT); + if (parameter_types_elements) + env->ReleaseLongArrayElements(parameter_types, parameter_types_elements, + JNI_ABORT); + if (contains_parameter_types_elements) + env->ReleaseLongArrayElements(contains_parameter_types, + contains_parameter_types_elements, JNI_ABORT); + auto res = env->NewLongArray(static_cast(out.size())); + auto res_element = env->GetLongArrayElements(res, nullptr); + for (size_t i = 0; i < out.size(); ++i) { + res_element[i] = out[i]; + } + env->ReleaseLongArrayElements(res, res_element, 0); + return res; +} + +extern "C" JNIEXPORT jlong JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_load(JNIEnv *env, jobject thiz, + jobject class_loader) { + if (!class_loader) + return 0; + auto path_list = env->GetObjectField(class_loader, path_list_field); + if (!path_list) + return 0; + auto elements = (jobjectArray)env->GetObjectField(path_list, element_field); + if (!elements) + return 0; + std::vector> images; + for (auto i = 0, len = env->GetArrayLength(elements); i < len; ++i) { + auto element = env->GetObjectArrayElement(elements, i); + if (!element) continue; + auto java_dex_file = env->GetObjectField(element, dex_file_field); + if (!java_dex_file) continue; + auto cookie = (jlongArray)env->GetObjectField(java_dex_file, cookie_field); + if (!cookie) continue; + auto dex_file_length = env->GetArrayLength(cookie); + const auto *dex_files = reinterpret_cast( + env->GetLongArrayElements(cookie, nullptr)); + while (dex_file_length-- > 1) { + const auto *dex_file = dex_files[dex_file_length]; + if (!dex_file) { + LOGE("dex_file is nullptr at %d", dex_file_length); + continue; + } + LOGD("Got dex file %d", dex_file_length); + if (dex::Reader::IsCompact(dex_file->begin_)) { + images.emplace_back(dex_file->begin_, dex_file->size_, dex_file->data_begin_, dex_file->data_size_); + } else { + images.emplace_back(dex_file->begin_, dex_file->size_, nullptr, 0); + } + } + } + if (images.empty()) return 0; + auto res = reinterpret_cast(new DexHelper(images)); + env->SetLongField(thiz, token_field, res); + return res; +} + +extern "C" JNIEXPORT jlongArray JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_findMethodInvoking( + JNIEnv *env, jobject thiz, jlong method_index, jlong return_type, + jshort parameter_count, jstring parameter_shorty, jlong declaring_class, + jlongArray parameter_types, jlongArray contains_parameter_types, + jintArray dex_priority, jboolean find_first) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return nullptr; + auto parameter_shorty_ = + parameter_shorty ? env->GetStringUTFChars(parameter_shorty, nullptr) + : nullptr; + std::vector dex_priority_; + jint *dex_priority_elements = nullptr; + if (dex_priority) { + dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); + dex_priority_.assign(dex_priority_elements, + dex_priority_elements + + env->GetArrayLength(dex_priority)); + } + + std::vector parameter_types_; + jlong *parameter_types_elements = nullptr; + if (parameter_types) { + parameter_types_elements = + env->GetLongArrayElements(parameter_types, nullptr); + parameter_types_.assign(parameter_types_elements, + parameter_types_elements + + env->GetArrayLength(parameter_types)); + } + + std::vector contains_parameter_types_; + jlong *contains_parameter_types_elements = nullptr; + if (contains_parameter_types) { + contains_parameter_types_elements = + env->GetLongArrayElements(contains_parameter_types, nullptr); + contains_parameter_types_.assign( + contains_parameter_types_elements, + contains_parameter_types_elements + + env->GetArrayLength(contains_parameter_types)); + } + + auto out = helper->FindMethodInvoking( + method_index, return_type, parameter_count, parameter_shorty_ ? parameter_shorty_ : "", + declaring_class, contains_parameter_types_, contains_parameter_types_, + dex_priority_, find_first); + + if (parameter_shorty_) + env->ReleaseStringUTFChars(parameter_shorty, parameter_shorty_); + if (dex_priority_elements) + env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, + JNI_ABORT); + if (parameter_types_elements) + env->ReleaseLongArrayElements(parameter_types, parameter_types_elements, + JNI_ABORT); + if (contains_parameter_types_elements) + env->ReleaseLongArrayElements(contains_parameter_types, + contains_parameter_types_elements, JNI_ABORT); + auto res = env->NewLongArray(static_cast(out.size())); + auto res_element = env->GetLongArrayElements(res, nullptr); + for (size_t i = 0; i < out.size(); ++i) { + res_element[i] = out[i]; + } + env->ReleaseLongArrayElements(res, res_element, 0); + return res; +} +extern "C" JNIEXPORT jlongArray JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_findMethodInvoked( + JNIEnv *env, jobject thiz, jlong method_index, jlong return_type, + jshort parameter_count, jstring parameter_shorty, jlong declaring_class, + jlongArray parameter_types, jlongArray contains_parameter_types, + jintArray dex_priority, jboolean find_first) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return nullptr; + auto parameter_shorty_ = + parameter_shorty ? env->GetStringUTFChars(parameter_shorty, nullptr) + : nullptr; + std::vector dex_priority_; + jint *dex_priority_elements = nullptr; + if (dex_priority) { + dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); + dex_priority_.assign(dex_priority_elements, + dex_priority_elements + + env->GetArrayLength(dex_priority)); + } + + std::vector parameter_types_; + jlong *parameter_types_elements = nullptr; + if (parameter_types) { + parameter_types_elements = + env->GetLongArrayElements(parameter_types, nullptr); + parameter_types_.assign(parameter_types_elements, + parameter_types_elements + + env->GetArrayLength(parameter_types)); + } + + std::vector contains_parameter_types_; + jlong *contains_parameter_types_elements = nullptr; + if (contains_parameter_types) { + contains_parameter_types_elements = + env->GetLongArrayElements(contains_parameter_types, nullptr); + contains_parameter_types_.assign( + contains_parameter_types_elements, + contains_parameter_types_elements + + env->GetArrayLength(contains_parameter_types)); + } + + auto out = helper->FindMethodInvoked( + method_index, return_type, parameter_count, parameter_shorty_ ? parameter_shorty_ : "", + declaring_class, contains_parameter_types_, contains_parameter_types_, + dex_priority_, find_first); + + if (parameter_shorty_) + env->ReleaseStringUTFChars(parameter_shorty, parameter_shorty_); + if (dex_priority_elements) + env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, + JNI_ABORT); + if (parameter_types_elements) + env->ReleaseLongArrayElements(parameter_types, parameter_types_elements, + JNI_ABORT); + if (contains_parameter_types_elements) + env->ReleaseLongArrayElements(contains_parameter_types, + contains_parameter_types_elements, JNI_ABORT); + auto res = env->NewLongArray(static_cast(out.size())); + auto res_element = env->GetLongArrayElements(res, nullptr); + for (size_t i = 0; i < out.size(); ++i) { + res_element[i] = out[i]; + } + env->ReleaseLongArrayElements(res, res_element, 0); + return res; +} +extern "C" JNIEXPORT jlongArray JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_findMethodSettingField( + JNIEnv *env, jobject thiz, jlong field_index, jlong return_type, + jshort parameter_count, jstring parameter_shorty, jlong declaring_class, + jlongArray parameter_types, jlongArray contains_parameter_types, + jintArray dex_priority, jboolean find_first) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return nullptr; + auto parameter_shorty_ = + parameter_shorty ? env->GetStringUTFChars(parameter_shorty, nullptr) + : nullptr; + std::vector dex_priority_; + jint *dex_priority_elements = nullptr; + if (dex_priority) { + dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); + dex_priority_.assign(dex_priority_elements, + dex_priority_elements + + env->GetArrayLength(dex_priority)); + } + + std::vector parameter_types_; + jlong *parameter_types_elements = nullptr; + if (parameter_types) { + parameter_types_elements = + env->GetLongArrayElements(parameter_types, nullptr); + parameter_types_.assign(parameter_types_elements, + parameter_types_elements + + env->GetArrayLength(parameter_types)); + } + + std::vector contains_parameter_types_; + jlong *contains_parameter_types_elements = nullptr; + if (contains_parameter_types) { + contains_parameter_types_elements = + env->GetLongArrayElements(contains_parameter_types, nullptr); + contains_parameter_types_.assign( + contains_parameter_types_elements, + contains_parameter_types_elements + + env->GetArrayLength(contains_parameter_types)); + } + + auto out = helper->FindMethodSettingField( + field_index, return_type, parameter_count, parameter_shorty_ ? parameter_shorty_ : "", + declaring_class, contains_parameter_types_, contains_parameter_types_, + dex_priority_, find_first); + + if (parameter_shorty_) + env->ReleaseStringUTFChars(parameter_shorty, parameter_shorty_); + if (dex_priority_elements) + env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, + JNI_ABORT); + if (parameter_types_elements) + env->ReleaseLongArrayElements(parameter_types, parameter_types_elements, + JNI_ABORT); + if (contains_parameter_types_elements) + env->ReleaseLongArrayElements(contains_parameter_types, + contains_parameter_types_elements, JNI_ABORT); + auto res = env->NewLongArray(static_cast(out.size())); + auto res_element = env->GetLongArrayElements(res, nullptr); + for (size_t i = 0; i < out.size(); ++i) { + res_element[i] = out[i]; + } + env->ReleaseLongArrayElements(res, res_element, 0); + return res; +} +extern "C" JNIEXPORT jlongArray JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_findMethodGettingField( + JNIEnv *env, jobject thiz, jlong field_index, jlong return_type, + jshort parameter_count, jstring parameter_shorty, jlong declaring_class, + jlongArray parameter_types, jlongArray contains_parameter_types, + jintArray dex_priority, jboolean find_first) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return nullptr; + auto parameter_shorty_ = + parameter_shorty ? env->GetStringUTFChars(parameter_shorty, nullptr) + : nullptr; + std::vector dex_priority_; + jint *dex_priority_elements = nullptr; + if (dex_priority) { + dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); + dex_priority_.assign(dex_priority_elements, + dex_priority_elements + + env->GetArrayLength(dex_priority)); + } + + std::vector parameter_types_; + jlong *parameter_types_elements = nullptr; + if (parameter_types) { + parameter_types_elements = + env->GetLongArrayElements(parameter_types, nullptr); + parameter_types_.assign(parameter_types_elements, + parameter_types_elements + + env->GetArrayLength(parameter_types)); + } + + std::vector contains_parameter_types_; + jlong *contains_parameter_types_elements = nullptr; + if (contains_parameter_types) { + contains_parameter_types_elements = + env->GetLongArrayElements(contains_parameter_types, nullptr); + contains_parameter_types_.assign( + contains_parameter_types_elements, + contains_parameter_types_elements + + env->GetArrayLength(contains_parameter_types)); + } + + auto out = helper->FindMethodGettingField( + field_index, return_type, parameter_count, parameter_shorty_ ? parameter_shorty_ : "", + declaring_class, contains_parameter_types_, contains_parameter_types_, + dex_priority_, find_first); + + if (parameter_shorty_) + env->ReleaseStringUTFChars(parameter_shorty, parameter_shorty_); + if (dex_priority_elements) + env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, + JNI_ABORT); + if (parameter_types_elements) + env->ReleaseLongArrayElements(parameter_types, parameter_types_elements, + JNI_ABORT); + if (contains_parameter_types_elements) + env->ReleaseLongArrayElements(contains_parameter_types, + contains_parameter_types_elements, JNI_ABORT); + auto res = env->NewLongArray(static_cast(out.size())); + auto res_element = env->GetLongArrayElements(res, nullptr); + for (size_t i = 0; i < out.size(); ++i) { + res_element[i] = out[i]; + } + env->ReleaseLongArrayElements(res, res_element, 0); + return res; +} +extern "C" JNIEXPORT jlongArray JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_findField(JNIEnv *env, jobject thiz, + jlong type, + jintArray dex_priority, + jboolean find_first) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + std::vector dex_priority_; + jint *dex_priority_elements = nullptr; + if (dex_priority) { + dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); + dex_priority_.assign(dex_priority_elements, + dex_priority_elements + + env->GetArrayLength(dex_priority)); + } + + auto out = helper->FindField(type, dex_priority_, find_first); + + if (dex_priority_elements) + env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, + JNI_ABORT); + + auto res = env->NewLongArray(static_cast(out.size())); + auto res_element = env->GetLongArrayElements(res, nullptr); + for (size_t i = 0; i < out.size(); ++i) { + res_element[i] = out[i]; + } + env->ReleaseLongArrayElements(res, res_element, 0); + return res; +} + +extern "C" JNIEXPORT jobject JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_decodeMethodIndex(JNIEnv *env, + jobject thiz, + jlong method_index) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return nullptr; + auto out = helper->DecodeMethod(method_index); + auto cl = env->GetObjectField(thiz, class_loader_field); + auto clazz = LoadClass(env, cl, out.declaring_class.name); + env->DeleteLocalRef(cl); + std::string sig; + sig.reserve(4096); + sig += "("; + for (const auto ¶m : out.parameters) { + sig += param.name; + } + sig += ")"; + sig += out.return_type.name; + auto *method = env->GetMethodID(clazz, out.name.data(), sig.data()); + if ((*env).ExceptionCheck()) { + (*env).ExceptionClear(); + } + if (!method) { + method = env->GetStaticMethodID(clazz, out.name.data(), sig.data()); + } + return env->ToReflectedMethod(clazz, method, false); +} + +extern "C" JNIEXPORT jobject JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_decodeFieldIndex(JNIEnv *env, + jobject thiz, + jlong field_index) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return nullptr; + auto out = helper->DecodeField(field_index); + auto cl = env->GetObjectField(thiz, class_loader_field); + auto clazz = LoadClass(env, cl, out.declaring_class.name); + env->DeleteLocalRef(cl); + return env->ToReflectedField( + clazz, env->GetFieldID(clazz, out.name.data(), out.type.name.data()), + false); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_encodeClassIndex(JNIEnv *env, + jobject thiz, + jclass clazz) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + return helper->CreateClassIndex(GetClassDescriptor(env, clazz)); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_encodeFieldIndex(JNIEnv *env, + jobject thiz, + jobject field) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return -1; + auto java_name = (jstring)env->CallObjectMethod(field, get_name_method); + auto clazz = (jclass)env->CallObjectMethod(field, get_declaring_class_method); + auto name = env->GetStringUTFChars(java_name, nullptr); + auto res = helper->CreateFieldIndex(name, GetClassDescriptor(env, clazz)); + env->ReleaseStringUTFChars(java_name, name); + return res; +} + +extern "C" JNIEXPORT jlong JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_encodeMethodIndex(JNIEnv *env, + jobject thiz, + jobject method) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return -1; + auto java_name = (jstring)env->CallObjectMethod(method, get_name_method); + auto clazz = + (jclass)env->CallObjectMethod(method, get_declaring_class_method); + auto name = env->GetStringUTFChars(java_name, nullptr); + auto params = + (jobjectArray)env->CallObjectMethod(method, get_parameters_method); + auto param_len = env->GetArrayLength(params); + std::vector param_descriptors; + param_descriptors.reserve(param_len); + for (size_t i = 0; i < param_len; ++i) { + auto param = (jclass)env->GetObjectArrayElement(params, i); + param_descriptors.emplace_back(GetClassDescriptor(env, param)); + env->DeleteLocalRef(param); + } + std::vector params_name; + params_name.reserve(param_descriptors.size()); + for (const auto &descriptor : param_descriptors) { + params_name.emplace_back(descriptor); + } + auto res = helper->CreateMethodIndex(name, GetClassDescriptor(env, clazz), + params_name); + env->ReleaseStringUTFChars(java_name, name); + return res; +} + +extern "C" JNIEXPORT jclass JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_decodeClassIndex(JNIEnv *env, + jobject thiz, + jlong class_index) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return nullptr; + auto out = helper->DecodeClass(class_index); + auto cl = env->GetObjectField(thiz, class_loader_field); + auto res = LoadClass(env, cl, out.name); + env->DeleteLocalRef(cl); + return res; +} + +extern "C" JNIEXPORT void JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_close(JNIEnv *env, jobject thiz) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + env->SetLongField(thiz, token_field, jlong(0)); + delete helper; +} + +extern "C" JNIEXPORT jint JNICALL BILI_JNI_OnLoad(JavaVM *vm, void *) { + JNIEnv *env = nullptr; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_4) != JNI_OK) { + return -1; + } + jclass helper = env->FindClass("me/iacn/biliroaming/utils/DexHelper"); + token_field = env->GetFieldID(helper, "token", "J"); + class_loader_field = + env->GetFieldID(helper, "classLoader", "Ljava/lang/ClassLoader;"); + auto class_loader = env->FindClass("java/lang/ClassLoader"); + load_class_method = env->GetMethodID( + class_loader, "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;"); + auto member = env->FindClass("java/lang/reflect/Member"); + get_declaring_class_method = + env->GetMethodID(member, "getDeclaringClass", "()Ljava/lang/Class;"); + get_name_method = env->GetMethodID(member, "getName", "()Ljava/lang/String;"); + auto executable = env->FindClass("java/lang/reflect/Executable"); + get_parameters_method = + env->GetMethodID(executable, "getParameterTypes", "()[Ljava/lang/Class;"); + auto clazz = env->FindClass("java/lang/Class"); + get_class_name_method = + env->GetMethodID(clazz, "getName", "()Ljava/lang/String;"); + auto dex_class_loader = env->FindClass("dalvik/system/BaseDexClassLoader"); + path_list_field = env->GetFieldID(dex_class_loader, "pathList", + "Ldalvik/system/DexPathList;"); + auto dex_path_list = env->FindClass("dalvik/system/DexPathList"); + element_field = env->GetFieldID(dex_path_list, "dexElements", + "[Ldalvik/system/DexPathList$Element;"); + auto element = env->FindClass("dalvik/system/DexPathList$Element"); + dex_file_field = + env->GetFieldID(element, "dexFile", "Ldalvik/system/DexFile;"); + auto dex_file = env->FindClass("dalvik/system/DexFile"); + cookie_field = env->GetFieldID(dex_file, "mCookie", "Ljava/lang/Object;"); + return JNI_VERSION_1_4; +} + +extern "C" +JNIEXPORT void JNICALL +Java_me_iacn_biliroaming_utils_DexHelper_createFullCache(JNIEnv *env, jobject thiz) { + auto *helper = + reinterpret_cast(env->GetLongField(thiz, token_field)); + if (!helper) + return; + helper->CreateFullCache(); +} diff --git a/app/src/main/cpp/logging.h b/app/src/main/cpp/logging.h new file mode 100644 index 00000000..2f3b6145 --- /dev/null +++ b/app/src/main/cpp/logging.h @@ -0,0 +1,29 @@ +#ifndef _LOGGING_H +#define _LOGGING_H + +#include + +#ifndef LOG_TAG +#define LOG_TAG "SimplicityTools" +#endif + +#ifdef LOG_DISABLED +#define LOGD(...) +#define LOGV(...) +#define LOGI(...) +#define LOGW(...) +#define LOGE(...) +#else +#ifndef NDEBUG +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#else +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#endif +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) +#endif + +#endif // _LOGGING_H diff --git a/app/src/main/cpp/native.cpp b/app/src/main/cpp/native.cpp new file mode 100644 index 00000000..aab2b785 --- /dev/null +++ b/app/src/main/cpp/native.cpp @@ -0,0 +1,9 @@ +#include +#include "logging.h" +#include "native.h" + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { + LOGD("%s", "JNI_OnLoad"); + BILI_JNI_OnLoad(vm, reserved); + return JNI_VERSION_1_6; +} diff --git a/app/src/main/cpp/native.h b/app/src/main/cpp/native.h new file mode 100644 index 00000000..e7989924 --- /dev/null +++ b/app/src/main/cpp/native.h @@ -0,0 +1,10 @@ +// +// Created by Owner on 2022/3/9. +// + +#ifndef FUCKCOOLAPKR_NATIVE_H +#define FUCKCOOLAPKR_NATIVE_H + +extern "C" jint BILI_JNI_OnLoad(JavaVM *vm, void *reserved); + +#endif //FUCKCOOLAPKR_NATIVE_H diff --git a/app/src/main/cpp/native_api.h b/app/src/main/cpp/native_api.h new file mode 100644 index 00000000..f2c3e5fe --- /dev/null +++ b/app/src/main/cpp/native_api.h @@ -0,0 +1,21 @@ +#ifndef XPOSEDTEMPLATE_NATIVE_API_H +#define XPOSEDTEMPLATE_NATIVE_API_H + +#include +#include + +typedef int (*HookFunType)(void *func, void *replace, void **backup); + +typedef int (*UnhookFunType)(void *func); + +typedef void (*NativeOnModuleLoaded)(const char *name, void *handle); + +typedef struct { + uint32_t version; + HookFunType hookFunc; + UnhookFunType unhookFunc; +} NativeAPIEntries; + +typedef NativeOnModuleLoaded (*NativeInit)(const NativeAPIEntries *entries); + +#endif //XPOSEDTEMPLATE_NATIVE_API_H diff --git a/app/src/main/java/com/lt2333/simplicitytools/hook/app/SecurityCenter.kt b/app/src/main/java/com/lt2333/simplicitytools/hook/app/SecurityCenter.kt index 64816012..4847c113 100644 --- a/app/src/main/java/com/lt2333/simplicitytools/hook/app/SecurityCenter.kt +++ b/app/src/main/java/com/lt2333/simplicitytools/hook/app/SecurityCenter.kt @@ -2,19 +2,33 @@ package com.lt2333.simplicitytools.hook.app import com.lt2333.simplicitytools.hook.app.securitycenter.LockOneHundred import com.lt2333.simplicitytools.hook.app.securitycenter.RemoveMacroBlacklist +import com.lt2333.simplicitytools.hook.app.securitycenter.ShowBatteryTemperature import com.lt2333.simplicitytools.hook.app.securitycenter.SkipWaitingTime +import com.lt2333.simplicitytools.util.log import de.robv.android.xposed.IXposedHookLoadPackage import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam +import me.iacn.biliroaming.utils.DexHelper class SecurityCenter : IXposedHookLoadPackage { + + var dexHelper: DexHelper? = null + override fun handleLoadPackage(lpparam: LoadPackageParam) { XposedBridge.log("Simplicitytools: 成功 Hook "+javaClass.simpleName) + try { + System.loadLibrary("DexBuilder") + dexHelper = DexHelper.getInstance(lpparam.classLoader) + } catch (e: UnsatisfiedLinkError) { + log("DexBuilder 加载失败") + } //跳过 5/10秒等待时间 SkipWaitingTime().handleLoadPackage(lpparam) //锁定 100分 LockOneHundred().handleLoadPackage(lpparam) //去除自动连招黑名单 RemoveMacroBlacklist().handleLoadPackage(lpparam) + //显示电池温度 + ShowBatteryTemperature(dexHelper).handleLoadPackage(lpparam) } } \ No newline at end of file diff --git a/app/src/main/java/com/lt2333/simplicitytools/hook/app/securitycenter/ShowBatteryTemperature.kt b/app/src/main/java/com/lt2333/simplicitytools/hook/app/securitycenter/ShowBatteryTemperature.kt new file mode 100644 index 00000000..30be292c --- /dev/null +++ b/app/src/main/java/com/lt2333/simplicitytools/hook/app/securitycenter/ShowBatteryTemperature.kt @@ -0,0 +1,39 @@ +package com.lt2333.simplicitytools.hook.app.securitycenter + +import com.lt2333.simplicitytools.util.log +import de.robv.android.xposed.IXposedHookLoadPackage +import de.robv.android.xposed.XC_MethodReplacement +import de.robv.android.xposed.XposedBridge +import de.robv.android.xposed.callbacks.XC_LoadPackage +import me.iacn.biliroaming.utils.DexHelper + +class ShowBatteryTemperature(private val dexHelper: DexHelper?): IXposedHookLoadPackage { + + override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { + if (dexHelper == null) { + log("DexHelper为null,hook ShowBatteryTemperature 失败") + return + } + + val methodIndex = dexHelper.findMethodUsingString( + "isBatteryLifeFunctionSupported", + true, + -1, + 0, + null, + -1, + null, + null, + null, + true + ).firstOrNull() + if (methodIndex == null) { + log("ShowBatteryTemperature 无法定位方法") + return + } + val method = dexHelper.decodeMethodIndex(methodIndex) + log(method) + XposedBridge.hookMethod(method, XC_MethodReplacement.returnConstant(true)) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/lt2333/simplicitytools/util/KotlinXposedHelper.kt b/app/src/main/java/com/lt2333/simplicitytools/util/KotlinXposedHelper.kt index d22161e9..81526532 100644 --- a/app/src/main/java/com/lt2333/simplicitytools/util/KotlinXposedHelper.kt +++ b/app/src/main/java/com/lt2333/simplicitytools/util/KotlinXposedHelper.kt @@ -8,6 +8,8 @@ import de.robv.android.xposed.XC_MethodReplacement import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedHelpers import de.robv.android.xposed.callbacks.XC_LayoutInflated +import me.iacn.biliroaming.utils.DexHelper + import java.lang.reflect.Field import java.lang.reflect.Member import java.util.* diff --git a/app/src/main/java/me/iacn/biliroaming/utils/DexHelper.java b/app/src/main/java/me/iacn/biliroaming/utils/DexHelper.java new file mode 100644 index 00000000..f676c227 --- /dev/null +++ b/app/src/main/java/me/iacn/biliroaming/utils/DexHelper.java @@ -0,0 +1,87 @@ +package me.iacn.biliroaming.utils; + +import com.lt2333.simplicitytools.util.KotlinXposedHelperKt; + +import java.io.Closeable; +import java.lang.reflect.Field; +import java.lang.reflect.Member; + +import dalvik.system.BaseDexClassLoader; + +public class DexHelper implements AutoCloseable, Closeable { + public static final int NO_CLASS_INDEX = -1; + private final ClassLoader classLoader; + private final long token; + private static DexHelper instance; + + public static DexHelper getInstance(ClassLoader classLoader) { + synchronized (DexHelper.class) { + if (instance != null) return instance; + BaseDexClassLoader loader = KotlinXposedHelperKt.findDexClassLoader(classLoader); + if (loader == null) { + return null; + } + instance = new DexHelper(loader); + return instance; + } + } + + public DexHelper(ClassLoader classLoader) { + this.classLoader = classLoader; + token = load(classLoader); + } + + public native long[] findMethodUsingString( + String str, boolean matchPrefix, long returnType, short parameterCount, + String parameterShorty, long declaringClass, long[] parameterTypes, + long[] containsParameterTypes, int[] dexPriority, boolean findFirst); + + public native long[] findMethodInvoking(long methodIndex, long returnType, + short parameterCount, String parameterShorty, + long declaringClass, long[] parameterTypes, + long[] containsParameterTypes, + int[] dexPriority, boolean findFirst); + + public native long[] findMethodInvoked(long methodIndex, long returnType, + short parameterCount, String parameterShorty, + long declaringClass, long[] parameterTypes, + long[] containsParameterTypes, + int[] dexPriority, boolean findFirst); + + public native long[] findMethodSettingField( + long fieldIndex, long returnType, short parameterCount, + String parameterShorty, long declaringClass, long[] parameterTypes, + long[] containsParameterTypes, int[] dexPriority, boolean findFirst); + + public native long[] findMethodGettingField( + long fieldIndex, long returnType, short parameterCount, + String parameterShorty, long declaringClass, long[] parameterTypes, + long[] containsParameterTypes, int[] dexPriority, boolean findFirst); + + public native long[] findField(long type, int[] dexPriority, boolean findFirst); + + public native Member decodeMethodIndex(long methodIndex); + + public native long encodeMethodIndex(Member method); + + public native Field decodeFieldIndex(long fieldIndex); + + public native long encodeFieldIndex(Field field); + + public native long encodeClassIndex(Class clazz); + + public native Class decodeClassIndex(long classIndex); + + public native void createFullCache(); + + @Override + public native void close(); + + @Override + protected void finalize() { + close(); + } + + private native long load(ClassLoader classLoader); +} +