Hook dex2oat functions to remove LSPosed traces (#152)
We use the env LD_PRELOAD to hook the execution of `dex2oat`, which can be directly set to be a file descriptor.
This commit is contained in:
parent
7cd98e6d48
commit
c0478f593e
|
|
@ -16,3 +16,6 @@
|
||||||
[submodule "apache/commons-lang"]
|
[submodule "apache/commons-lang"]
|
||||||
path = apache/commons-lang
|
path = apache/commons-lang
|
||||||
url = https://github.com/apache/commons-lang
|
url = https://github.com/apache/commons-lang
|
||||||
|
[submodule "external/lsplt"]
|
||||||
|
path = external/lsplt
|
||||||
|
url = https://github.com/JingMatrix/LSPlt
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@ public class Dex2OatService implements Runnable {
|
||||||
private static final String WRAPPER32 = "bin/dex2oat32";
|
private static final String WRAPPER32 = "bin/dex2oat32";
|
||||||
private static final String WRAPPER64 = "bin/dex2oat64";
|
private static final String WRAPPER64 = "bin/dex2oat64";
|
||||||
|
|
||||||
private final String[] dex2oatArray = new String[4];
|
private final String[] dex2oatArray = new String[6];
|
||||||
private final FileDescriptor[] fdArray = new FileDescriptor[4];
|
private final FileDescriptor[] fdArray = new FileDescriptor[6];
|
||||||
private final FileObserver selinuxObserver;
|
private final FileObserver selinuxObserver;
|
||||||
private int compatibility = DEX2OAT_OK;
|
private int compatibility = DEX2OAT_OK;
|
||||||
|
|
||||||
|
|
@ -76,6 +76,9 @@ public class Dex2OatService implements Runnable {
|
||||||
openDex2oat(3, "/apex/com.android.art/bin/dex2oatd64");
|
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 enforce = Paths.get("/sys/fs/selinux/enforce");
|
||||||
var policy = Paths.get("/sys/fs/selinux/policy");
|
var policy = Paths.get("/sys/fs/selinux/policy");
|
||||||
var list = new ArrayList<File>();
|
var list = new ArrayList<File>();
|
||||||
|
|
@ -126,7 +129,7 @@ public class Dex2OatService implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean notMounted() {
|
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];
|
var bin = dex2oatArray[i];
|
||||||
if (bin == null) continue;
|
if (bin == null) continue;
|
||||||
try {
|
try {
|
||||||
|
|
@ -193,8 +196,7 @@ public class Dex2OatService implements Runnable {
|
||||||
var fd = new FileDescriptor[]{fdArray[id]};
|
var fd = new FileDescriptor[]{fdArray[id]};
|
||||||
client.setFileDescriptorsForSend(fd);
|
client.setFileDescriptorsForSend(fd);
|
||||||
os.write(1);
|
os.write(1);
|
||||||
Log.d(TAG, "Sent stock fd: is64 = " + ((id & 0b10) != 0) +
|
Log.d(TAG, "Sent fd of " + dex2oatArray[id]);
|
||||||
", isDebug = " + ((id & 0b01) != 0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,13 @@ cmake_minimum_required(VERSION 3.10)
|
||||||
project(dex2oat)
|
project(dex2oat)
|
||||||
|
|
||||||
add_executable(dex2oat dex2oat.c)
|
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(dex2oat log)
|
||||||
|
target_link_libraries(oat_hook log lsplt_static)
|
||||||
|
|
||||||
if (DEFINED DEBUG_SYMBOLS_PATH)
|
if (DEFINED DEBUG_SYMBOLS_PATH)
|
||||||
message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}")
|
message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}")
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
// Created by Nullptr on 2022/4/1.
|
// Created by Nullptr on 2022/4/1.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -96,6 +95,7 @@ int main(int argc, char **argv) {
|
||||||
struct sockaddr_un sock = {};
|
struct sockaddr_un sock = {};
|
||||||
sock.sun_family = AF_UNIX;
|
sock.sun_family = AF_UNIX;
|
||||||
strlcpy(sock.sun_path + 1, kSockName, sizeof(sock.sun_path) - 1);
|
strlcpy(sock.sun_path + 1, kSockName, sizeof(sock.sun_path) - 1);
|
||||||
|
|
||||||
int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
size_t len = sizeof(sa_family_t) + strlen(sock.sun_path + 1) + 1;
|
size_t len = sizeof(sa_family_t) + strlen(sock.sun_path + 1) + 1;
|
||||||
if (connect(sock_fd, (struct sockaddr *)&sock, len)) {
|
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);
|
int stock_fd = recv_fd(sock_fd);
|
||||||
read_int(sock_fd);
|
read_int(sock_fd);
|
||||||
close(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);
|
LOGD("sock: %s %d", sock.sun_path + 1, stock_fd);
|
||||||
|
|
||||||
const char *new_argv[argc + 2];
|
const char *new_argv[argc + 2];
|
||||||
|
|
@ -114,16 +125,20 @@ int main(int argc, char **argv) {
|
||||||
new_argv[argc + 1] = NULL;
|
new_argv[argc + 1] = NULL;
|
||||||
|
|
||||||
if (getenv("LD_LIBRARY_PATH") == NULL) {
|
if (getenv("LD_LIBRARY_PATH") == NULL) {
|
||||||
#if defined(__LP64__)
|
char const *libenv = LP_SELECT(
|
||||||
char const *libenv =
|
"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";
|
"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
|
|
||||||
putenv((char *)libenv);
|
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);
|
fexecve(stock_fd, (char **)new_argv, environ);
|
||||||
|
|
||||||
PLOGE("fexecve failed");
|
PLOGE("fexecve failed");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
#ifndef LOG_TAG
|
#ifndef LOG_TAG
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 1516b06634198360b94fbd7ab6a712052c5c9a23
|
||||||
|
|
@ -221,6 +221,7 @@ fun afterEval() = android.applicationVariants.forEach { variant ->
|
||||||
into("bin") {
|
into("bin") {
|
||||||
from(project(":dex2oat").layout.buildDirectory.dir("intermediates/cmake/$buildTypeLowered/obj")) {
|
from(project(":dex2oat").layout.buildDirectory.dir("intermediates/cmake/$buildTypeLowered/obj")) {
|
||||||
include("**/dex2oat")
|
include("**/dex2oat")
|
||||||
|
include("**/liboat_hook.so")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val dexOutPath = if (buildTypeLowered == "release")
|
val dexOutPath = if (buildTypeLowered == "release")
|
||||||
|
|
|
||||||
|
|
@ -123,19 +123,27 @@ if [ "$API" -ge 29 ]; then
|
||||||
|
|
||||||
if [ "$ARCH" = "arm" ] || [ "$ARCH" = "arm64" ]; then
|
if [ "$ARCH" = "arm" ] || [ "$ARCH" = "arm64" ]; then
|
||||||
extract "$ZIPFILE" "bin/armeabi-v7a/dex2oat" "$MODPATH/bin" true
|
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/dex2oat" "$MODPATH/bin/dex2oat32"
|
||||||
|
mv "$MODPATH/bin/liboat_hook.so" "$MODPATH/bin/liboat_hook32.so"
|
||||||
|
|
||||||
if [ "$IS64BIT" = true ]; then
|
if [ "$IS64BIT" = true ]; then
|
||||||
extract "$ZIPFILE" "bin/arm64-v8a/dex2oat" "$MODPATH/bin" true
|
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/dex2oat" "$MODPATH/bin/dex2oat64"
|
||||||
|
mv "$MODPATH/bin/liboat_hook.so" "$MODPATH/bin/liboat_hook64.so"
|
||||||
fi
|
fi
|
||||||
elif [ "$ARCH" == "x86" ] || [ "$ARCH" == "x64" ]; then
|
elif [ "$ARCH" == "x86" ] || [ "$ARCH" == "x64" ]; then
|
||||||
extract "$ZIPFILE" "bin/x86/dex2oat" "$MODPATH/bin" true
|
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/dex2oat" "$MODPATH/bin/dex2oat32"
|
||||||
|
mv "$MODPATH/bin/liboat_hook.so" "$MODPATH/bin/liboat_hook32.so"
|
||||||
|
|
||||||
if [ "$IS64BIT" = true ]; then
|
if [ "$IS64BIT" = true ]; then
|
||||||
extract "$ZIPFILE" "bin/x86_64/dex2oat" "$MODPATH/bin" true
|
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/dex2oat" "$MODPATH/bin/dex2oat64"
|
||||||
|
mv "$MODPATH/bin/liboat_hook.so" "$MODPATH/bin/liboat_hook64.so"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ allow shell shell dir write
|
||||||
|
|
||||||
type xposed_file file_type
|
type xposed_file file_type
|
||||||
typeattribute xposed_file mlstrustedobject
|
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
|
type xposed_data file_type
|
||||||
typeattribute xposed_data mlstrustedobject
|
typeattribute xposed_data mlstrustedobject
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue