Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hook dex2oat functions to remove LSPosed traces #152

Merged
merged 3 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
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 of " + dex2oatArray[id]);
}
}
} catch (IOException e) {
Expand Down
5 changes: 5 additions & 0 deletions dex2oat/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand Down
31 changes: 23 additions & 8 deletions dex2oat/src/main/cpp/dex2oat.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
// Created by Nullptr on 2022/4/1.
//

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -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)) {
Expand All @@ -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];
Expand All @@ -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;
}
1 change: 1 addition & 0 deletions dex2oat/src/main/cpp/logging.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <errno.h>
#include <android/log.h>

#ifndef LOG_TAG
Expand Down
116 changes: 116 additions & 0 deletions dex2oat/src/main/cpp/oat_hook.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include <dlfcn.h>

#include <lsplt.hpp>
#include <map>
#include <string>
#include <string_view>
#include <vector>

#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<std::string, std::string>* key_value, uint8_t* store) {
LOGD("updating KeyValueStore");
char* data_ptr = reinterpret_cast<char*>(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<const char*>(key_value_store_);
const char* end = ptr + key_value_store_size_;
std::map<std::string, std::string> 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<const char*>(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<const char*>(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<void*>(new_##NAME), \
reinterpret_cast<void**>(&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");
};
}
1 change: 1 addition & 0 deletions external/lsplt
Submodule lsplt added at 1516b0
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
2 changes: 1 addition & 1 deletion magisk-loader/magisk_module/sepolicy.rule
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading