[core] Cache symbol address on Zygisk (#1326)

This commit is contained in:
LoveSy 2021-10-25 00:38:30 +08:00 committed by GitHub
parent 3facd944af
commit 67fd7ca054
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 264 additions and 85 deletions

View File

@ -20,11 +20,23 @@
check_magisk_version() { check_magisk_version() {
ui_print "- Magisk version: $MAGISK_VER_CODE" ui_print "- Magisk version: $MAGISK_VER_CODE"
if [ "$FLAVOR" == "riru" ]; then
if [ "$MAGISK_VER_CODE" -lt 23000 ]; then if [ "$MAGISK_VER_CODE" -lt 23000 ]; then
ui_print "*********************************************************" ui_print "*********************************************************"
ui_print "! Please install Magisk v23+" ui_print "! Please install Magisk v23+"
abort "*********************************************************" abort "*********************************************************"
fi fi
elif [ "$FLAVOR" == "zygisk" ]; then
if [ "$MAGISK_VER_CODE" -lt 23010 ]; then
ui_print "*********************************************************"
ui_print "! Please install Magisk Lastest Canary (23010+)"
abort "*********************************************************"
fi
else
ui_print "*********************************************************"
ui_print "! Unsupported flavor $FLAVOR"
abort "*********************************************************"
fi
} }
require_new_android() { require_new_android() {

View File

@ -25,6 +25,7 @@
#include "logging.h" #include "logging.h"
#include "config.h" #include "config.h"
#include "context.h" #include "context.h"
#include "symbol_cache.h"
#define RIRU_MODULE #define RIRU_MODULE
#include <riru.h> #include <riru.h>
@ -40,6 +41,7 @@ namespace lspd {
void onModuleLoaded() { void onModuleLoaded() {
LOGI("onModuleLoaded: welcome to LSPosed!"); LOGI("onModuleLoaded: welcome to LSPosed!");
LOGI("onModuleLoaded: version v%s (%d)", versionName, versionCode); LOGI("onModuleLoaded: version v%s (%d)", versionName, versionCode);
InitSymbolCache(nullptr);
Context::GetInstance()->Init(); Context::GetInstance()->Init();
if constexpr (isDebug) { if constexpr (isDebug) {
Context::GetInstance()->PreLoadDex("/system/" + kDexPath); Context::GetInstance()->PreLoadDex("/system/" + kDexPath);

View File

@ -25,6 +25,7 @@
#include "logging.h" #include "logging.h"
#include "context.h" #include "context.h"
#include "config.h" #include "config.h"
#include "symbol_cache.h"
namespace lspd { namespace lspd {
namespace { namespace {
@ -171,6 +172,107 @@ namespace lspd {
int *allowUnload = &allow_unload; int *allowUnload = &allow_unload;
class SharedMem {
inline static void *cutils = nullptr;
inline static int (*ashmem_create_region)(const char *name, std::size_t size) = nullptr;
inline static int (*ashmem_set_prot_region)(int fd, int prot) = nullptr;
inline static bool init = false;
static void Init() {
if (init) return;
cutils = dlopen("/system/lib" LP_SELECT("", "64") "/libcutils.so", 0);
ashmem_create_region = cutils ? reinterpret_cast<decltype(ashmem_create_region)>(
dlsym(cutils, "ashmem_create_region")) : nullptr;
ashmem_set_prot_region = cutils ? reinterpret_cast<decltype(ashmem_set_prot_region)>(
dlsym(cutils, "ashmem_set_prot_region")) : nullptr;
init = true;
}
int fd_ = -1;
std::size_t size_ = 0;
class MappedMem {
void *addr_ = nullptr;
std::size_t size_ = 0;
friend class SharedMem;
MappedMem(int fd, std::size_t size, int prot, int flags, off_t offset) : addr_(
mmap(nullptr, size, prot, flags, fd, offset)), size_(size) {
if (addr_ == MAP_FAILED) {
PLOGE("failed to mmap");
addr_ = nullptr;
size_ = 0;
}
}
MappedMem(const MappedMem &) = delete;
MappedMem &operator=(const MappedMem &other) = delete;
public:
MappedMem(MappedMem &&other) : addr_(other.addr_), size_(other.size_) {
other.addr_ = nullptr;
other.size_ = 0;
}
MappedMem &operator=(MappedMem &&other) {
new(this)MappedMem(std::move(other));
return *this;
}
constexpr operator bool() { return addr_; }
~MappedMem() {
if (addr_) {
munmap(addr_, size_);
}
}
constexpr auto size() const { return size_; }
constexpr auto get() const { return addr_; }
};
public:
MappedMem map(int prot, int flags, off_t offset) {
return {fd_, size_, prot, flags, offset};
}
constexpr bool ok() const { return fd_ > 0 && size_ > 0; }
SharedMem(std::string_view name, std::size_t size) {
Init();
if (ashmem_create_region && (fd_ = ashmem_create_region(name.data(), size)) > 0) {
size_ = size;
LOGD("using memfd");
} else {
LOGD("using tmp file");
auto *tmp = tmpfile();
if (tmp) {
fd_ = fileno(tmp);
ftruncate(fd_, size);
size_ = size;
}
}
}
void SetProt(int prot) {
ashmem_set_prot_region(fd_, prot);
}
SharedMem() : fd_(-1), size_(0) {
Init();
}
constexpr auto get() const { return fd_; }
constexpr auto size() const { return size_; }
};
class ZygiskModule : public zygisk::ModuleBase { class ZygiskModule : public zygisk::ModuleBase {
JNIEnv *env_; JNIEnv *env_;
zygisk::Api *api_; zygisk::Api *api_;
@ -193,6 +295,21 @@ namespace lspd {
} else { } else {
LOGE("Failed to read dex fd"); LOGE("Failed to read dex fd");
} }
if (int fd = -1, size = 0; (size = read_int(companion)) > 0 &&
(fd = recv_fd(companion)) != -1) {
if (auto addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
addr && addr != MAP_FAILED) {
InitSymbolCache(reinterpret_cast<SymbolCache *>(addr));
msync(addr, size, MS_SYNC);
munmap(addr, size);
} else {
InitSymbolCache(nullptr);
}
close(fd);
} else {
LOGE("Failed to read symbol fd");
InitSymbolCache(nullptr);
}
close(companion); close(companion);
} }
@ -217,50 +334,59 @@ namespace lspd {
} }
}; };
std::tuple<int, std::size_t> InitCompanion() { std::tuple<SharedMem, SharedMem> InitCompanion() {
LOGI("onModuleLoaded: welcome to LSPosed!"); LOGI("onModuleLoaded: welcome to LSPosed!");
LOGI("onModuleLoaded: version v%s (%d)", versionName, versionCode); LOGI("onModuleLoaded: version v%s (%d)", versionName, versionCode);
int (*ashmem_create_region)(const char *name, std::size_t size) = nullptr;
int (*ashmem_set_prot_region)(int fd, int prot) = nullptr;
std::string path = "/data/adb/modules/"s + lspd::moduleName + "/" + kDexPath; std::string path = "/data/adb/modules/"s + lspd::moduleName + "/" + kDexPath;
int fd = open(path.data(), O_RDONLY | O_CLOEXEC); int dex_fd = open(path.data(), O_RDONLY | O_CLOEXEC);
if (fd < 0) { if (dex_fd < 0) {
LOGE("Failed to load dex: %s", path.data()); PLOGE("Failed to load dex: %s", path.data());
return {-1, 0}; return {{}, {}};
} }
auto fsize = lseek(fd, 0, SEEK_END); size_t dex_size = lseek(dex_fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET); lseek(dex_fd, 0, SEEK_SET);
auto *cutils = dlopen("/system/lib" LP_SELECT("", "64") "/libcutils.so", 0);
ashmem_create_region = cutils ? reinterpret_cast<decltype(ashmem_create_region)>( SharedMem dex{"lspd.dex", dex_size};
dlsym(cutils, "ashmem_create_region")) : nullptr; SharedMem symbol{"symbol", sizeof(lspd::SymbolCache)};
ashmem_set_prot_region = cutils ? reinterpret_cast<decltype(ashmem_set_prot_region)>(
dlsym(cutils, "ashmem_set_prot_region")) : nullptr; if (!dex.ok() || !symbol.ok()) {
int tmp_fd = -1; PLOGE("Failed to allocate shared mem");
if (void *addr = nullptr; close(dex_fd);
ashmem_create_region && ashmem_set_prot_region && return {{}, {}};
(tmp_fd = ashmem_create_region("lspd.dex", fsize)) > 0 &&
(addr = mmap(nullptr, fsize, PROT_WRITE, MAP_SHARED, tmp_fd, 0)) &&
read(fd, addr, fsize) > 0 && munmap(addr, fsize) == 0) {
LOGD("using memfd");
ashmem_set_prot_region(tmp_fd, PROT_READ);
std::swap(fd, tmp_fd);
} else {
LOGD("using raw fd");
} }
close(tmp_fd); if (auto dex_map = dex.map(PROT_WRITE, MAP_SHARED, 0); !dex_map ||
dlclose(cutils); read(dex_fd, dex_map.get(),
return {fd, fsize}; dex_map.size()) < 0) {
PLOGE("Failed to read dex %p", dex_map.get());
close(dex_fd);
return {{}, {}};
}
dex.SetProt(PROT_READ);
if (auto symbol_map = symbol.map(PROT_WRITE, MAP_SHARED, 0); symbol_map) {
memcpy(symbol_map.get(), lspd::symbol_cache.get(), symbol_map.size());
}
close(dex_fd);
return {std::move(dex), std::move(symbol)};
} }
void CompanionEntry(int client) { void CompanionEntry(int client) {
using namespace std::string_literals; using namespace std::string_literals;
static auto[fd, size] = InitCompanion(); static auto[dex, symbol] = InitCompanion();
if (fd > 0 && size > 0) { LOGD("Got dex with fd=%d size=%d; Got cache with fd=%d size=%d", dex.get(),
write_int(client, size); (int) dex.size(),
send_fd(client, fd); symbol.get(), (int) symbol.size());
if (dex.ok()) {
write_int(client, dex.size());
send_fd(client, dex.get());
} else write_int(client, -1);
if (symbol.ok()) {
write_int(client, symbol.size());
send_fd(client, symbol.get());
} else write_int(client, -1); } else write_int(client, -1);
close(client); close(client);
} }

View File

@ -121,14 +121,14 @@ namespace art {
if (api_level < __ANDROID_API_P__) { if (api_level < __ANDROID_API_P__) {
return; return;
} }
DexFile_setTrustedSym = reinterpret_cast<decltype(DexFile_setTrustedSym)>(lspd::sym_setTrusted); DexFile_setTrustedSym = reinterpret_cast<decltype(DexFile_setTrustedSym)>(lspd::symbol_cache->setTrusted);
lspd::HookSymNoHandle(lspd::sym_openDexFileNative, DexFile_openDexFileNative); lspd::HookSymNoHandle(lspd::symbol_cache->openDexFileNative, DexFile_openDexFileNative);
lspd::HookSymNoHandle(lspd::sym_openInMemoryDexFilesNative, lspd::HookSymNoHandle(lspd::symbol_cache->openInMemoryDexFilesNative,
DexFile_openInMemoryDexFilesNative); DexFile_openInMemoryDexFilesNative);
if (api_level == __ANDROID_API_P__) { if (api_level == __ANDROID_API_P__) {
lspd::HookSymNoHandle(lspd::sym_createCookieWithArray, lspd::HookSymNoHandle(lspd::symbol_cache->createCookieWithArray,
DexFile_createCookieWithArray); DexFile_createCookieWithArray);
lspd::HookSymNoHandle(lspd::sym_createCookieWithDirectBuffer, lspd::HookSymNoHandle(lspd::symbol_cache->createCookieWithDirectBuffer,
DexFile_createCookieWithDirectBuffer); DexFile_createCookieWithDirectBuffer);
} }
}; };

View File

@ -121,7 +121,6 @@ namespace lspd {
} }
void Context::Init() { void Context::Init() {
InitSymbolCache();
} }
void Context::Init(JNIEnv *env) { void Context::Init(JNIEnv *env) {
@ -188,7 +187,10 @@ namespace lspd {
void void
Context::OnNativeForkSystemServerPre(JNIEnv *env) { Context::OnNativeForkSystemServerPre(JNIEnv *env) {
Service::instance()->InitService(env); Service::instance()->InitService(env);
skip_ = !sym_initialized; skip_ = !symbol_cache->initialized.test(std::memory_order_acquire);
if (skip_) [[unlikely]] {
LOGW("skip system server due to symbol cache");
}
setAllowUnload(skip_); setAllowUnload(skip_);
} }
@ -204,6 +206,9 @@ namespace lspd {
FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", binder); FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", binder);
} else skip_ = true; } else skip_ = true;
} }
if (skip_) [[unlikely]] {
LOGW("skipped system server");
}
setAllowUnload(skip_); setAllowUnload(skip_);
} }
@ -226,7 +231,7 @@ namespace lspd {
Service::instance()->InitService(env); Service::instance()->InitService(env);
const auto app_id = uid % PER_USER_RANGE; const auto app_id = uid % PER_USER_RANGE;
JUTFString process_name(env, nice_name); JUTFString process_name(env, nice_name);
skip_ = !sym_initialized; skip_ = !symbol_cache->initialized.test(std::memory_order_acquire);
if (!skip_ && !app_data_dir) { if (!skip_ && !app_data_dir) {
LOGD("skip injecting into %s because it has no data dir", process_name.get()); LOGD("skip injecting into %s because it has no data dir", process_name.get());
skip_ = true; skip_ = true;
@ -266,7 +271,7 @@ namespace lspd {
} else { } else {
auto context = Context::ReleaseInstance(); auto context = Context::ReleaseInstance();
auto service = Service::ReleaseInstance(); auto service = Service::ReleaseInstance();
art_img.reset(); GetArt().reset();
LOGD("skipped %s", process_name.get()); LOGD("skipped %s", process_name.get());
setAllowUnload(true); setAllowUnload(true);
} }

View File

@ -122,9 +122,9 @@ namespace lspd {
}); });
bool InstallNativeAPI() { bool InstallNativeAPI() {
LOGD("InstallNativeAPI: %p", sym_do_dlopen); LOGD("InstallNativeAPI: %p", symbol_cache->do_dlopen);
if (sym_do_dlopen) [[likely]] { if (symbol_cache->do_dlopen) [[likely]] {
HookSymNoHandle(sym_do_dlopen, do_dlopen); HookSymNoHandle(symbol_cache->do_dlopen, do_dlopen);
return true; return true;
} }
return false; return false;

View File

@ -47,7 +47,7 @@ namespace lspd {
return; return;
} }
LOGD("Start to install inline hooks"); LOGD("Start to install inline hooks");
const auto &handle_libart = *art_img; const auto &handle_libart = *GetArt();
if (!handle_libart.isValid()) { if (!handle_libart.isValid()) {
LOGE("Failed to fetch libart.so"); LOGE("Failed to fetch libart.so");
} }
@ -62,7 +62,7 @@ namespace lspd {
art::thread_list::ScopedSuspendAll::Setup(handle_libart); art::thread_list::ScopedSuspendAll::Setup(handle_libart);
art::gc::ScopedGCCriticalSection::Setup(handle_libart); art::gc::ScopedGCCriticalSection::Setup(handle_libart);
art::jit::jit_code_cache::Setup(handle_libart); art::jit::jit_code_cache::Setup(handle_libart);
art_img.reset(); GetArt().reset();
LOGD("Inline hooks installed"); LOGD("Inline hooks installed");
} }
} // namespace lspd } // namespace lspd

View File

@ -171,7 +171,7 @@ namespace lspd {
auto binder_class = JNI_FindClass(env, "android/os/Binder"); auto binder_class = JNI_FindClass(env, "android/os/Binder");
exec_transact_backup_methodID_ = JNI_GetMethodID(env, binder_class, "execTransact", exec_transact_backup_methodID_ = JNI_GetMethodID(env, binder_class, "execTransact",
"(IJJI)Z"); "(IJJI)Z");
if (!sym_set_table_override) { if (!symbol_cache->setTableOverride) {
LOGE("set table override not found"); LOGE("set table override not found");
} }
memcpy(&native_interface_replace_, env->functions, sizeof(JNINativeInterface)); memcpy(&native_interface_replace_, env->functions, sizeof(JNINativeInterface));
@ -179,8 +179,8 @@ namespace lspd {
call_boolean_method_va_backup_ = env->functions->CallBooleanMethodV; call_boolean_method_va_backup_ = env->functions->CallBooleanMethodV;
native_interface_replace_.CallBooleanMethodV = &call_boolean_method_va_replace; native_interface_replace_.CallBooleanMethodV = &call_boolean_method_va_replace;
if (sym_set_table_override != nullptr) { if (symbol_cache->setTableOverride != nullptr) {
reinterpret_cast<void (*)(JNINativeInterface *)>(sym_set_table_override)( reinterpret_cast<void (*)(JNINativeInterface *)>(symbol_cache->setTableOverride)(
&native_interface_replace_); &native_interface_replace_);
} }
if (auto activity_thread_class = JNI_FindClass(env, "android/app/IActivityManager$Stub")) { if (auto activity_thread_class = JNI_FindClass(env, "android/app/IActivityManager$Stub")) {

View File

@ -31,48 +31,60 @@
#include <logging.h> #include <logging.h>
namespace lspd { namespace lspd {
bool sym_initialized = false; std::unique_ptr<SymbolCache> symbol_cache = std::make_unique<SymbolCache>();
void *sym_do_dlopen = nullptr;
void *sym_openInMemoryDexFilesNative = nullptr; std::unique_ptr<const SandHook::ElfImg> &GetArt() {
void *sym_createCookieWithArray = nullptr; static std::unique_ptr<const SandHook::ElfImg> kArtImg = nullptr;
void *sym_createCookieWithDirectBuffer = nullptr; if (!kArtImg) {
void *sym_openDexFileNative = nullptr; kArtImg = std::make_unique<SandHook::ElfImg>(kLibArtName);
void *sym_setTrusted = nullptr; }
void *sym_set_table_override = nullptr; return kArtImg;
std::unique_ptr<const SandHook::ElfImg> art_img = nullptr; }
bool FindLibArt() { bool FindLibArt() {
art_img = std::make_unique<SandHook::ElfImg>(kLibArtName); auto &art = GetArt();
if (!art_img->isValid()) return false; if (!art->isValid()) return false;
auto api_level = GetAndroidApiLevel(); auto api_level = GetAndroidApiLevel();
return (sym_set_table_override = art_img->getSymbAddress<void *>( return (symbol_cache->setTableOverride = art->getSymbAddress<void *>(
"_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface")) != nullptr "_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface")) != nullptr
&& (api_level < __ANDROID_API_P__ || ( && (api_level < __ANDROID_API_P__ || (
(sym_openDexFileNative = art_img->getSymbAddress<void *>( (symbol_cache->openDexFileNative = art->getSymbAddress<void *>(
"_ZN3artL25DexFile_openDexFileNativeEP7_JNIEnvP7_jclassP8_jstringS5_iP8_jobjectP13_jobjectArray")) && "_ZN3artL25DexFile_openDexFileNativeEP7_JNIEnvP7_jclassP8_jstringS5_iP8_jobjectP13_jobjectArray")) &&
( (
(sym_openInMemoryDexFilesNative = art_img->getSymbAddress<void *>( (symbol_cache->openInMemoryDexFilesNative = art->getSymbAddress<void *>(
"_ZN3artL34DexFile_openInMemoryDexFilesNativeEP7_JNIEnvP7_jclassP13_jobjectArrayS5_P10_jintArrayS7_P8_jobjectS5_")) || "_ZN3artL34DexFile_openInMemoryDexFilesNativeEP7_JNIEnvP7_jclassP13_jobjectArrayS5_P10_jintArrayS7_P8_jobjectS5_")) ||
( (
(sym_createCookieWithArray = art_img->getSymbAddress<void *>( (symbol_cache->createCookieWithArray = art->getSymbAddress<void *>(
"_ZN3artL29DexFile_createCookieWithArrayEP7_JNIEnvP7_jclassP11_jbyteArrayii")) && "_ZN3artL29DexFile_createCookieWithArrayEP7_JNIEnvP7_jclassP11_jbyteArrayii")) &&
(sym_createCookieWithDirectBuffer = art_img->getSymbAddress<void *>( (symbol_cache->createCookieWithDirectBuffer = art->getSymbAddress<void *>(
"_ZN3artL36DexFile_createCookieWithDirectBufferEP7_JNIEnvP7_jclassP8_jobjectii")) "_ZN3artL36DexFile_createCookieWithDirectBufferEP7_JNIEnvP7_jclassP8_jobjectii"))
) )
) && ) &&
(sym_setTrusted = art_img->getSymbAddress<void *>( (symbol_cache->setTrusted = art->getSymbAddress<void *>(
"_ZN3artL18DexFile_setTrustedEP7_JNIEnvP7_jclassP8_jobject")))); "_ZN3artL18DexFile_setTrustedEP7_JNIEnvP7_jclassP8_jobject"))));
} }
void InitSymbolCache() { void InitSymbolCache(SymbolCache *other) {
LOGD("InitSymbolCache"); LOGD("InitSymbolCache");
sym_initialized = FindLibArt(); if (other && other->initialized.test(std::memory_order_acquire)) {
sym_do_dlopen = SandHook::ElfImg("/linker").getSymbAddress<void *>( LOGD("Already initialized");
*symbol_cache = *other;
symbol_cache->initialized.test_and_set(std::memory_order_relaxed);
return;
}
auto ok = FindLibArt();
symbol_cache->do_dlopen = SandHook::ElfImg("/linker").getSymbAddress<void *>(
"__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv"); "__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv");
if (!sym_initialized) [[unlikely]] { if (!ok) [[unlikely]] {
sym_initialized = false; GetArt().reset();
art_img.reset();
LOGE("Init symbol cache failed"); LOGE("Init symbol cache failed");
} else {
symbol_cache->initialized.test_and_set(std::memory_order_relaxed);
if (other) {
*other = *symbol_cache;
other->initialized.test_and_set(std::memory_order_acq_rel);
}
} }
} }
} // namespace lspd } // namespace lspd

View File

@ -26,22 +26,44 @@
#define LSPOSED_SYMBOL_CACHE_H #define LSPOSED_SYMBOL_CACHE_H
#include <memory> #include <memory>
namespace SandHook { namespace SandHook {
class ElfImg; class ElfImg;
} }
namespace lspd { namespace lspd {
extern bool sym_initialized; struct SymbolCache {
extern std::unique_ptr<const SandHook::ElfImg> art_img; std::atomic_flag initialized{};
extern void *sym_do_dlopen; void *do_dlopen;
extern void *sym_openInMemoryDexFilesNative; void *openInMemoryDexFilesNative;
extern void *sym_createCookieWithArray; void *createCookieWithArray;
extern void *sym_createCookieWithDirectBuffer; void *createCookieWithDirectBuffer;
extern void *sym_openDexFileNative; void *openDexFileNative;
extern void *sym_setTrusted; void *setTrusted;
extern void *sym_set_table_override; void *setTableOverride;
void InitSymbolCache(); SymbolCache() = default;
SymbolCache(const SymbolCache &other) :
do_dlopen(other.do_dlopen),
openInMemoryDexFilesNative(other.openInMemoryDexFilesNative),
createCookieWithArray(other.createCookieWithArray),
createCookieWithDirectBuffer(other.createCookieWithDirectBuffer),
openDexFileNative(other.openDexFileNative),
setTrusted(other.setTrusted),
setTableOverride(other.setTableOverride) {}
SymbolCache &operator=(const SymbolCache &other) {
new(this)SymbolCache(other);
return *this;
}
};
extern std::unique_ptr<SymbolCache> symbol_cache;
void InitSymbolCache(SymbolCache *other);
std::unique_ptr<const SandHook::ElfImg> &GetArt();
} }
#endif //LSPOSED_SYMBOL_CACHE_H #endif //LSPOSED_SYMBOL_CACHE_H