improve: hook pthread_attr_setstacksize

Relying on dlclose to unload libzygisk.so will block us to clean its trace in the solist.
This commit allows us to unmap libzygisk.so without using dlclose.
To call munmap, we use the function pthread_attr_setstacksize instead of pthread_attr_destroy, so that tail-call can still be applied here since it has the same signature as munmap.
This commit is contained in:
JingMatrix
2024-12-12 15:57:41 +01:00
parent 8fb5d9197a
commit 5e43e4a71b
4 changed files with 34 additions and 18 deletions

View File

@@ -1,16 +1,17 @@
#include "daemon.h"
#include "logging.h"
#include "zygisk.hpp"
#include "module.hpp"
using namespace std;
void *self_handle = nullptr;
void *start_addr = nullptr;
size_t block_size = 0;
extern "C" [[gnu::visibility("default")]]
void entry(void* handle, const char* path) {
void entry(void* addr, size_t size, const char* path) {
LOGI("Zygisk library injected, version %s", ZKSU_VERSION);
self_handle = handle;
start_addr = addr;
block_size = size;
zygiskd::Init(path);
if (!zygiskd::PingHeartbeat()) {
@@ -22,6 +23,6 @@ void entry(void* handle, const char* path) {
logging::setfd(zygiskd::RequestLogcatFd());
#endif
LOGI("Start hooking");
LOGI("Start hooking, call %p", hook_functions);
hook_functions();
}

View File

@@ -176,22 +176,24 @@ DCL_HOOK_FUNC(void, android_log_close) {
// We cannot directly call `dlclose` to unload ourselves, otherwise when `dlclose` returns,
// it will return to our code which has been unmapped, causing segmentation fault.
// Instead, we hook `pthread_attr_destroy` which will be called when VM daemon threads start.
DCL_HOOK_FUNC(int, pthread_attr_destroy, void *target) {
int res = old_pthread_attr_destroy((pthread_attr_t *)target);
// Instead, we hook `pthread_attr_setstacksize` which will be called when VM daemon threads start.
DCL_HOOK_FUNC(int, pthread_attr_setstacksize, void *target, size_t size) {
int res = old_pthread_attr_setstacksize((pthread_attr_t *)target, size);
LOGV("Call pthread_attr_setstacksize in [tid, pid]: %d, %d", gettid(), getpid());
// Only perform unloading on the main thread
if (gettid() != getpid())
return res;
LOGV("pthread_attr_destroy");
LOGV("Clean zygisk reminders");
if (should_unmap_zygisk) {
unhook_functions();
if (should_unmap_zygisk) {
// Because both `pthread_attr_destroy` and `dlclose` have the same function signature,
// Because both `pthread_attr_setstacksize` and `dlclose` have the same function signature,
// we can use `musttail` to let the compiler reuse our stack frame and thus
// `dlclose` will directly return to the caller of `pthread_attr_destroy`.
[[clang::musttail]] return dlclose(self_handle);
// `dlclose` will directly return to the caller of `pthread_attr_setstacksize`.
LOGV("Unmap libzygisk.so");
[[clang::musttail]] return munmap(start_addr, block_size);
}
}
@@ -858,7 +860,7 @@ static void hook_unloader() {
}
}
PLT_HOOK_REGISTER(art_dev, art_inode, pthread_attr_destroy);
PLT_HOOK_REGISTER(art_dev, art_inode, pthread_attr_setstacksize);
hook_commit();
}

View File

@@ -1,10 +1,9 @@
#pragma once
#include <stdint.h>
#include <jni.h>
#include <vector>
extern void *self_handle;
extern void *start_addr;
extern size_t block_size;
void hook_functions();

View File

@@ -222,10 +222,24 @@ bool inject_on_main(int pid, const char *lib_path) {
return false;
}
/* call injector entry(handle, path) */
/* record the address range of libzygisk.so */
map = MapInfo::Scan(std::to_string(pid));
void *start_addr = nullptr;
size_t block_size = 0;
for (auto &info : map) {
if (strstr(info.path.c_str(), "libzygisk.so")) {
void *addr = (void *)info.start;
if (start_addr == nullptr) start_addr = addr;
size_t size = info.end - info.start;
block_size += size;
LOGD("found block %s: [%p-%p] with size %zu", info.path.c_str(), addr, (void *)info.end, size);
}
}
/* call injector entry(start_addr, block_size, path) */
args.clear();
args.push_back(remote_handle);
args.push_back((uintptr_t) start_addr);
args.push_back(block_size);
str = push_string(pid, regs, zygiskd::GetTmpPath().c_str());
args.push_back((long) str);