diff --git a/.gitmodules b/.gitmodules index 1d2f57253c7..631c3b5209b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "apache/commons-lang"] path = apache/commons-lang url = https://github.com/apache/commons-lang +[submodule "external/lsplt"] + path = external/lsplt + url = https://github.com/JingMatrix/LSPlt diff --git a/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java b/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java index 4f1f5d8f766..627f17afb10 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java @@ -51,8 +51,8 @@ public class Dex2OatService implements Runnable { private static final String WRAPPER32 = "bin/dex2oat32"; private static final String WRAPPER64 = "bin/dex2oat64"; - private final String[] dex2oatArray = new String[4]; - private final FileDescriptor[] fdArray = new FileDescriptor[4]; + private final String[] dex2oatArray = new String[6]; + private final FileDescriptor[] fdArray = new FileDescriptor[6]; private final FileObserver selinuxObserver; private int compatibility = DEX2OAT_OK; @@ -76,6 +76,9 @@ public Dex2OatService() { openDex2oat(3, "/apex/com.android.art/bin/dex2oatd64"); } + openDex2oat(4, "/data/adb/modules/zygisk_lsposed/bin/liboat_hook32.so"); + openDex2oat(5, "/data/adb/modules/zygisk_lsposed/bin/liboat_hook64.so"); + var enforce = Paths.get("/sys/fs/selinux/enforce"); var policy = Paths.get("/sys/fs/selinux/policy"); var list = new ArrayList(); @@ -126,7 +129,7 @@ public void stopWatching() { } private boolean notMounted() { - for (int i = 0; i < dex2oatArray.length; i++) { + for (int i = 0; i < dex2oatArray.length && i < 4; i++) { var bin = dex2oatArray[i]; if (bin == null) continue; try { @@ -193,8 +196,7 @@ public void run() { var fd = new FileDescriptor[]{fdArray[id]}; client.setFileDescriptorsForSend(fd); os.write(1); - Log.d(TAG, "Sent stock fd: is64 = " + ((id & 0b10) != 0) + - ", isDebug = " + ((id & 0b01) != 0)); + Log.d(TAG, "Sent fd of " + dex2oatArray[id]); } } } catch (IOException e) { diff --git a/dex2oat/src/main/cpp/CMakeLists.txt b/dex2oat/src/main/cpp/CMakeLists.txt index 748b3743177..bff9855f30b 100644 --- a/dex2oat/src/main/cpp/CMakeLists.txt +++ b/dex2oat/src/main/cpp/CMakeLists.txt @@ -2,8 +2,13 @@ cmake_minimum_required(VERSION 3.10) project(dex2oat) add_executable(dex2oat dex2oat.c) +add_library(oat_hook SHARED oat_hook.cpp) + +OPTION(LSPLT_BUILD_SHARED OFF) +add_subdirectory(${EXTERNAL_ROOT}/lsplt/lsplt/src/main/jni external) target_link_libraries(dex2oat log) +target_link_libraries(oat_hook log lsplt_static) if (DEFINED DEBUG_SYMBOLS_PATH) message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}") diff --git a/dex2oat/src/main/cpp/dex2oat.c b/dex2oat/src/main/cpp/dex2oat.c index 73c526d3b1d..b3b530243f0 100644 --- a/dex2oat/src/main/cpp/dex2oat.c +++ b/dex2oat/src/main/cpp/dex2oat.c @@ -21,7 +21,6 @@ // Created by Nullptr on 2022/4/1. // -#include #include #include #include @@ -96,6 +95,7 @@ int main(int argc, char **argv) { struct sockaddr_un sock = {}; sock.sun_family = AF_UNIX; strlcpy(sock.sun_path + 1, kSockName, sizeof(sock.sun_path) - 1); + int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); size_t len = sizeof(sa_family_t) + strlen(sock.sun_path + 1) + 1; if (connect(sock_fd, (struct sockaddr *)&sock, len)) { @@ -106,6 +106,17 @@ int main(int argc, char **argv) { int stock_fd = recv_fd(sock_fd); read_int(sock_fd); close(sock_fd); + + sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (connect(sock_fd, (struct sockaddr *)&sock, len)) { + PLOGE("failed to connect to %s", sock.sun_path + 1); + return 1; + } + write_int(sock_fd, LP_SELECT(4, 5)); + int hooker_fd = recv_fd(sock_fd); + read_int(sock_fd); + close(sock_fd); + LOGD("sock: %s %d", sock.sun_path + 1, stock_fd); const char *new_argv[argc + 2]; @@ -114,16 +125,20 @@ int main(int argc, char **argv) { new_argv[argc + 1] = NULL; if (getenv("LD_LIBRARY_PATH") == NULL) { -#if defined(__LP64__) - char const *libenv = - "LD_LIBRARY_PATH=/apex/com.android.art/lib64:/apex/com.android.os.statsd/lib64"; -#else - char const *libenv = - "LD_LIBRARY_PATH=/apex/com.android.art/lib:/apex/com.android.os.statsd/lib"; -#endif + char const *libenv = LP_SELECT( + "LD_LIBRARY_PATH=/apex/com.android.art/lib:/apex/com.android.os.statsd/lib", + "LD_LIBRARY_PATH=/apex/com.android.art/lib64:/apex/com.android.os.statsd/lib64"); putenv((char *)libenv); } + + // Set LD_PRELOAD to load liboat_hook.so + const int STRING_BUFFER = 50; + char env_str[STRING_BUFFER]; + snprintf(env_str, STRING_BUFFER, "LD_PRELOAD=/proc/%d/fd/%d", getpid(), hooker_fd); + putenv(env_str); + fexecve(stock_fd, (char **)new_argv, environ); + PLOGE("fexecve failed"); return 2; } diff --git a/dex2oat/src/main/cpp/logging.h b/dex2oat/src/main/cpp/logging.h index 829ebcfde20..07574fa73cc 100644 --- a/dex2oat/src/main/cpp/logging.h +++ b/dex2oat/src/main/cpp/logging.h @@ -1,5 +1,6 @@ #pragma once +#include #include #ifndef LOG_TAG diff --git a/dex2oat/src/main/cpp/oat_hook.cpp b/dex2oat/src/main/cpp/oat_hook.cpp new file mode 100644 index 00000000000..53510424f7d --- /dev/null +++ b/dex2oat/src/main/cpp/oat_hook.cpp @@ -0,0 +1,116 @@ +#include + +#include +#include +#include +#include +#include + +#include "logging.h" + +const std::string_view parameter_to_remove = " --inline-max-code-units=0"; + +#define DCL_HOOK_FUNC(ret, func, ...) \ + ret (*old_##func)(__VA_ARGS__); \ + ret new_##func(__VA_ARGS__) + +bool store_updated = false; + +void UpdateKeyValueStore(std::map* key_value, uint8_t* store) { + LOGD("updating KeyValueStore"); + char* data_ptr = reinterpret_cast(store); + if (key_value != nullptr) { + auto it = key_value->begin(); + auto end = key_value->end(); + for (; it != end; ++it) { + strlcpy(data_ptr, it->first.c_str(), it->first.length() + 1); + data_ptr += it->first.length() + 1; + strlcpy(data_ptr, it->second.c_str(), it->second.length() + 1); + data_ptr += it->second.length() + 1; + } + } + LOGD("KeyValueStore updated"); + store_updated = true; +} + +DCL_HOOK_FUNC(uint32_t, _ZNK3art9OatHeader20GetKeyValueStoreSizeEv, void* header) { + uint32_t size = old__ZNK3art9OatHeader20GetKeyValueStoreSizeEv(header); + if (store_updated) { + LOGD("OatHeader::GetKeyValueStoreSize() called on object at %p\n", header); + size = size - parameter_to_remove.size(); + } + return size; +} + +DCL_HOOK_FUNC(uint8_t*, _ZNK3art9OatHeader16GetKeyValueStoreEv, void* header) { + LOGD("OatHeader::GetKeyValueStore() called on object at %p\n", header); + uint8_t* key_value_store_ = old__ZNK3art9OatHeader16GetKeyValueStoreEv(header); + uint32_t key_value_store_size_ = old__ZNK3art9OatHeader20GetKeyValueStoreSizeEv(header); + const char* ptr = reinterpret_cast(key_value_store_); + const char* end = ptr + key_value_store_size_; + std::map new_store = {}; + + LOGD("scanning [%p-%p] for oat headers", ptr, end); + while (ptr < end) { + // Scan for a closing zero. + const char* str_end = reinterpret_cast(memchr(ptr, 0, end - ptr)); + if (str_end == nullptr) [[unlikely]] { + LOGE("failed to find str_end"); + return key_value_store_; + } + std::string_view key = std::string_view(ptr, str_end - ptr); + const char* value_start = str_end + 1; + const char* value_end = + reinterpret_cast(memchr(value_start, 0, end - value_start)); + if (value_end == nullptr) [[unlikely]] { + LOGE("failed to find value_end"); + return key_value_store_; + } + std::string_view value = std::string_view(value_start, value_end - value_start); + LOGV("header %s:%s", key.data(), value.data()); + if (key == "dex2oat-cmdline") { + value = value.substr(0, value.size() - parameter_to_remove.size()); + } + new_store.insert(std::make_pair(std::string(key), std::string(value))); + // Different from key. Advance over the value. + ptr = value_end + 1; + } + UpdateKeyValueStore(&new_store, key_value_store_); + + return key_value_store_; +} + +#undef DCL_HOOK_FUNC + +void register_hook(dev_t dev, ino_t inode, const char* symbol, void* new_func, void** old_func) { + LOGD("RegisterHook: %s, %p, %p", symbol, new_func, old_func); + if (!lsplt::RegisterHook(dev, inode, symbol, new_func, old_func)) { + LOGE("Failed to register plt_hook \"%s\"\n", symbol); + return; + } +} + +#define PLT_HOOK_REGISTER_SYM(DEV, INODE, SYM, NAME) \ + register_hook(DEV, INODE, SYM, reinterpret_cast(new_##NAME), \ + reinterpret_cast(&old_##NAME)) + +#define PLT_HOOK_REGISTER(DEV, INODE, NAME) PLT_HOOK_REGISTER_SYM(DEV, INODE, #NAME, NAME) + +__attribute__((constructor)) static void initialize() { + dev_t dev = 0; + ino_t inode = 0; + for (auto& info : lsplt::MapInfo::Scan()) { + if (info.path.starts_with("/apex/com.android.art/bin/dex2oat")) { + dev = info.dev; + inode = info.inode; + break; + } + } + LOGD("dex2oat binary %lu:%lu", dev, inode); + + PLT_HOOK_REGISTER(dev, inode, _ZNK3art9OatHeader20GetKeyValueStoreSizeEv); + PLT_HOOK_REGISTER(dev, inode, _ZNK3art9OatHeader16GetKeyValueStoreEv); + if (lsplt::CommitHook()) { + LOGD("lsplt hooks done"); + }; +} diff --git a/external/lsplt b/external/lsplt new file mode 160000 index 00000000000..1516b066341 --- /dev/null +++ b/external/lsplt @@ -0,0 +1 @@ +Subproject commit 1516b06634198360b94fbd7ab6a712052c5c9a23 diff --git a/magisk-loader/build.gradle.kts b/magisk-loader/build.gradle.kts index cf3d0570782..82f6ba878d9 100644 --- a/magisk-loader/build.gradle.kts +++ b/magisk-loader/build.gradle.kts @@ -221,6 +221,7 @@ fun afterEval() = android.applicationVariants.forEach { variant -> into("bin") { from(project(":dex2oat").layout.buildDirectory.dir("intermediates/cmake/$buildTypeLowered/obj")) { include("**/dex2oat") + include("**/liboat_hook.so") } } val dexOutPath = if (buildTypeLowered == "release") diff --git a/magisk-loader/magisk_module/customize.sh b/magisk-loader/magisk_module/customize.sh index 55ed29c14bf..f1a12db2269 100644 --- a/magisk-loader/magisk_module/customize.sh +++ b/magisk-loader/magisk_module/customize.sh @@ -123,19 +123,27 @@ if [ "$API" -ge 29 ]; then if [ "$ARCH" = "arm" ] || [ "$ARCH" = "arm64" ]; then extract "$ZIPFILE" "bin/armeabi-v7a/dex2oat" "$MODPATH/bin" true + extract "$ZIPFILE" "bin/armeabi-v7a/liboat_hook.so" "$MODPATH/bin" true mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat32" + mv "$MODPATH/bin/liboat_hook.so" "$MODPATH/bin/liboat_hook32.so" if [ "$IS64BIT" = true ]; then extract "$ZIPFILE" "bin/arm64-v8a/dex2oat" "$MODPATH/bin" true + extract "$ZIPFILE" "bin/arm64-v8a/liboat_hook.so" "$MODPATH/bin" true mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat64" + mv "$MODPATH/bin/liboat_hook.so" "$MODPATH/bin/liboat_hook64.so" fi elif [ "$ARCH" == "x86" ] || [ "$ARCH" == "x64" ]; then extract "$ZIPFILE" "bin/x86/dex2oat" "$MODPATH/bin" true + extract "$ZIPFILE" "bin/x86/liboat_hook.so" "$MODPATH/bin" true mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat32" + mv "$MODPATH/bin/liboat_hook.so" "$MODPATH/bin/liboat_hook32.so" if [ "$IS64BIT" = true ]; then extract "$ZIPFILE" "bin/x86_64/dex2oat" "$MODPATH/bin" true + extract "$ZIPFILE" "bin/x86_64/liboat_hook.so" "$MODPATH/bin" true mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat64" + mv "$MODPATH/bin/liboat_hook.so" "$MODPATH/bin/liboat_hook64.so" fi fi diff --git a/magisk-loader/magisk_module/sepolicy.rule b/magisk-loader/magisk_module/sepolicy.rule index bfbef3d8318..30b56f22826 100644 --- a/magisk-loader/magisk_module/sepolicy.rule +++ b/magisk-loader/magisk_module/sepolicy.rule @@ -4,7 +4,7 @@ allow shell shell dir write type xposed_file file_type typeattribute xposed_file mlstrustedobject -allow {installd isolated_app shell} xposed_file {file dir} * +allow {dex2oat installd isolated_app shell} xposed_file {file dir} * type xposed_data file_type typeattribute xposed_data mlstrustedobject