Lsposed Java HOOK原理及检测

当使用 XposedBridge.hookMethod 这个 api对java函数进行hook时:

public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
    if (!(hookMethod instanceof Executable)) {
        throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod);
    } else if (Modifier.isAbstract(hookMethod.getModifiers())) {
        throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod);
    } else if (hookMethod.getDeclaringClass().getClassLoader() == XposedBridge.class.getClassLoader()) {
        throw new IllegalArgumentException("Do not allow hooking inner methods");
    } else if (hookMethod.getDeclaringClass() == Method.class && hookMethod.getName().equals("invoke")) {
        throw new IllegalArgumentException("Cannot hook Method.invoke");
    }
 
 
    if (callback == null) {
        throw new IllegalArgumentException("callback should not be null!");
    }
 
    if (!HookBridge.hookMethod(false, (Executable) hookMethod, LSPosedBridge.NativeHooker.class, callback.priority, callback)) {
        log("Failed to hook " + hookMethod);
        return null;
    }
 
    return callback.new Unhook(hookMethod);
}

会调用  HookBridge.hookMethod 这个内部api,实现在native层:

public class HookBridge {
    public static native boolean hookMethod(boolean useModernApi, Executable hookMethod, Class<?> hooker, int priority, Object callback);
 
 
    public static native boolean unhookMethod(boolean useModernApi, Executable hookMethod, Object callback);
 
    public static native boolean deoptimizeMethod(Executable method);
 
    public static native <T> T allocateObject(Class<T> clazz) throws InstantiationException;
 
