[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,9 +20,21 @@
check_magisk_version() {
ui_print "- Magisk version: $MAGISK_VER_CODE"
if [ "$MAGISK_VER_CODE" -lt 23000 ]; then
if [ "$FLAVOR" == "riru" ]; then
if [ "$MAGISK_VER_CODE" -lt 23000 ]; then
ui_print "*********************************************************"
ui_print "! Please install Magisk v23+"
abort "*********************************************************"
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 "! Please install Magisk v23+"
ui_print "! Unsupported flavor $FLAVOR"
abort "*********************************************************"
fi
}

View File

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

View File

@ -25,6 +25,7 @@
#include "logging.h"
#include "context.h"
#include "config.h"
#include "symbol_cache.h"
namespace lspd {
namespace {
@ -171,6 +172,107 @@ namespace lspd {
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 {
JNIEnv *env_;
zygisk::Api *api_;
@ -193,6 +295,21 @@ namespace lspd {
} else {
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);
}
@ -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: 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;
int fd = open(path.data(), O_RDONLY | O_CLOEXEC);
if (fd < 0) {
LOGE("Failed to load dex: %s", path.data());
return {-1, 0};
int dex_fd = open(path.data(), O_RDONLY | O_CLOEXEC);
if (dex_fd < 0) {
PLOGE("Failed to load dex: %s", path.data());
return {{}, {}};
}
auto fsize = lseek(fd, 0, SEEK_END);
lseek(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)>(
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;
int tmp_fd = -1;
if (void *addr = nullptr;
ashmem_create_region && ashmem_set_prot_region &&
(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");
size_t dex_size = lseek(dex_fd, 0, SEEK_END);
lseek(dex_fd, 0, SEEK_SET);
SharedMem dex{"lspd.dex", dex_size};
SharedMem symbol{"symbol", sizeof(lspd::SymbolCache)};
if (!dex.ok() || !symbol.ok()) {
PLOGE("Failed to allocate shared mem");
close(dex_fd);
return {{}, {}};
}
close(tmp_fd);
dlclose(cutils);
return {fd, fsize};
if (auto dex_map = dex.map(PROT_WRITE, MAP_SHARED, 0); !dex_map ||
read(dex_fd, dex_map.get(),
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) {
using namespace std::string_literals;
static auto[fd, size] = InitCompanion();
if (fd > 0 && size > 0) {
write_int(client, size);
send_fd(client, fd);
static auto[dex, symbol] = InitCompanion();
LOGD("Got dex with fd=%d size=%d; Got cache with fd=%d size=%d", dex.get(),
(int) dex.size(),
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);
close(client);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -31,48 +31,60 @@
#include <logging.h>
namespace lspd {
bool sym_initialized = false;
void *sym_do_dlopen = nullptr;
void *sym_openInMemoryDexFilesNative = nullptr;
void *sym_createCookieWithArray = nullptr;
void *sym_createCookieWithDirectBuffer = nullptr;
void *sym_openDexFileNative = nullptr;
void *sym_setTrusted = nullptr;
void *sym_set_table_override = nullptr;
std::unique_ptr<const SandHook::ElfImg> art_img = nullptr;
std::unique_ptr<SymbolCache> symbol_cache = std::make_unique<SymbolCache>();
std::unique_ptr<const SandHook::ElfImg> &GetArt() {
static std::unique_ptr<const SandHook::ElfImg> kArtImg = nullptr;
if (!kArtImg) {
kArtImg = std::make_unique<SandHook::ElfImg>(kLibArtName);
}
return kArtImg;
}
bool FindLibArt() {
art_img = std::make_unique<SandHook::ElfImg>(kLibArtName);
if (!art_img->isValid()) return false;
auto &art = GetArt();
if (!art->isValid()) return false;
auto api_level = GetAndroidApiLevel();
return (sym_set_table_override = art_img->getSymbAddress<void *>(
return (symbol_cache->setTableOverride = art->getSymbAddress<void *>(
"_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface")) != nullptr
&& (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")) &&
(
(sym_openInMemoryDexFilesNative = art_img->getSymbAddress<void *>(
(symbol_cache->openInMemoryDexFilesNative = art->getSymbAddress<void *>(
"_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")) &&
(sym_createCookieWithDirectBuffer = art_img->getSymbAddress<void *>(
(symbol_cache->createCookieWithDirectBuffer = art->getSymbAddress<void *>(
"_ZN3artL36DexFile_createCookieWithDirectBufferEP7_JNIEnvP7_jclassP8_jobjectii"))
)
) &&
(sym_setTrusted = art_img->getSymbAddress<void *>(
(symbol_cache->setTrusted = art->getSymbAddress<void *>(
"_ZN3artL18DexFile_setTrustedEP7_JNIEnvP7_jclassP8_jobject"))));
}
void InitSymbolCache() {
void InitSymbolCache(SymbolCache *other) {
LOGD("InitSymbolCache");
sym_initialized = FindLibArt();
sym_do_dlopen = SandHook::ElfImg("/linker").getSymbAddress<void *>(
if (other && other->initialized.test(std::memory_order_acquire)) {
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");
if (!sym_initialized) [[unlikely]] {
sym_initialized = false;
art_img.reset();
if (!ok) [[unlikely]] {
GetArt().reset();
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

View File

@ -26,22 +26,44 @@
#define LSPOSED_SYMBOL_CACHE_H
#include <memory>
namespace SandHook {
class ElfImg;
}
namespace lspd {
extern bool sym_initialized;
extern std::unique_ptr<const SandHook::ElfImg> art_img;
extern void *sym_do_dlopen;
extern void *sym_openInMemoryDexFilesNative;
extern void *sym_createCookieWithArray;
extern void *sym_createCookieWithDirectBuffer;
extern void *sym_openDexFileNative;
extern void *sym_setTrusted;
extern void *sym_set_table_override;
struct SymbolCache {
std::atomic_flag initialized{};
void *do_dlopen;
void *openInMemoryDexFilesNative;
void *createCookieWithArray;
void *createCookieWithDirectBuffer;
void *openDexFileNative;
void *setTrusted;
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