From b7266e490fd3b993b542cc6b5d60aa40f9d327e7 Mon Sep 17 00:00:00 2001 From: WhiteChi Date: Sun, 26 Nov 2023 22:29:11 +0800 Subject: [PATCH] =?UTF-8?q?`Shamrock`:=20=E3=82=A2=E3=83=B3=E3=83=81?= =?UTF-8?q?=E3=82=B7=E3=83=9F=E3=83=A5=E3=83=AC=E3=83=BC=E3=82=BF=E6=A4=9C?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 +- xposed/src/main/cpp/CMakeLists.txt | 3 + xposed/src/main/cpp/clover.cpp | 103 +++++++++++++++++- xposed/src/main/cpp/jnihelper.h | 40 +++++++ xposed/src/main/cpp/lsposed.h | 7 ++ .../moe/fuqiuluo/shamrock/helper/LogCenter.kt | 25 ++++- .../fuqiuluo/shamrock/xposed/XposedEntry.kt | 17 +-- .../shamrock/xposed/actions/AntiDetection.kt | 21 ++++ .../shamrock/xposed/loader/NativeLoader.kt | 3 +- 9 files changed, 207 insertions(+), 14 deletions(-) create mode 100644 xposed/src/main/cpp/jnihelper.h diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 510ad2d..8b9920f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,7 +19,7 @@ android:theme="@style/Theme.Shamrock" android:zygotePreloadName="@string/app_name" android:multiArch="true" - android:extractNativeLibs="false" + android:extractNativeLibs="true" tools:targetApi="31"> #include #include +#include +#include #include +#include #include +#include #include "lsposed.h" +#include "jnihelper.h" + +static HookFunType hook_function = nullptr; + +static std::vector qemu_detect_props = { + "init.svc.qemu-props", "qemu.hw.mainkeys", "qemu.sf.fake_camera", "ro.kernel.android.qemud", + "qemu.sf.lcd_density", "init.svc.qemud", "ro.kernel.qemu", + "libc.debug.malloc" +}; + +int (*backup_system_property_get)(const char *name, char *value); + +int fake_system_property_get(const char *name, char *value) { + for (auto &prop: qemu_detect_props) { + if (strstr(name, prop.c_str())) { + LOGI("[Shamrock] bypass qemu detection"); + value[0] = 0; + return 0; + } + } + + if (strstr(name, "ro.debuggable") + || strstr(name, "ro.kernel.qemu.gles") + || strstr(name, "debug.atrace.tags.enableflags")) { + strcpy(value, "0"); + return 1; + } + + if (strstr(name, "ro.product.cpu.abilist")) { + int len = backup_system_property_get(name, value); + if (len > 0) { + if (strstr(value, "x86")) { + strcpy(value, "arm64-v8a,armeabi-v7a,armeabi"); + return 29; + } + } + return len; + } + + if (strstr(name, "ro.hardware")) { + int len = backup_system_property_get(name, value); + if (len > 0) { + if (strstr(value, "generic") + || strstr(value, "unknown") + || strstr(value, "emulator") + || strstr(value, "vbox") + || strstr(value, "genymotion") + || strstr(value, "goldfish")) { + strcpy(value, "qcom"); + return 4; + } + } + return len; + } + + //LOGI("[Shamrock] fake_system_property_get(%s)", name); + return backup_system_property_get(name, value); +} void on_library_loaded(const char *name, void *handle) { - if (std::string(name) == "libc.so") { + auto libraryName = std::string(name); + if (libraryName.ends_with("libc.so") || libraryName.ends_with("libfekit.so")) { void *target = dlsym(handle, "__system_property_get"); - + if (target != nullptr) { + hook_function(target, (void *)fake_system_property_get, (void **) &backup_system_property_get); + } else { + LOGE("[Shamrock] failed to hook __system_property_get"); + } } } +extern "C" [[gnu::visibility("default")]] [[gnu::used]] +jint JNI_OnLoad(JavaVM *jvm, void*) { + JNIHelper::initJavaVM(jvm); + int attach = 0; + JNIEnv *env = JNIHelper::getJNIEnv(&attach); + + // do something + LOGI("[Shamrock] JNI_OnLoad NativeModule Init: %p", env); + + if (attach == 1) { + JNIHelper::delJNIEnv(); + } + + //hook_function((void *)env->functions->FindClass, (void *)fake_FindClass, (void **)&backup_FindClass); + return JNI_VERSION_1_6; +} + extern "C" [[gnu::visibility("default")]] [[gnu::used]] NativeOnModuleLoaded native_init(const NativeAPIEntries *entries) { - + hook_function = entries->hook_func; + LOGI("[Shamrock] LSPosed NativeModule Init: %p", hook_function); return on_library_loaded; +} + +extern "C" +JNIEXPORT jboolean JNICALL +Java_moe_fuqiuluo_shamrock_xposed_XposedEntry_00024Companion_injected(JNIEnv *env, jobject thiz) { + return hook_function != nullptr; +} + +extern "C" +JNIEXPORT jboolean JNICALL +Java_moe_fuqiuluo_shamrock_xposed_XposedEntry_00024Companion_hasEnv(JNIEnv *env, jobject thiz) { + return JNIHelper::global_jvm != nullptr; } \ No newline at end of file diff --git a/xposed/src/main/cpp/jnihelper.h b/xposed/src/main/cpp/jnihelper.h new file mode 100644 index 0000000..a45fef6 --- /dev/null +++ b/xposed/src/main/cpp/jnihelper.h @@ -0,0 +1,40 @@ +#ifndef SHAMROCK_JNIHELPER_H +#define SHAMROCK_JNIHELPER_H + +#include "android/log.h" + +namespace JNIHelper { + static JavaVM *global_jvm = nullptr; + + void initJavaVM(JavaVM *jvm) { + global_jvm = jvm; + } + + JNIEnv *getJNIEnv(int *attach) { + if (global_jvm == NULL) return NULL; + + *attach = 0; + JNIEnv *jni_env = NULL; + + int status = global_jvm->GetEnv((void **)&jni_env, JNI_VERSION_1_6); + + if (status == JNI_EDETACHED || jni_env == NULL) { + status = global_jvm->AttachCurrentThread(&jni_env, NULL); + if (status < 0) { + jni_env = NULL; + } else { + *attach = 1; + } + } + return jni_env; + } + + jint delJNIEnv() { + if (global_jvm == nullptr) return 0; + return global_jvm->DetachCurrentThread(); + } +} + + + +#endif //SHAMROCK_JNIHELPER_H diff --git a/xposed/src/main/cpp/lsposed.h b/xposed/src/main/cpp/lsposed.h index c678d62..2be13b7 100644 --- a/xposed/src/main/cpp/lsposed.h +++ b/xposed/src/main/cpp/lsposed.h @@ -3,6 +3,13 @@ #include "stdint.h" +#define TAG "LSPosed-Bridge" +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) +#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) + typedef int (*HookFunType)(void *func, void *replace, void **backup); typedef int (*UnhookFunType)(void *func); diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/LogCenter.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/LogCenter.kt index 55e8dbc..d82b26a 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/LogCenter.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/LogCenter.kt @@ -51,8 +51,29 @@ internal object LogCenter { private val format = SimpleDateFormat("[HH:mm:ss] ") - fun log(string: String, level: Level = Level.INFO, toast: Boolean = false) = - log({ string }, level, toast) + fun log(string: String, level: Level = Level.INFO, toast: Boolean = false) { + if (!ShamrockConfig.isDebug() && level == Level.DEBUG) { + return + } + + if (toast) { + MobileQQ.getContext().toast(string) + } + // 把日志广播到主进程 + GlobalScope.launch(Dispatchers.Default) { + DataRequester.request("send_message", bodyBuilder = { + put("string", string) + put("level", level.id) + }) + } + + if (!LogFile.exists()) { + LogFile.createNewFile() + } + val format = "%s%s %s\n".format(format.format(Date()), level.name, string) + + LogFile.appendText(format) + } fun log( string: () -> String, diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/XposedEntry.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/XposedEntry.kt index 987e9f2..6753acb 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/XposedEntry.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/XposedEntry.kt @@ -5,6 +5,8 @@ import de.robv.android.xposed.IXposedHookLoadPackage import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.callbacks.XC_LoadPackage import de.robv.android.xposed.XposedBridge.log +import moe.fuqiuluo.shamrock.helper.Level +import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.utils.MMKVFetcher import moe.fuqiuluo.shamrock.xposed.loader.ActionLoader import moe.fuqiuluo.shamrock.xposed.loader.FuckAMS @@ -12,6 +14,7 @@ import moe.fuqiuluo.shamrock.xposed.loader.LuoClassloader import moe.fuqiuluo.shamrock.tools.FuzzySearchClass import moe.fuqiuluo.shamrock.tools.afterHook import moe.fuqiuluo.shamrock.utils.PlatformUtils +import moe.fuqiuluo.shamrock.xposed.loader.NativeLoader import mqq.app.MobileQQ import java.lang.reflect.Field import java.lang.reflect.Modifier @@ -26,6 +29,12 @@ internal class XposedEntry: IXposedHookLoadPackage { companion object { @JvmStatic var sec_static_stage_inited = false + @JvmStatic + var sec_static_nativehook_inited = false + + external fun injected(): Boolean + + external fun hasEnv(): Boolean } private var firstStageInit = false @@ -108,7 +117,6 @@ internal class XposedEntry: IXposedHookLoadPackage { if (sec_static_stage_inited) return val classLoader = ctx.classLoader.also { requireNotNull(it) } - LuoClassloader.hostClassLoader = classLoader if(injectClassloader(XposedEntry::class.java.classLoader)) { @@ -116,12 +124,7 @@ internal class XposedEntry: IXposedHookLoadPackage { System.setProperty("qxbot_flag", "1") } else return - log("Process Name = " + MobileQQ.getMobileQQ().qqProcessName.apply { - // if (!contains("msf", ignoreCase = true)) return // 非MSF进程 退出 - //if (contains("peak")) { - // PlatformUtils.killProcess(ctx, this) - //} - }) + log("Process Name = " + MobileQQ.getMobileQQ().qqProcessName) PlatformUtils.isTim() diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/AntiDetection.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/AntiDetection.kt index 6944aee..6507dc8 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/AntiDetection.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/AntiDetection.kt @@ -6,12 +6,15 @@ import android.content.pm.PackageManager import android.content.pm.VersionedPackage import android.os.Build import de.robv.android.xposed.XC_MethodReplacement +import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedHelpers import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig import moe.fuqiuluo.shamrock.tools.hookMethod +import moe.fuqiuluo.shamrock.xposed.XposedEntry import moe.fuqiuluo.shamrock.xposed.loader.LuoClassloader +import moe.fuqiuluo.shamrock.xposed.loader.NativeLoader /** * 反检测 @@ -19,6 +22,7 @@ import moe.fuqiuluo.shamrock.xposed.loader.LuoClassloader class AntiDetection: IAction { override fun invoke(ctx: Context) { antiFindPackage(ctx) + antiNativeDetection() if (ShamrockConfig.isAntiTrace()) antiTrace() antiMemoryWalking() @@ -35,6 +39,23 @@ class AntiDetection: IAction { return false } + private fun antiNativeDetection() { + try { + //System.loadLibrary("clover") + NativeLoader.load("clover") + val env = XposedEntry.hasEnv() + val injected = XposedEntry.injected() + if (!env || !injected) { + LogCenter.log("[Shamrock] Shamrock反检测启动失败(env=$env, injected=$injected)", Level.ERROR) + } else { + XposedEntry.sec_static_nativehook_inited = true + LogCenter.log("[Shamrock] Shamrock反检测启动成功", Level.INFO) + } + } catch (e: Throwable) { + LogCenter.log("[Shamrock] Shamrock反检测启动失败,请检查LSPosed版本使用大于100: ${e.message}", Level.ERROR) + } + } + private fun antiFindPackage(context: Context) { val packageManager = context.packageManager val applicationInfo = packageManager.getApplicationInfo("moe.fuqiuluo.shamrock", 0) diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/loader/NativeLoader.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/loader/NativeLoader.kt index cb30a11..babb63f 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/loader/NativeLoader.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/loader/NativeLoader.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import de.robv.android.xposed.XposedBridge import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter +import moe.fuqiuluo.shamrock.xposed.XposedEntry import mqq.app.MobileQQ import java.io.File @@ -22,7 +23,7 @@ internal object NativeLoader { @SuppressLint("UnsafeDynamicallyLoadedCode") fun load(name: String) { try { - if (name == "shamrock") { + if (name == "shamrock" || name == "clover") { val context = MobileQQ.getContext() val packageManager = context.packageManager val applicationInfo = packageManager.getApplicationInfo("moe.fuqiuluo.shamrock.hided", 0)