Skip to content

Commit

Permalink
Hook dex2oat functions to remove LSPosed traces
Browse files Browse the repository at this point in the history
We use LD_PRELOAD to hook the execution of `dex2oat`.
To do so, we first overwrite the output file by `liboat_hook.so`, which is given by the parameter `--oat-location`.
However, we always get permission error of opening the target `base.odex` file in function `save_fd_to_file`.
  • Loading branch information
JingMatrix committed Jan 5, 2025
1 parent fd25732 commit ae97def
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<File>();
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions dex2oat/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand Down
91 changes: 83 additions & 8 deletions dex2oat/src/main/cpp/dex2oat.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
//

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>

#include "logging.h"
Expand Down Expand Up @@ -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)) {
Expand All @@ -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;
}
25 changes: 25 additions & 0 deletions dex2oat/src/main/cpp/oat_hook.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

#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);
}
1 change: 1 addition & 0 deletions magisk-loader/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
8 changes: 8 additions & 0 deletions magisk-loader/magisk_module/customize.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit ae97def

Please sign in to comment.