update: LSPlt

This commit updates LSPlt and adapt to its changes so that it works properly.
This commit is contained in:
ThePedroo
2025-07-30 21:28:27 -03:00
parent e0ce1473dd
commit a7917e20fe
3 changed files with 91 additions and 37 deletions

View File

@@ -7,7 +7,7 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include <lsplt.hpp> #include <lsplt.h>
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h> #include <dirent.h>
@@ -127,7 +127,6 @@ map<string, vector<JNINativeMethod>> *jni_hook_list;
bool should_unmap_zygisk = false; bool should_unmap_zygisk = false;
bool enable_unloader = false; bool enable_unloader = false;
bool hooked_unloader = false; bool hooked_unloader = false;
std::vector<lsplt::MapInfo> cached_map_infos = {};
} // namespace } // namespace
@@ -242,7 +241,8 @@ DCL_HOOK_FUNC(int, pthread_attr_setstacksize, void *target, size_t size) {
if (should_unmap_zygisk) { if (should_unmap_zygisk) {
unhook_functions(); unhook_functions();
cached_map_infos.clear();
lsplt_free_resources();
if (should_unmap_zygisk) { if (should_unmap_zygisk) {
// Because both `pthread_attr_setstacksize` and `dlclose` have the same function signature, // Because both `pthread_attr_setstacksize` and `dlclose` have the same function signature,
@@ -263,8 +263,6 @@ DCL_HOOK_FUNC(char *, strdup, const char *s) {
if (strcmp(s, "com.android.internal.os.ZygoteInit") == 0) { if (strcmp(s, "com.android.internal.os.ZygoteInit") == 0) {
LOGV("strdup %s", s); LOGV("strdup %s", s);
initialize_jni_hook(); initialize_jni_hook();
cached_map_infos = lsplt::MapInfo::Scan();
LOGD("cached_map_infos updated");
} }
return old_strdup(s); return old_strdup(s);
@@ -349,9 +347,18 @@ void initialize_jni_hook() {
auto get_created_java_vms = reinterpret_cast<jint (*)(JavaVM **, jsize, jsize *)>( auto get_created_java_vms = reinterpret_cast<jint (*)(JavaVM **, jsize, jsize *)>(
dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs")); dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs"));
if (!get_created_java_vms) { if (!get_created_java_vms) {
for (auto &map: cached_map_infos) { struct lsplt_map_info *map_infos = lsplt_scan_maps("self");
if (!map.path.ends_with("/libnativehelper.so")) continue; if (!map_infos) {
void *h = dlopen(map.path.data(), RTLD_LAZY); LOGE("Failed to scan maps for self");
return;
}
for (size_t i = 0; i < map_infos->length; i++) {
struct lsplt_map_entry map = map_infos->maps[i];
if (!strstr(map.path, "/libnativehelper.so")) continue;
void *h = dlopen(map.path, RTLD_LAZY);
if (!h) { if (!h) {
LOGW("cannot dlopen libnativehelper.so: %s", dlerror()); LOGW("cannot dlopen libnativehelper.so: %s", dlerror());
break; break;
@@ -360,6 +367,9 @@ void initialize_jni_hook() {
dlclose(h); dlclose(h);
break; break;
} }
lsplt_free_maps(map_infos);
if (!get_created_java_vms) { if (!get_created_java_vms) {
LOGW("JNI_GetCreatedJavaVMs not found"); LOGW("JNI_GetCreatedJavaVMs not found");
return; return;
@@ -430,11 +440,11 @@ bool ZygiskModule::RegisterModuleImpl(ApiTable *api, long *module) {
api->v2.getFlags = [](auto) { return ZygiskModule::getFlags(); }; api->v2.getFlags = [](auto) { return ZygiskModule::getFlags(); };
} }
if (api_version >= 4) { if (api_version >= 4) {
api->v4.pltHookCommit = []() { return lsplt::CommitHook(cached_map_infos); }; api->v4.pltHookCommit = []() { return lsplt_commit_hook(); };
api->v4.pltHookRegister = [](dev_t dev, ino_t inode, const char *symbol, void *fn, void **backup) { api->v4.pltHookRegister = [](dev_t dev, ino_t inode, const char *symbol, void *fn, void **backup) {
if (dev == 0 || inode == 0 || symbol == nullptr || fn == nullptr) if (dev == 0 || inode == 0 || symbol == nullptr || fn == nullptr)
return; return;
lsplt::RegisterHook(dev, inode, symbol, fn, backup); lsplt_register_hook(dev, inode, symbol, fn, backup);
}; };
api->v4.exemptFd = [](int fd) { return g_ctx && g_ctx->exempt_fd(fd); }; api->v4.exemptFd = [](int fd) { return g_ctx && g_ctx->exempt_fd(fd); };
} }
@@ -466,14 +476,24 @@ void ZygiskContext::plt_hook_exclude(const char *regex, const char *symbol) {
void ZygiskContext::plt_hook_process_regex() { void ZygiskContext::plt_hook_process_regex() {
if (register_info.empty()) if (register_info.empty())
return; return;
for (auto &map : cached_map_infos) {
struct lsplt_map_info *map_infos = lsplt_scan_maps("self");
if (!map_infos) {
LOGE("Failed to scan maps for self");
return;
}
for (size_t i = 0; i < map_infos->length; i++) {
struct lsplt_map_entry map = map_infos->maps[i];
if (map.offset != 0 || !map.is_private || !(map.perms & PROT_READ)) continue; if (map.offset != 0 || !map.is_private || !(map.perms & PROT_READ)) continue;
for (auto &reg: register_info) { for (auto &reg: register_info) {
if (regexec(&reg.regex, map.path.data(), 0, nullptr, 0) != 0) if (regexec(&reg.regex, map.path, 0, nullptr, 0) != 0)
continue; continue;
bool ignored = false; bool ignored = false;
for (auto &ign: ignore_info) { for (auto &ign: ignore_info) {
if (regexec(&ign.regex, map.path.data(), 0, nullptr, 0) != 0) if (regexec(&ign.regex, map.path, 0, nullptr, 0) != 0)
continue; continue;
if (ign.symbol.empty() || ign.symbol == reg.symbol) { if (ign.symbol.empty() || ign.symbol == reg.symbol) {
ignored = true; ignored = true;
@@ -481,10 +501,12 @@ void ZygiskContext::plt_hook_process_regex() {
} }
} }
if (!ignored) { if (!ignored) {
lsplt::RegisterHook(map.dev, map.inode, reg.symbol, reg.callback, reg.backup); lsplt_register_hook(map.dev, map.inode, reg.symbol.c_str(), reg.callback, reg.backup);
} }
} }
} }
lsplt_free_maps(map_infos);
} }
bool ZygiskContext::plt_hook_commit() { bool ZygiskContext::plt_hook_commit() {
@@ -496,7 +518,7 @@ bool ZygiskContext::plt_hook_commit() {
pthread_mutex_unlock(&hook_info_lock); pthread_mutex_unlock(&hook_info_lock);
} }
return lsplt::CommitHook(cached_map_infos); return lsplt_commit_hook();
} }
@@ -961,8 +983,8 @@ ZygiskContext::~ZygiskContext() {
} // namespace } // namespace
static bool hook_commit(std::vector<lsplt::MapInfo> &map_infos = cached_map_infos) { static bool hook_commit(struct lsplt_map_info *map_infos) {
if (lsplt::CommitHook(map_infos)) { if (map_infos ? lsplt_commit_hook_manual(map_infos) : lsplt_commit_hook()) {
return true; return true;
} else { } else {
LOGE("plt_hook failed"); LOGE("plt_hook failed");
@@ -971,7 +993,7 @@ static bool hook_commit(std::vector<lsplt::MapInfo> &map_infos = cached_map_info
} }
static void hook_register(dev_t dev, ino_t inode, const char *symbol, void *new_func, void **old_func) { static void hook_register(dev_t dev, ino_t inode, const char *symbol, void *new_func, void **old_func) {
if (!lsplt::RegisterHook(dev, inode, symbol, new_func, old_func)) { if (!lsplt_register_hook(dev, inode, symbol, new_func, old_func)) {
LOGE("Failed to register plt_hook \"%s\"", symbol); LOGE("Failed to register plt_hook \"%s\"", symbol);
return; return;
} }
@@ -1007,21 +1029,33 @@ void hook_functions() {
ino_t android_runtime_inode = 0; ino_t android_runtime_inode = 0;
dev_t android_runtime_dev = 0; dev_t android_runtime_dev = 0;
cached_map_infos = lsplt::MapInfo::Scan(); struct lsplt_map_info *map_infos = lsplt_scan_maps("self");
for (auto &map : cached_map_infos) { if (!map_infos) {
if (map.path.ends_with("libandroid_runtime.so")) { LOGE("Failed to scan maps for self");
android_runtime_inode = map.inode;
android_runtime_dev = map.dev;
break; return;
} }
for (size_t i = 0; i < map_infos->length; i++) {
struct lsplt_map_entry map = map_infos->maps[i];
if (!strstr(map.path, "libandroid_runtime.so")) continue;
android_runtime_inode = map.inode;
android_runtime_dev = map.dev;
LOGD("Found libandroid_runtime.so at [%zu:%lu]", android_runtime_dev, android_runtime_inode);
break;
} }
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, fork); PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, fork);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, strdup); PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, strdup);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, property_get); PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, property_get);
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, _ZNK18FileDescriptorInfo14ReopenOrDetachERKNSt3__18functionIFvNS0_12basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEEEE); PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, _ZNK18FileDescriptorInfo14ReopenOrDetachERKNSt3__18functionIFvNS0_12basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEEEE);
hook_commit(); hook_commit(map_infos);
lsplt_free_maps(map_infos);
// Remove unhooked methods // Remove unhooked methods
plt_hook_list->erase( plt_hook_list->erase(
@@ -1037,11 +1071,22 @@ static void hook_unloader() {
ino_t art_inode = 0; ino_t art_inode = 0;
dev_t art_dev = 0; dev_t art_dev = 0;
cached_map_infos = lsplt::MapInfo::Scan(); struct lsplt_map_info *map_infos = lsplt_scan_maps("self");
for (auto &map : cached_map_infos) { if (!map_infos) {
if (map.path.ends_with("/libart.so")) { LOGE("Failed to scan maps for self");
return;
}
for (size_t i = 0; i < map_infos->length; i++) {
struct lsplt_map_entry map = map_infos->maps[i];
if (strstr(map.path, "/libart.so") != nullptr) {
art_inode = map.inode; art_inode = map.inode;
art_dev = map.dev; art_dev = map.dev;
LOGD("Found libart.so at [%zu:%lu]", art_dev, art_inode);
break; break;
} }
} }
@@ -1056,24 +1101,25 @@ static void hook_unloader() {
LOGE("virtual map for libart.so is not cached"); LOGE("virtual map for libart.so is not cached");
hooked_unloader = false; hooked_unloader = false;
return;
} else { } else {
LOGD("hook_unloader called with libart.so [%zu:%lu]", art_dev, art_inode); LOGD("hook_unloader called with libart.so [%zu:%lu]", art_dev, art_inode);
PLT_HOOK_REGISTER(art_dev, art_inode, pthread_attr_setstacksize);
hook_commit(map_infos);
} }
PLT_HOOK_REGISTER(art_dev, art_inode, pthread_attr_setstacksize);
hook_commit(); lsplt_free_maps(map_infos);
} }
static void unhook_functions() { static void unhook_functions() {
// Unhook plt_hook // Unhook plt_hook
for (const auto &[dev, inode, sym, old_func] : *plt_hook_list) { for (const auto &[dev, inode, sym, old_func] : *plt_hook_list) {
if (!lsplt::RegisterHook(dev, inode, sym, *old_func, nullptr)) { if (!lsplt_register_hook(dev, inode, sym, *old_func, NULL)) {
LOGE("Failed to register plt_hook [%s]", sym); LOGE("Failed to register plt_hook [%s]", sym);
} }
} }
delete plt_hook_list; delete plt_hook_list;
if (!hook_commit()) { if (!hook_commit(NULL)) {
LOGE("Failed to restore plt_hook"); LOGE("Failed to restore plt_hook");
should_unmap_zygisk = false; should_unmap_zygisk = false;
} }

View File

@@ -558,7 +558,15 @@ void wait_for_trace(int pid, int *status, int flags) {
exit(1); exit(1);
} }
if (*status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) { /* INFO: We'll fork there. This will signal SIGCHLD. We just ignore and continue
to avoid blocking/not continuing. */
if (WSTOPSIG(*status) == SIGCHLD) {
LOGI("process %d stopped by SIGCHLD, continue", pid);
ptrace(PTRACE_CONT, pid, 0, 0);
continue;
} else if (*status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
tracee_skip_syscall(pid); tracee_skip_syscall(pid);
ptrace(PTRACE_CONT, pid, 0, 0); ptrace(PTRACE_CONT, pid, 0, 0);