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 androidBuildToolsVersion by extra("35.0.0")
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+")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,13 +12,6 @@ namespace lsplant {
template <size_t N>
struct FixedString {
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] = {};
};
@ -181,29 +174,11 @@ private:
}
};
struct Dummy;
template<typename F>
concept Backup = std::is_function_v<std::remove_pointer_t<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*>; };
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)>()...))>();
}
}>);
concept MemBackup = std::is_member_function_pointer_v<std::remove_pointer_t<F>> || Backup<F>;
template<FixedString S>
struct Symbol {
@ -218,21 +193,18 @@ struct Symbol {
[[no_unique_address]] struct Hook {
template<typename F>
requires(requires { std::declval<Signature<F, false>>(); })
auto operator->*(F&&) const {
using HookerType = Hooker<S, Signature<F, false>>;
return HookerType{static_cast<decltype(HookerType::replace_)>(&F::template operator()<HookerType::operator()>)};
using Signature = decltype(F::template operator()<&decltype([] static {})::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;
};

View File

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