    public static native Object invokeOriginalMethod(Executable method, Object thisObject, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
 
    public static native <T> Object invokeSpecialMethod(Executable method, char[] shorty, Class<T> clazz, Object thisObject, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
 
    @FastNative
    public static native boolean instanceOf(Object obj, Class<?> clazz);
 
    @FastNative
    public static native boolean setTrusted(Object cookie);
 
    public static native Object[][] callbackSnapshot(Class<?> hooker_callback, Executable method);
}

native层方法定义如下:

LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, hookMethod, jboolean useModernApi, jobject hookMethod,
                      jclass hooker, jint priority, jobject callback) {
    bool newHook = false;
#ifndef NDEBUG
    struct finally {
        std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
        bool &newHook;
        ~finally() {
            auto finish = std::chrono::steady_clock::now();
            if (newHook) {
                LOGV("New hook took {}us",
                     std::chrono::duration_cast<std::chrono::microseconds>(finish - start).count());
            }
        }
    } finally {
        .newHook = newHook
    };
#endif
    auto target = env->FromReflectedMethod(hookMethod);     //ArtMethod 结构体
    HookItem * hook_item = nullptr;
    hooked_methods.lazy_emplace_l(target, [&hook_item](auto &it) {
        hook_item = it.second.get();
    }, [&hook_item, &target, &newHook](const auto &ctor) {
        auto ptr = std::make_unique<HookItem>();
        hook_item = ptr.get();
        ctor(target, std::move(ptr));
        newHook = true;
    });
    if (newHook) {
        auto init = env->GetMethodID(hooker, "<init>", "(Ljava/lang/reflect/Executable;)V");
        auto callback_method = env->ToReflectedMethod(hooker, env->GetMethodID(hooker, "callback",   //LSPosedBridge.NativeHooker
                                                                               "([Ljava/lang/Object;)Ljava/lang/Object;"),
                                                      false);
        auto hooker_object = env->NewObject(hooker, init, hookMethod);  //新建 NativeHooker 对象,传入 目标hookMethod
        hook_item->SetBackup(lsplant::Hook(env, hookMethod, hooker_object, callback_method)); //传入的目标method 和 NativeHooker对象
        env->DeleteLocalRef(hooker_object);
    }
    jobject backup = hook_item->GetBackup();
    if (!backup) return JNI_FALSE;
    JNIMonitor monitor(env, backup);
    if (useModernApi) {
        if (before_method_field == nullptr) {
            auto callback_class = JNI_GetObjectClass(env, callback);
            callback_ctor = JNI_GetMethodID(env, callback_class, "<init>", "(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V");
            before_method_field = JNI_GetFieldID(env, callback_class, "beforeInvocation", "Ljava/lang/reflect/Method;");
            after_method_field = JNI_GetFieldID(env, callback_class, "afterInvocation", "Ljava/lang/reflect/Method;");
        }
        auto before_method = JNI_GetObjectField(env, callback, before_method_field);
        auto after_method = JNI_GetObjectField(env, callback, after_method_field);
        auto callback_type = ModuleCallback {
                .before_method = env->FromReflectedMethod(before_method),
                .after_method = env->FromReflectedMethod(after_method),
        };
        hook_item->modern_callbacks.emplace(priority, callback_type);
    } else {
        hook_item->legacy_callbacks.emplace(priority, env->NewGlobalRef(callback));
    }
    return JNI_TRUE;
}

关键处是调用  lsplant::Hook(env, hookMethod, hooker_object, callback_method) 这个接口进行HOOK操作。

[[maybe_unused]] jobject Hook(JNIEnv *env, jobject target_method, jobject hooker_object,   //lsplant 处理JAVA函数hook的地方
                              jobject callback_method) {
    if (!target_method || !JNI_IsInstanceOf(env, target_method, executable)) {
        LOGE("target method is not an executable");
        return nullptr;
    }
    if (!callback_method || !JNI_IsInstanceOf(env, callback_method, executable)) {
        LOGE("callback method is not an executable");
        return nullptr;
    }
 
 
    jmethodID hook_method = nullptr;
    jmethodID backup_method = nullptr;
    jfieldID hooker_field = nullptr;
 
    auto target_class =
        JNI_Cast<jclass>(JNI_CallObjectMethod(env, target_method, method_get_declaring_class)); //获取target方法的申明类
    constexpr static uint32_t kAccClassIsProxy = 0x00040000;
    bool is_proxy = JNI_GetIntField(env, target_class, class_access_flags) & kAccClassIsProxy;
    auto *target = ArtMethod::FromReflectedMethod(env, target_method);  // ArtMethod 结构体
    bool is_static = target->IsStatic();
 
    if (IsHooked(target, true)) {   //从 sharedHashMap缓存中查找
        LOGW("Skip duplicate hook");
        return nullptr;
    }
 
    ScopedLocalRef<jclass> built_class{env};
    {
        auto callback_name =
            JNI_Cast<jstring>(JNI_CallObjectMethod(env, callback_method, method_get_name));
        JUTFString callback_method_name(callback_name);
        auto target_name =
            JNI_Cast<jstring>(JNI_CallObjectMethod(env, target_method, method_get_name));
        JUTFString target_method_name(target_name);
        auto callback_class = JNI_Cast<jclass>(
            JNI_CallObjectMethod(env, callback_method, method_get_declaring_class));
        auto callback_class_loader =
            JNI_CallObjectMethod(env, callback_class, class_get_class_loader);
        auto callback_class_name =
            JNI_Cast<jstring>(JNI_CallObjectMethod(env, callback_class, class_get_name));
        JUTFString class_name(callback_class_name);
        if (!JNI_IsInstanceOf(env, hooker_object, callback_class)) {
            LOGE("callback_method is not a method of hooker_object");
            return nullptr;
        }
        std::tie(built_class, hooker_field, hook_method, backup_method) = WrapScope(
            env,
            BuildDex(env, callback_class_loader,
                     __builtin_expect(is_proxy, 0) ? GetProxyMethodShorty(env, target_method)
                                                   : ArtMethod::GetMethodShorty(env, target_method),
                     is_static, target->IsConstructor() ? "constructor" : target_method_name.get(),
                     class_name.get(), callback_method_name.get()));
        if (!built_class || !hooker_field || !hook_method || !backup_method) {
            LOGE("Failed to generate hooker");
            return nullptr;
        }
    }
 
    auto reflected_hook = JNI_ToReflectedMethod(env, built_class, hook_method, is_static);
    auto reflected_backup = JNI_ToReflectedMethod(env, built_class, backup_method, is_static);
 
    JNI_CallVoidMethod(env, reflected_backup, set_accessible, JNI_TRUE);
 
    auto *hook = ArtMethod::FromReflectedMethod(env, reflected_hook);
    auto *backup = ArtMethod::FromReflectedMethod(env, reflected_backup);
 
    JNI_SetStaticObjectField(env, built_class, hooker_field, hooker_object);
 
    if (DoHook(target, hook, backup)) {
        std::apply(
            [backup_method, target_method_id = env->FromReflectedMethod(target_method)](auto... v) {
                ((*v == target_method_id &&
                  (LOGD("Propagate internal used method because of hook"), *v = backup_method)) ||
                 ...);
            },
            kInternalMethods);
        jobject global_backup = JNI_NewGlobalRef(env, reflected_backup);
        RecordHooked(target, target->GetDeclaringClass()->GetClassDef(), global_backup, backup);
        if (!is_proxy) [[likely]] {
            RecordJitMovement(target, backup);
        }
        // Always record backup as deoptimized since we dont want its entrypoint to be updated
        // by FixupStaticTrampolines on hooker class
        // Used hook's declaring class here since backup's is no longer the same with hook's
        RecordDeoptimized(hook->GetDeclaringClass()->GetClassDef(), backup);
        return global_backup;
    }
 
    return nullptr;
}

这个函数先使用  BuildDex 接口动态生成HOOK类和方法,然后创建ClassLoader加载这个DEX,详情可以分析 BuildDex 内部,会创建 LSPHooker_ 类名和hook的方法名。然后调用 DoHook来HOOK函数:

bool DoHook(ArtMethod *target, ArtMethod *hook, ArtMethod *backup) {
    ScopedGCCriticalSection section(art::Thread::Current(), art::gc::kGcCauseDebugger,
                                    art::gc::kCollectorTypeDebugger);
    ScopedSuspendAll suspend("LSPlant Hook", false);
    LOGV("Hooking: target = %s(%p), hook = %s(%p), backup = %s(%p)", target->PrettyMethod().c_str(),
         target, hook->PrettyMethod().c_str(), hook, backup->PrettyMethod().c_str(), backup);
 
 
    if (auto *entrypoint = GenerateTrampolineFor(hook); !entrypoint) {       // 获取 hook方法的 entrypoint
        LOGE("Failed to generate trampoline");
        return false;
        // NOLINTNEXTLINE
    } else {
        LOGV("Generated trampoline %p", entrypoint);
 
        target->SetNonCompilable();                  //设置不编译jit
        hook->SetNonCompilable();
 
        // copy after setNonCompilable
        backup->CopyFrom(target);
 
        target->ClearFastInterpretFlag();            //设置 flag
 
        target->SetEntryPoint(entrypoint);           //替换 entrypoint
 
        if (!backup->IsStatic()) backup->SetPrivate();
 
        LOGV("Done hook: target(%p:0x%x) -> %p; backup(%p:0x%x) -> %p; hook(%p:0x%x) -> %p", target,
             target->GetAccessFlags(), target->GetEntryPoint(), backup, backup->GetAccessFlags(),
             backup->GetEntryPoint(), hook, hook->GetAccessFlags(), hook->GetEntryPoint());
 
        return true;
    }
}

所以 HOOK的核心是 替换方法的  entrypoint,那么接着看 替换的  entrypoint 是怎么生成的:

void *GenerateTrampolineFor(art::ArtMethod *hook) {
    unsigned count;
    uintptr_t address;
    while (true) {
        auto tl = Trampoline{.address = trampoline_pool.fetch_add(1, std::memory_order_release)};
        count = tl.count;
        address = tl.address & ~kAddressMask;
        if (address == 0 || count >= kTrampolineNumPerPage) {
            if (trampoline_lock.test_and_set(std::memory_order_acq_rel)) {
                trampoline_lock.wait(true, std::memory_order_acquire);
                continue;
            }
            address = reinterpret_cast<uintptr_t>(mmap(nullptr, kPageSize,
                                                       PROT_READ | PROT_WRITE | PROT_EXEC,
                                                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
            if (address == reinterpret_cast<uintptr_t>(MAP_FAILED)) {
                PLOGE("mmap trampoline");
                trampoline_lock.clear(std::memory_order_release);
                trampoline_lock.notify_all();
                return nullptr;
            }
            count = 0;
            tl.address = address;
            tl.count = count + 1;
            trampoline_pool.store(tl.address, std::memory_order_release);
            trampoline_lock.clear(std::memory_order_release);
            trampoline_lock.notify_all();
        }
        LOGV("trampoline: count = %u, address = %zx, target = %zx", count, address,
             address + count * kTrampolineSize);
        address = address + count * kTrampolineSize;
        break;
    }
    auto *address_ptr = reinterpret_cast<char *>(address);
    std::memcpy(address_ptr, trampoline.data(), trampoline.size());  //拷贝前3条指令,用于跳转,  接着后面应该是一个 trampoline 跳转地址!,里面保存了跳转地址。
 
 
    *reinterpret_cast<art::ArtMethod **>(address_ptr + art_method_offset) = hook;
 
    __builtin___clear_cache(address_ptr, reinterpret_cast<char *>(address + trampoline.size()));
 
    return address_ptr;
}

实际上就是创建了一个内存映射,生成3个跳板指令,第4个指令保存HOOK方法的 ArtMethod结构体指针,trampoline生成代码:

consteval inline auto GetTrampoline() {
    if constexpr (kArch == Arch::kArm) {
        return std::make_tuple("\x00\x00\x9f\xe5\x00\xf0\x90\xe5\x78\x56\x34\x12"_uarr,
                               // NOLINTNEXTLINE
                               uint8_t{32u}, uintptr_t{8u});
    }
    if constexpr (kArch == Arch::kArm64) {
        return std::make_tuple(
            "\x60\x00\x00\x58\x10\x00\x40\xf8\x00\x02\x1f\xd6\x78\x56\x34\x12\x78\x56\x34\x12"_uarr,
            // NOLINTNEXTLINE
            uint8_t{44u}, uintptr_t{12u});
    }
    if constexpr (kArch == Arch::kX86) {
        return std::make_tuple("\xb8\x78\x56\x34\x12\xff\x70\x00\xc3"_uarr,
                               // NOLINTNEXTLINE
                               uint8_t{56u}, uintptr_t{1u});
    }
    if constexpr (kArch == Arch::kX86_64) {
        return std::make_tuple("\x48\xbf\x78\x56\x34\x12\x78\x56\x34\x12\xff\x77\x00\xc3"_uarr,
                               // NOLINTNEXTLINE
                               uint8_t{96u}, uintptr_t{2u});
    }
    if constexpr (kArch == Arch::kRiscv64) {
        return std::make_tuple(
            "\x17\x05\x00\x00\x03\x35\xc5\x00\x67\x00\x05\x00\x78\x56\x34\x12\x78\x56\x34\x12"_uarr,
            // NOLINTNEXTLINE
            uint8_t{84u}, uintptr_t{12u});
    }
}
 
 
auto [trampoline, entry_point_offset, art_method_offset] = GetTrampoline();

上述根据不同平台生成了不同的跳板指令。

测试HOOK前和HOOK后 系统函数  Thread.dispatchUncaughtException 的ArtMethod 结构体中 entrypoint地址指向:

1、HOOk前:

指向了libart.so这个art虚拟机so

2、HOOK后

 指向了一段匿名可读可写可执行内存,长度为0x1000,这个是lsposed映射的一段内存作为跳板地址。

接着dump出内存查看前3个指令:

从当前指令 + 0xC处,取地址保存到 X0,根据前面的分析,这是HOOk方法的 ArtMethod结构体指针,接着取出 ArtMethod + 0x20处的值,这个值正是HOOk方法的ArtMethod对应的 entrypoint。最后跳转到这个entrypoint执行。

Lsposed检测

首先Lsposed在初始化时,会HOOK一些系统函数:

public class Startup {
    private static void startBootstrapHook(boolean isSystem) {
        Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
        LSPosedHelper.hookMethod(CrashDumpHooker.class, Thread.class, "dispatchUncaughtException", Throwable.class);
        if (isSystem) {
            LSPosedHelper.hookAllMethods(HandleSystemServerProcessHooker.class, ZygoteInit.class, "handleSystemServerProcess");
        } else {
            LSPosedHelper.hookAllMethods(OpenDexFileHooker.class, DexFile.class, "openDexFile");
            LSPosedHelper.hookAllMethods(OpenDexFileHooker.class, DexFile.class, "openInMemoryDexFile");
            LSPosedHelper.hookAllMethods(OpenDexFileHooker.class, DexFile.class, "openInMemoryDexFiles");
        }
        LSPosedHelper.hookConstructor(LoadedApkCtorHooker.class, LoadedApk.class,
                ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class,
                ClassLoader.class, boolean.class, boolean.class, boolean.class);
        LSPosedHelper.hookMethod(LoadedApkCreateCLHooker.class, LoadedApk.class, "createOrUpdateClassLoaderLocked", List.class);
        LSPosedHelper.hookAllMethods(AttachHooker.class, ActivityThread.class, "attach");
    }
 
 
    public static void bootstrapXposed() {
        // Initialize the Xposed framework
        try {
            startBootstrapHook(XposedInit.startsSystemServer);
            XposedInit.loadLegacyModules();
        } catch (Throwable t) {
            Utils.logE("error during Xposed initialization", t);
        }
    }
 
    public static void initXposed(boolean isSystem, String processName, String appDir, ILSPApplicationService service) {
        // init logger
        ApplicationServiceClient.Init(service, processName);
        XposedBridge.initXResources();
        XposedInit.startsSystemServer = isSystem;
        LSPosedContext.isSystemServer = isSystem;
        LSPosedContext.appDir = appDir;
        LSPosedContext.processName = processName;
        PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
    }
}

这里检测  dispatchUncaughtException 函数是否被HOOK:

/*
    ldr x0, #0xc
    ldur x16, [x0, #0x20]
    br x16
 */
 
static bool checkLsposedHeader(uintptr_t func_addr)
{
    //ldr 58000060
    unsigned int ins1 = *(unsigned int*)func_addr;
    unsigned int op = (ins1 & 0xbf000000) >> 24;
    if(op != 0x18)return false;  //ldr指令
 
    unsigned int imme = ((ins1 >> 5) << 2) & 0x1fffff;  //常量,应该是 0xC
    unsigned int rn1 = ins1 & 0x1f;                     //寄存器,X0
    if(imme != 0xC) return false;
 
    // LDUR 指令 F8420001  111 1100 0010
    unsigned int ins2 = *(unsigned int*)(func_addr + sizeof(int));
    unsigned int op2 = (ins2 >> 21);
    unsigned int Xd = ins2 & 0x1f;  //目标寄存器
    unsigned int Xn = (ins2 >> 5) & 0x1f;  //源寄存器
    unsigned int imme2 = (ins2 >> 12) & 0x1FF;   //9位带符号数,这里不处理符号,对应ArtMethod中entry_point偏移
    if(op2 != 0x7C2) return false;
 
    //br 指令
    unsigned int ins3 = *(unsigned int*)(func_addr + sizeof(int)*2);
    unsigned int op3 = ins3 & 0xfffffc1f;            //br指令,应该是 0xd61f0000
    unsigned int rn3 = (ins3 >> 5) & 0x1f;           // rn3 应该是X16
    if(op3 != 0xd61f0000) return false;
 
    return Xd == rn3 && Xn == rn1;
}
 
bool checkLsPosedHook(JNIEnv* env) {
 
    auto thread = env->FindClass("java/lang/Thread");
    if (!thread) {
        __android_log_print(ANDROID_LOG_INFO, "native-android", "Failed to found Executable");
        return false;
    }
    auto clazz = env->FindClass("java/lang/Class");
    jmethodID get_declared_constructors = env->GetMethodID(clazz, "getDeclaredConstructors",
                                                           "()[Ljava/lang/reflect/Constructor;");
 
    //获取相连连个构造函数,计算得到ArtMethod结构体大小,这里可以省略
    auto constructors = static_cast<jobjectArray>(env->CallObjectMethod(thread,
                                                                                get_declared_constructors));
    jsize length = env->GetArrayLength(constructors);
    if (length < 2) {
        __android_log_print(ANDROID_LOG_INFO, "native-android", "Throwable has less than 2 constructors");
        return false;
    }
    jobject first_ctor = env->GetObjectArrayElement(constructors, 0);
    jobject second_ctor = env->GetObjectArrayElement(constructors, 1);
    //反射得到 ArtMethod指针
    jmethodID first = env->FromReflectedMethod(first_ctor);
    jmethodID second = env->FromReflectedMethod(second_ctor);
    //计算大小
    size_t art_method_size = reinterpret_cast<uintptr_t>(second) - reinterpret_cast<uintptr_t>(first);
 
    __android_log_print(ANDROID_LOG_INFO, "native-android", "ArtMethod size: %zu", art_method_size);
 
    // entry_point 指针是结构体最后一个元素
    size_t entry_point_offset = art_method_size - sizeof(void*);
    size_t data_offset = entry_point_offset - sizeof(void*);
 
    //获取待检测的方法
    //待检测的方法 Thread.dispatchUncaughtException Thread.class.getDeclaredMethod("dispatchUncaughtException", Throwable.class);
    jmethodID get_declared_method = env->GetMethodID(clazz, "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
    auto throwable = env->FindClass("java/lang/Throwable");
    jstring dispatchUncaughtException = env->NewStringUTF("dispatchUncaughtException");
    jobjectArray paramTypes = env->NewObjectArray(1, env->FindClass("java/lang/Class"), nullptr);
    env->SetObjectArrayElement(paramTypes, 0, throwable);
    jobject m = env->CallObjectMethod(thread, get_declared_method, dispatchUncaughtException, paramTypes);
 
    jmethodID art_method = env->FromReflectedMethod(m); //需要确定这个是指针还是下标
    __android_log_print(ANDROID_LOG_INFO, "native-android", "art_method: %02llx", (long long)art_method);
    //输出 entry_point
    auto* p = (long long *)(((char *)art_method) + entry_point_offset);
    __android_log_print(ANDROID_LOG_INFO, "native-android", "offset %d = %02llx\n" , entry_point_offset, *p);
 
    //检测 entry_point 指向的地址是否是跳板
    bool hooked = checkLsposedHeader(*p);  //检测地址的前三个指令是否有inlinehook
    if(hooked) {
        //有跳板特征,可以进一步提取hook函数
        auto hook = (jmethodID)*(long long *)((*p) + 12); //这个取到hook函数的ArtMethod
        jobject hookMethod = env->ToReflectedMethod(thread, hook, false);
 
        jclass methodClass = env->FindClass("java/lang/reflect/Method");
        jmethodID getDeclaringClassMethod = env->GetMethodID(methodClass, "getDeclaringClass", "()Ljava/lang/Class;");
        jobject declaringClass = env->CallObjectMethod(hookMethod, getDeclaringClassMethod);
        jmethodID getClassNameMethod = env->GetMethodID(clazz, "getName", "()Ljava/lang/String;");
        auto className = (jstring) env->CallObjectMethod(declaringClass, getClassNameMethod);
        //应该是 LSPHooker_
        char* lspHookClassName = const_cast<char *>(env->GetStringUTFChars(className, nullptr));
        __android_log_print(ANDROID_LOG_INFO, "native-android", "lspHookClassName = %s\n" , lspHookClassName);
        env->ReleaseStringUTFChars(className, lspHookClassName);
        env->DeleteLocalRef(methodClass);
    }
 
    env->DeleteLocalRef(dispatchUncaughtException);
    env->DeleteLocalRef(throwable);
    env->DeleteLocalRef(thread);
    env->DeleteLocalRef(clazz);
    env->DeleteLocalRef(paramTypes);
 
    return hooked;
}

 打印日志如下:

成功获取到 hook方法的ArtMethod结构体,并且获取到 HOOK方法的类 LSPHooker_ 。通过这个类可以获取到对应的 ClassLoader,从而可以找到Xposed的接口口  de.robv.android.xposed.XposedHelpers。

### LSPosed Hook 工作原理及实现机制 LSPosed 是基于 Xposed 框架的一个轻量级替代方案,其核心功能之一是对 Android 应用中的方法进行动态修改或增强(即 Hook)。以下是关于 LSPosed Hook 的工作原理及其具体实现机制的详细介绍。 #### 方法地址替换与 Native Hook LSPosed 使用 **Native Hook 技术**来拦截目标函数并注入自定义逻辑。通过获取目标方法的内存地址,并将其替换为一个新的实现地址,从而使得每次调用该方法时都会跳转到新的实现中[^1]。这种技术允许开发者在不改变原始应用代码的情况下,插入额外的功能或者修改行为。 #### 动态代理模式下的 MethodHooking 当需要 hook Java 层面上的方法时,通常会采用一种类似于动态代理的方式。具体来说,就是先找到待 hook 方法的实际执行入口点——也就是它的机器码位置;接着在此基础上构建一个全新的复合型处理流程,这个新流程能够依次完成如下操作: - 执行用户指定的前置回调 (before); - 调用被取代掉的真实业务逻辑; - 运行用户的后置处理器 (after)[^2]。 这样的设计不仅简化了复杂场景下多阶段干预的需求满足过程,同时也提高了整体架构灵活性和可维护性水平。 #### 安全防护规避策略 值得注意的是,在实际部署过程中还需要考虑如何绕过某些应用程序内置的安全检查措施。例如阿里巴巴旗下的若干知名软件可能会主动扫描加载进来的共享库文件名称是否存在敏感关键词如 "hack", "inject" 或者其他暗示非法篡改意图字符串的情况发生 [^3] 。因此为了确保兼容性和稳定性,LSPosed 可能采取了一些特别手段比如重命名资源、混淆字节码等方式隐藏自身特征以逃避上述类型的探测动作. ```java // 示例伪代码展示了一个简单的Java层Method Hook实现方式 public class MyHook { public static void before() { /* 用户定义的前置逻辑 */ } public static void after(Object result) throws Throwable { /* 后置逻辑*/ } @Override protected Object invokeOriginalMethod(Method method, Object... args)throws Throwable{ return method.invoke(null,args); // 调用原始方法 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值