upgrade to ndk 29 an use module partition

This commit is contained in:
LoveSy 2025-03-06 11:37:19 +08:00
parent e2a35a4fab
commit 0110cf7e6e
18 changed files with 193 additions and 231 deletions

View File

@ -6,5 +6,5 @@ val androidTargetSdkVersion by extra(35)
val androidMinSdkVersion by extra(21) val androidMinSdkVersion by extra(21)
val androidBuildToolsVersion by extra("35.0.0") val androidBuildToolsVersion by extra("35.0.0")
val androidCompileSdkVersion by extra(35) val androidCompileSdkVersion by extra(35)
val androidNdkVersion by extra("28.0.13004108") val androidNdkVersion by extra("29.0.13113456")
val androidCmakeVersion by extra("3.28.0+") val androidCmakeVersion by extra("3.28.0+")

View File

@ -4,17 +4,17 @@ module;
#include "logging.hpp" #include "logging.hpp"
export module clazz; export module lsplant:clazz;
import common; import :common;
import art_method; import :art_method;
import thread; import :thread;
import handle; import :handle;
import hook_helper; import hook_helper;
namespace lsplant::art::mirror { export namespace lsplant::art::mirror {
export class Class { class Class {
private: private:
inline static auto GetDescriptor_ = inline static auto GetDescriptor_ =
"_ZN3art6mirror5Class13GetDescriptorEPNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE"_sym.as<const char *(Class::*)(std::string *)>; "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE"_sym.as<const char *(Class::*)(std::string *)>;

View File

@ -6,17 +6,13 @@ module;
#include "logging.hpp" #include "logging.hpp"
export module art_method; export module lsplant:art_method;
import common; import :common;
import hook_helper; import hook_helper;
namespace lsplant::art { export namespace lsplant::art {
namespace mirror { class ArtMethod {
class Class;
}
export class ArtMethod {
inline static auto PrettyMethod_ = inline static auto PrettyMethod_ =
"_ZN3art9ArtMethod12PrettyMethodEPS0_b"_sym.as<std::string(ArtMethod::*)(bool)>; "_ZN3art9ArtMethod12PrettyMethodEPS0_b"_sym.as<std::string(ArtMethod::*)(bool)>;

View File

@ -4,15 +4,15 @@ module;
#include "logging.hpp" #include "logging.hpp"
export module class_linker; export module lsplant:class_linker;
import art_method; import :art_method;
import thread; import :thread;
import common; import :common;
import clazz; import :clazz;
import handle; import :handle;
import :runtime;
import hook_helper; import hook_helper;
import runtime;
namespace lsplant::art { namespace lsplant::art {
export class ClassLinker { export class ClassLinker {

View File

@ -6,9 +6,9 @@ module;
#include "logging.hpp" #include "logging.hpp"
export module dex_file; export module lsplant:dex_file;
import common; import :common;
import hook_helper; import hook_helper;
namespace lsplant::art { namespace lsplant::art {

View File

@ -2,10 +2,10 @@ module;
#include <android/api-level.h> #include <android/api-level.h>
export module scope_gc_critical_section; export module lsplant:scope_gc_critical_section;
import thread; import :thread;
import common; import :common;
import hook_helper; import hook_helper;
namespace lsplant::art::gc { namespace lsplant::art::gc {

View File

@ -3,9 +3,9 @@ module;
#include <cstdint> #include <cstdint>
#include <type_traits> #include <type_traits>
export module handle; export module lsplant:handle;
import art_method; import :art_method;
namespace lsplant::art { namespace lsplant::art {

View File

@ -2,10 +2,10 @@ module;
#include "logging.hpp" #include "logging.hpp"
export module instrumentation; export module lsplant:instrumentation;
import art_method; import :art_method;
import common; import :common;
import hook_helper; import hook_helper;
namespace lsplant::art { namespace lsplant::art {

View File

@ -2,11 +2,11 @@ module;
#include "logging.hpp" #include "logging.hpp"
export module jit; export module lsplant:jit;
import art_method; import :art_method;
import common; import :common;
import thread; import :thread;
import hook_helper; import hook_helper;
namespace lsplant::art::jit { namespace lsplant::art::jit {

View File

@ -2,11 +2,11 @@ module;
#include "logging.hpp" #include "logging.hpp"
export module jit_code_cache; export module lsplant:jit_code_cache;
import art_method; import :art_method;
import common; import :common;
import thread; import :thread;
import hook_helper; import hook_helper;
namespace lsplant::art::jit { namespace lsplant::art::jit {

View File

@ -2,11 +2,11 @@ module;
#include "logging.hpp" #include "logging.hpp"
export module jni_id_manager; export module lsplant:jni_id_manager;
import art_method; import :art_method;
import common; import :common;
import handle; import :handle;
import hook_helper; import hook_helper;
namespace lsplant::art::jni { namespace lsplant::art::jni {

View File

@ -5,9 +5,9 @@ module;
#include "logging.hpp" #include "logging.hpp"
export module runtime; export module lsplant:runtime;
import common; import :common;
import hook_helper; import hook_helper;
namespace lsplant::art { namespace lsplant::art {

View File

@ -1,6 +1,6 @@
module; module;
export module thread; export module lsplant:thread;
import hook_helper; import hook_helper;

View File

@ -1,6 +1,6 @@
module; module;
export module thread_list; export module lsplant:thread_list;
import hook_helper; import hook_helper;

View File

@ -9,12 +9,12 @@ module;
#include <string_view> #include <string_view>
#include "logging.hpp" #include "logging.hpp"
#include "utils/jni_helper.hpp"
export module common; export module lsplant:common;
export import jni_helper; export import jni_helper;
export import hook_helper;
namespace lsplant { export namespace lsplant {
namespace art { namespace art {
class ArtMethod; class ArtMethod;
@ -22,12 +22,12 @@ namespace mirror {
class Class; class Class;
} }
namespace dex { namespace dex {
export class ClassDef {}; class ClassDef {};
} // namespace dex } // namespace dex
} // namespace art } // namespace art
export enum class Arch { enum class Arch {
kArm, kArm,
kArm64, kArm64,
kX86, kX86,
@ -61,119 +61,117 @@ template <class T, class Hash = phmap::priv::hash_default_hash<T>,
size_t N = 4> size_t N = 4>
using SharedHashSet = phmap::parallel_flat_hash_set<T, Hash, Eq, Alloc, N, std::shared_mutex>; using SharedHashSet = phmap::parallel_flat_hash_set<T, Hash, Eq, Alloc, N, std::shared_mutex>;
export { constexpr auto kArch = GetArch();
constexpr auto kArch = GetArch();
template <typename T> template <typename T>
constexpr inline auto RoundUpTo(T v, size_t size) { constexpr inline auto RoundUpTo(T v, size_t size) {
return v + size - 1 - ((v + size - 1) & (size - 1)); return v + size - 1 - ((v + size - 1) & (size - 1));
} }
[[gnu::const]] inline auto GetAndroidApiLevel() { [[gnu::const]] inline auto GetAndroidApiLevel() {
static auto kApiLevel = []() { static auto kApiLevel = []() {
std::array<char, PROP_VALUE_MAX> prop_value; std::array<char, PROP_VALUE_MAX> prop_value;
__system_property_get("ro.build.version.sdk", prop_value.data()); __system_property_get("ro.build.version.sdk", prop_value.data());
int base = atoi(prop_value.data()); int base = atoi(prop_value.data());
__system_property_get("ro.build.version.preview_sdk", prop_value.data()); __system_property_get("ro.build.version.preview_sdk", prop_value.data());
return base + atoi(prop_value.data()); return base + atoi(prop_value.data());
}(); }();
return kApiLevel; return kApiLevel;
} }
inline auto IsJavaDebuggable(JNIEnv * env) { inline auto IsJavaDebuggable(JNIEnv * env) {
static auto kDebuggable = [&env]() { static auto kDebuggable = [&env]() {
auto sdk_int = GetAndroidApiLevel(); auto sdk_int = GetAndroidApiLevel();
if (sdk_int < __ANDROID_API_P__) { if (sdk_int < __ANDROID_API_P__) {
return false; return false;
} }
auto runtime_class = JNI_FindClass(env, "dalvik/system/VMRuntime"); auto runtime_class = JNI_FindClass(env, "dalvik/system/VMRuntime");
if (!runtime_class) { if (!runtime_class) {
LOGE("Failed to find VMRuntime"); LOGE("Failed to find VMRuntime");
return false; return false;
} }
auto get_runtime_method = JNI_GetStaticMethodID(env, runtime_class, "getRuntime", auto get_runtime_method = JNI_GetStaticMethodID(env, runtime_class, "getRuntime",
"()Ldalvik/system/VMRuntime;"); "()Ldalvik/system/VMRuntime;");
if (!get_runtime_method) { if (!get_runtime_method) {
LOGE("Failed to find VMRuntime.getRuntime()"); LOGE("Failed to find VMRuntime.getRuntime()");
return false; return false;
} }
auto is_debuggable_method = auto is_debuggable_method =
JNI_GetMethodID(env, runtime_class, "isJavaDebuggable", "()Z"); JNI_GetMethodID(env, runtime_class, "isJavaDebuggable", "()Z");
if (!is_debuggable_method) { if (!is_debuggable_method) {
LOGE("Failed to find VMRuntime.isJavaDebuggable()"); LOGE("Failed to find VMRuntime.isJavaDebuggable()");
return false; return false;
} }
auto runtime = JNI_CallStaticObjectMethod(env, runtime_class, get_runtime_method); auto runtime = JNI_CallStaticObjectMethod(env, runtime_class, get_runtime_method);
if (!runtime) { if (!runtime) {
LOGE("Failed to get VMRuntime"); LOGE("Failed to get VMRuntime");
return false; return false;
} }
bool is_debuggable = JNI_CallBooleanMethod(env, runtime, is_debuggable_method); bool is_debuggable = JNI_CallBooleanMethod(env, runtime, is_debuggable_method);
LOGD("java runtime debuggable %s", is_debuggable ? "true" : "false"); LOGD("java runtime debuggable %s", is_debuggable ? "true" : "false");
return is_debuggable; return is_debuggable;
}(); }();
return kDebuggable; return kDebuggable;
} }
constexpr auto kPointerSize = sizeof(void *); constexpr auto kPointerSize = sizeof(void *);
SharedHashMap<art::ArtMethod *, std::pair<jobject, art::ArtMethod *>> hooked_methods_; SharedHashMap<art::ArtMethod *, std::pair<jobject, art::ArtMethod *>> hooked_methods_;
SharedHashMap<const art::dex::ClassDef *, phmap::flat_hash_set<art::ArtMethod *>> SharedHashMap<const art::dex::ClassDef *, phmap::flat_hash_set<art::ArtMethod *>>
hooked_classes_; hooked_classes_;
SharedHashSet<art::ArtMethod *> deoptimized_methods_set_; SharedHashSet<art::ArtMethod *> deoptimized_methods_set_;
SharedHashMap<const art::dex::ClassDef *, phmap::flat_hash_set<art::ArtMethod *>> SharedHashMap<const art::dex::ClassDef *, phmap::flat_hash_set<art::ArtMethod *>>
deoptimized_classes_; deoptimized_classes_;
std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> jit_movements_; std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> jit_movements_;
std::shared_mutex jit_movements_lock_; std::shared_mutex jit_movements_lock_;
inline art::ArtMethod *IsHooked(art::ArtMethod * art_method, bool including_backup = false) { inline art::ArtMethod *IsHooked(art::ArtMethod * art_method, bool including_backup = false) {
art::ArtMethod *backup = nullptr; art::ArtMethod *backup = nullptr;
hooked_methods_.if_contains(art_method, [&backup, &including_backup](const auto &it) { hooked_methods_.if_contains(art_method, [&backup, &including_backup](const auto &it) {
if (including_backup || it.second.first) backup = it.second.second; if (including_backup || it.second.first) backup = it.second.second;
});
return backup;
}
inline art::ArtMethod *IsBackup(art::ArtMethod * art_method) {
art::ArtMethod *backup = nullptr;
hooked_methods_.if_contains(art_method, [&backup](const auto &it) {
if (!it.second.first) backup = it.second.second;
});
return backup;
}
inline bool IsDeoptimized(art::ArtMethod * art_method) {
return deoptimized_methods_set_.contains(art_method);
}
inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> GetJitMovements() {
std::unique_lock lk(jit_movements_lock_);
return std::move(jit_movements_);
}
inline void RecordHooked(art::ArtMethod * target, const art::dex::ClassDef *class_def,
jobject reflected_backup, art::ArtMethod *backup) {
hooked_classes_.lazy_emplace_l(
class_def, [&target](auto &it) { it.second.emplace(target); },
[&class_def, &target](const auto &ctor) {
ctor(class_def, phmap::flat_hash_set<art::ArtMethod *>{target});
}); });
return backup; hooked_methods_.insert({std::make_pair(target, std::make_pair(reflected_backup, backup)),
} std::make_pair(backup, std::make_pair(nullptr, target))});
}
inline art::ArtMethod *IsBackup(art::ArtMethod * art_method) { inline void RecordDeoptimized(const art::dex::ClassDef *class_def, art::ArtMethod *art_method) {
art::ArtMethod *backup = nullptr; { deoptimized_classes_[class_def].emplace(art_method); }
hooked_methods_.if_contains(art_method, [&backup](const auto &it) { deoptimized_methods_set_.insert(art_method);
if (!it.second.first) backup = it.second.second; }
});
return backup;
}
inline bool IsDeoptimized(art::ArtMethod * art_method) { inline void RecordJitMovement(art::ArtMethod * target, art::ArtMethod * backup) {
return deoptimized_methods_set_.contains(art_method); std::unique_lock lk(jit_movements_lock_);
} jit_movements_.emplace_back(target, backup);
inline std::list<std::pair<art::ArtMethod *, art::ArtMethod *>> GetJitMovements() {
std::unique_lock lk(jit_movements_lock_);
return std::move(jit_movements_);
}
inline void RecordHooked(art::ArtMethod * target, const art::dex::ClassDef *class_def,
jobject reflected_backup, art::ArtMethod *backup) {
hooked_classes_.lazy_emplace_l(
class_def, [&target](auto &it) { it.second.emplace(target); },
[&class_def, &target](const auto &ctor) {
ctor(class_def, phmap::flat_hash_set<art::ArtMethod *>{target});
});
hooked_methods_.insert({std::make_pair(target, std::make_pair(reflected_backup, backup)),
std::make_pair(backup, std::make_pair(nullptr, target))});
}
inline void RecordDeoptimized(const art::dex::ClassDef *class_def, art::ArtMethod *art_method) {
{ deoptimized_classes_[class_def].emplace(art_method); }
deoptimized_methods_set_.insert(art_method);
}
inline void RecordJitMovement(art::ArtMethod * target, art::ArtMethod * backup) {
std::unique_lock lk(jit_movements_lock_);
jit_movements_.emplace_back(target, backup);
}
} }
} // namespace lsplant } // namespace lsplant

View File

@ -4,16 +4,14 @@ module;
export module lsplant; export module lsplant;
export namespace lsplant { export namespace lsplant::inline v2{
inline namespace v2 { using lsplant::v2::InitInfo;
using lsplant::v2::InitInfo; using lsplant::v2::Init;
using lsplant::v2::Init; using lsplant::v2::Hook;
using lsplant::v2::Hook; using lsplant::v2::UnHook;
using lsplant::v2::UnHook; using lsplant::v2::IsHooked;
using lsplant::v2::IsHooked; using lsplant::v2::Deoptimize;
using lsplant::v2::Deoptimize; using lsplant::v2::GetNativeFunction;
using lsplant::v2::GetNativeFunction; using lsplant::v2::MakeClassInheritable;
using lsplant::v2::MakeClassInheritable; using lsplant::v2::MakeDexFileTrusted;
using lsplant::v2::MakeDexFileTrusted;
}
} }

View File

@ -12,13 +12,6 @@ namespace lsplant {
template <size_t N> template <size_t N>
struct FixedString { struct FixedString {
consteval FixedString(const char (&str)[N]) { std::copy_n(str, N, data); } consteval FixedString(const char (&str)[N]) { std::copy_n(str, N, data); }
#if defined(__LP64__)
template <size_t M>
consteval FixedString(const char (&)[M], const char (&str)[N]) : FixedString(str) {}
#else
template <size_t M>
consteval FixedString(const char (&str)[N], const char (&)[M]) : FixedString(str) {}
#endif
char data[N] = {}; char data[N] = {};
}; };
@ -181,29 +174,11 @@ private:
} }
}; };
template<typename F>
struct Dummy; concept Backup = std::is_function_v<std::remove_pointer_t<F>>;
template<typename F> template<typename F>
concept Backup = std::is_function_v<std::remove_pointer_t<F>> || requires(F&& f) { { f(std::declval<Dummy*>()) } -> std::same_as<Dummy*>; }; concept MemBackup = std::is_member_function_pointer_v<std::remove_pointer_t<F>> || Backup<F>;
template<typename F>
concept MemBackup = std::is_function_v<std::remove_pointer_t<F>> || requires(F&& f) { { f(std::declval<Dummy*>()) } -> std::same_as<Dummy**>; };
template <typename T>
T return_t();
// for ndk 29+, use decltype(F::template operator()<&decltype([] static {})::operator()>)
// and remove Dummy, set MemBackup as std::is_member_function_pointer_v<F>
template<typename F, bool mem>
using Signature = decltype(F::template operator()<[](auto...a) static {
using D = std::conditional_t<mem, Dummy**, Dummy*>;
if constexpr ((false || ... || std::is_same_v<decltype(a), Dummy*>)) {
return return_t<D>();
} else {
return return_t<decltype(F::template operator()<[](auto...) -> D {}>(std::declval<decltype(a)>()...))>();
}
}>);
template<FixedString S> template<FixedString S>
struct Symbol { struct Symbol {
@ -218,21 +193,18 @@ struct Symbol {
[[no_unique_address]] struct Hook { [[no_unique_address]] struct Hook {
template<typename F> template<typename F>
requires(requires { std::declval<Signature<F, false>>(); })
auto operator->*(F&&) const { auto operator->*(F&&) const {
using HookerType = Hooker<S, Signature<F, false>>; using Signature = decltype(F::template operator()<&decltype([] static {})::operator()>);
return HookerType{static_cast<decltype(HookerType::replace_)>(&F::template operator()<HookerType::operator()>)}; if constexpr (requires { F::template operator()<&decltype([] {})::operator()>; }) {
using HookerType = Hooker<S, decltype([]<class This, typename Ret, typename... Args>(Ret(*)(This*, Args...)) -> Ret(This::*)(Args...) {
return {};
}.template operator()(std::declval<Signature>()))>;
return HookerType{static_cast<decltype(HookerType::replace_)>(&F::template operator()<HookerType::operator()>)};
} else {
using HookerType = Hooker<S, Signature>;
return HookerType{static_cast<decltype(HookerType::replace_)>(&F::template operator()<HookerType::operator()>)};
}
}; };
template<typename F>
requires(requires { std::declval<Signature<F, true>>(); })
auto operator->*(F&&) const {
constexpr auto c = []<class This, typename Ret, typename... Args>(Ret(*f)(This*, Args...)) -> Ret(This::*)(Args...) {
return {};
};
using HookerType = Hooker<S, decltype(c.template operator()(std::declval<Signature<F, true>>()))>;
return HookerType{static_cast<decltype(HookerType::replace_)>(&F::template operator()<HookerType::operator()>)};
};
} hook; } hook;
}; };

View File

@ -1,3 +1,7 @@
module;
#include "lsplant.hpp"
#include <android/api-level.h> #include <android/api-level.h>
#include <bits/sysconf.h> #include <bits/sysconf.h>
#include <jni.h> #include <jni.h>
@ -12,29 +16,23 @@
#include "logging.hpp" #include "logging.hpp"
module lsplant;
import dex_builder; import dex_builder;
import lsplant;
import common; import :common;
import art_method; import :art_method;
import clazz; import :clazz;
import thread; import :thread;
import instrumentation; import :instrumentation;
import runtime; import :runtime;
import thread_list; import :thread_list;
import class_linker; import :class_linker;
import scope_gc_critical_section; import :scope_gc_critical_section;
import jit_code_cache; import :jit_code_cache;
import jni_id_manager; import :jni_id_manager;
import dex_file; import :dex_file;
import jit; import :jit;
import hook_helper;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma ide diagnostic ignored "ConstantConditionsOC"
#pragma ide diagnostic ignored "Simplify"
#pragma ide diagnostic ignored "UnreachableCode"
namespace lsplant { namespace lsplant {
@ -633,6 +631,7 @@ std::string GetProxyMethodShorty(JNIEnv *env, jobject proxy_method) {
} // namespace } // namespace
inline namespace v2 { inline namespace v2 {
extern "C++" {
using ::lsplant::IsHooked; using ::lsplant::IsHooked;
@ -835,8 +834,7 @@ using ::lsplant::IsHooked;
if (!cookie) return false; if (!cookie) return false;
return DexFile::SetTrusted(env, cookie); return DexFile::SetTrusted(env, cookie);
} }
}
} // namespace v2 } // namespace v2
} // namespace lsplant } // namespace lsplant
#pragma clang diagnostic pop