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..3ba86fd52e8 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 with id = " + id); } } } catch (IOException e) { diff --git a/dex2oat/src/main/cpp/CMakeLists.txt b/dex2oat/src/main/cpp/CMakeLists.txt index 748b3743177..6f0998caafb 100644 --- a/dex2oat/src/main/cpp/CMakeLists.txt +++ b/dex2oat/src/main/cpp/CMakeLists.txt @@ -2,8 +2,10 @@ cmake_minimum_required(VERSION 3.10) project(dex2oat) add_executable(dex2oat dex2oat.c) +add_library(oat_hook SHARED oat_hook.c) target_link_libraries(dex2oat log) +target_link_libraries(oat_hook log) 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..c376f32bd82 100644 --- a/dex2oat/src/main/cpp/dex2oat.c +++ b/dex2oat/src/main/cpp/dex2oat.c @@ -22,11 +22,14 @@ // #include +#include #include #include #include +#include #include #include +#include #include #include "logging.h" @@ -91,11 +94,58 @@ static void write_int(int fd, int val) { write(fd, &val, sizeof(val)); } +static int save_fd_to_file(int src_fd, const char *filepath) { + int dest_fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (dest_fd == -1) { + PLOGE("open %s", filepath); + return -1; + } + + char buffer[512]; + ssize_t bytes_read; + + while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) { + ssize_t bytes_written = 0; + LOGD("%zd bytes read", bytes_read); + while (bytes_written < bytes_read) { + ssize_t bytes_now = write(dest_fd, buffer + bytes_written, bytes_read - bytes_written); + if (bytes_now == -1) { + PLOGE("write to %s", filepath); + close(dest_fd); + return -1; + } + bytes_written += bytes_now; + LOGD("%zd bytes written", bytes_written); + } + } + + if (bytes_read == -1) { + PLOGE("read %d", src_fd); + close(dest_fd); + return -1; + } + + // Ensure data is written to disk + if (fsync(dest_fd) == -1) { + PLOGE("fsync %d", dest_fd); + close(dest_fd); + return -1; + } + + if (close(dest_fd) == -1) { + PLOGE("close %d", dest_fd); + return -1; + } + + return 0; // Success +} + int main(int argc, char **argv) { LOGD("dex2oat wrapper ppid=%d", getppid()); 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,24 +156,49 @@ 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); + char *oat_path; const char *new_argv[argc + 2]; - for (int i = 0; i < argc; i++) new_argv[i] = argv[i]; + for (int i = 0; i < argc; i++) { + new_argv[i] = argv[i]; + if (strstr(argv[i], "--oat-location") != NULL) { + oat_path = strstr(strdup(argv[i]), "/data/"); + } + } new_argv[argc] = "--inline-max-code-units=0"; 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 + if (save_fd_to_file(hooker_fd, oat_path) == 0) { + LOGD("liboat_hook.so written to %s", oat_path); + } + close(hooker_fd); + const int STRING_BUFFER = 200; + char env_str[STRING_BUFFER]; + snprintf(env_str, STRING_BUFFER, "LD_PRELOAD=%s", oat_path); + putenv(env_str); + fexecve(stock_fd, (char **)new_argv, environ); + PLOGE("fexecve failed"); return 2; } diff --git a/dex2oat/src/main/cpp/oat_hook.c b/dex2oat/src/main/cpp/oat_hook.c new file mode 100644 index 00000000000..97575051aac --- /dev/null +++ b/dex2oat/src/main/cpp/oat_hook.c @@ -0,0 +1,25 @@ +#include +#include +#include + +#include "logging.h" + +typedef void* (*OatHeader_GetKeyValueStore_t)(const void*); + +OatHeader_GetKeyValueStore_t real_OatHeader_GetKeyValueStore; + +void* _ZNK3art9OatHeader16GetKeyValueStoreEv(const void* header) { + if (!real_OatHeader_GetKeyValueStore) { + real_OatHeader_GetKeyValueStore = (OatHeader_GetKeyValueStore_t)dlsym( + RTLD_NEXT, "_ZNK3art9OatHeader16GetKeyValueStoreEv"); + if (!real_OatHeader_GetKeyValueStore) { + fprintf(stderr, "Error resolving symbol: _ZNK3art9OatHeader16GetKeyValueStoreEv\n"); + exit(1); + } + } + + // Here you can add your custom logic before calling the original function + LOGD("OatHeader_GetKeyValueStore() called on object at %p\n", header); + + return real_OatHeader_GetKeyValueStore(header); +} 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