Rework hooking to avoid polluting global namespace with too many symbols

This commit is contained in:
Billy Laws 2021-12-18 22:50:02 +00:00
parent 332425ac2e
commit bec42142cf
11 changed files with 205 additions and 141 deletions

View file

@ -4,11 +4,8 @@ if(NOT ${CMAKE_ANDROID_ARCH_ABI} STREQUAL arm64-v8a)
message(FATAL_ERROR "Unsupported target architecture: ${CMAKE_ANDROID_ARCH_ABI}. Please make an issue on the repo!") message(FATAL_ERROR "Unsupported target architecture: ${CMAKE_ANDROID_ARCH_ABI}. Please make an issue on the repo!")
endif() endif()
project(adrenotools LANGUAGES CXX) project(adrenotools LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 17)
include_directories(lib/linkernsbypass)
add_subdirectory(lib/linkernsbypass) add_subdirectory(lib/linkernsbypass)
set(LIB_SOURCES src/bcenabler.cpp set(LIB_SOURCES src/bcenabler.cpp

@ -1 +1 @@
Subproject commit 8a819a35e1585b8adfd95174299901362b57b154 Subproject commit 341f6e2d585c9501ad044338da34797c060aacf3

View file

@ -8,11 +8,11 @@
#include <android/api-level.h> #include <android/api-level.h>
#include <android/log.h> #include <android/log.h>
#include <android_linker_ns.h> #include <android_linker_ns.h>
#include "hook/main_hook.h" #include "hook/hook_impl_params.h"
#include <adrenotools/driver.h> #include <adrenotools/driver.h>
void *adrenotools_open_libvulkan(int dlopenMode, int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir) { void *adrenotools_open_libvulkan(int dlopenFlags, int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir) {
// Bail out if linkernsbyapss failed to load, this probably means we're on api < 28 // Bail out if linkernsbypass failed to load, this probably means we're on api < 28
if (!linkernsbypass_load_status()) if (!linkernsbypass_load_status())
return nullptr; return nullptr;
@ -47,8 +47,28 @@ void *adrenotools_open_libvulkan(int dlopenMode, int featureFlags, const char *t
return nullptr; return nullptr;
} }
// Will be destroyed by the libmain_hook.so destructor on unload // Create a namespace that can isolate our hook from the classloader namespace
auto *hookParam{new MainHookParam(featureFlags, tmpLibDir, hookLibDir, customDriverDir, customDriverName, fileRedirectDir)}; auto hookNs{android_create_namespace("adrenotools-libvulkan", hookLibDir, nullptr, ANDROID_NAMESPACE_TYPE_SHARED, nullptr, nullptr)};
return linkernsbypass_dlopen_unique_hooked("/system/lib64/libvulkan.so", tmpLibDir, dlopenMode, hookLibDir, "libmain_hook.so", nullptr, true, reinterpret_cast<void *>(hookParam)); // Link it to the default namespace so the hook can use libandroid etc
if (!linkernsbypass_link_namespace_to_default_all_libs(hookNs))
return nullptr;
// Preload the hook implementation, otherwise we get a weird issue where despite being in NEEDED of the hook lib the hook's symbols will overwrite ours and cause an infinite loop
auto hookImpl{linkernsbypass_namespace_dlopen("libhook_impl.so", RTLD_NOW, hookNs)};
if (!hookImpl)
return nullptr;
// Pass parameters to the hook implementation
auto initHookParam{reinterpret_cast<void (*)(const void *)>(dlsym(hookImpl, "init_hook_param"))};
if (!initHookParam)
return nullptr;
initHookParam(new HookImplParams(featureFlags, tmpLibDir, hookLibDir, customDriverDir, customDriverName, fileRedirectDir));
// Load the libvulkan hook into the isolated namespace
if (!linkernsbypass_namespace_dlopen("libmain_hook.so", RTLD_GLOBAL, hookNs))
return nullptr;
return linkernsbypass_namespace_dlopen_unique("/system/lib64/libvulkan.so", tmpLibDir, dlopenFlags, hookNs);
} }

View file

@ -1,15 +1,20 @@
add_library(main_hook SHARED main_hook.cpp) add_library(hook_impl SHARED hook_impl.cpp hook_impl.h hook_impl_params.h)
target_compile_options(hook_impl PRIVATE -Wall -Wextra)
target_link_libraries(hook_impl linkernsbypass log)
target_include_directories(hook_impl PRIVATE ../../include)
set_target_properties(hook_impl PROPERTIES CXX_VISIBILITY_PRESET hidden)
add_library(main_hook SHARED main_hook.c)
target_compile_options(main_hook PRIVATE -Wall -Wextra) target_compile_options(main_hook PRIVATE -Wall -Wextra)
target_link_options(main_hook PRIVATE -z global) target_link_options(main_hook PRIVATE -z global)
target_link_libraries(main_hook linkernsbypass log) target_link_libraries(main_hook hook_impl)
target_include_directories(main_hook PRIVATE ../../include)
set_target_properties(main_hook PROPERTIES CXX_VISIBILITY_PRESET hidden) set_target_properties(main_hook PROPERTIES CXX_VISIBILITY_PRESET hidden)
add_library(file_redirect_hook SHARED file_redirect_hook.cpp) add_library(file_redirect_hook SHARED file_redirect_hook.c)
target_compile_options(file_redirect_hook PRIVATE -Wall -Wextra) target_compile_options(file_redirect_hook PRIVATE -Wall -Wextra)
target_link_options(file_redirect_hook PRIVATE -z global) target_link_options(file_redirect_hook PRIVATE -z global)
target_link_libraries(file_redirect_hook linkernsbypass dl log) target_link_libraries(file_redirect_hook hook_impl)
target_include_directories(file_redirect_hook PRIVATE ../../include)
set_target_properties(file_redirect_hook PROPERTIES CXX_VISIBILITY_PRESET hidden) set_target_properties(file_redirect_hook PROPERTIES CXX_VISIBILITY_PRESET hidden)

View file

@ -0,0 +1,5 @@
#include "hook_impl.h"
__attribute__((visibility("default"))) FILE *fopen(const char *filename, const char *mode) {
return hook_fopen(filename, mode);
}

View file

@ -1,35 +1,9 @@
#include <string> #include "hook_impl.h"
#include <cstdio>
#include <dlfcn.h>
#include <android/log.h>
#define TAG "file_redirect_hook" __attribute__((visibility("default"))) void init_hook_param(const void *param) {
#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##__VA_ARGS__) init_file_redirect_hook_param(param);
static decltype(&fopen) libc_fopen{[]() {
auto libcHandle{dlopen("libc.so", RTLD_LAZY)};
if (!libcHandle)
__builtin_trap();
auto sym{dlsym(libcHandle, "fopen")};
if (!sym)
__builtin_trap();
return reinterpret_cast<decltype(&fopen)>(sym);
}()};
extern "C" {
__attribute__((visibility("default"))) const char *hook_param; //!< The prefix to add to filesystem calls
__attribute__((visibility("default"))) FILE *fopen(const char *filename, const char *mode) {
if ((strncmp("/proc", filename, 5) == 0) || (strncmp("/sys", filename, 4) == 0)) {
LOGI("fopen: passthrough: %s", filename);
return libc_fopen(filename, mode);
}
auto replacement{std::string{hook_param} + filename};
LOGI("fopen: %s -> %s", filename, replacement.c_str());
return libc_fopen(replacement.c_str(), mode);
}
} }
__attribute__((visibility("default"))) FILE *fopen(const char *filename, const char *mode) {
return hook_fopen(filename, mode);
}

116
src/hook/hook_impl.cpp Normal file
View file

@ -0,0 +1,116 @@
#include <initializer_list>
#include <string>
#include <cstdio>
#include <cstring>
#include <dlfcn.h>
#include <android_linker_ns.h>
#include <android/dlext.h>
#include <android/log.h>
#include "hook_impl_params.h"
#include "hook_impl.h"
#define TAG "hook_impl"
#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##__VA_ARGS__)
const HookImplParams *hook_params; //!< Bunch of info needed to load/patch the driver
__attribute__((visibility("default"))) void init_hook_param(const void *param) {
hook_params = reinterpret_cast<const HookImplParams *>(param);
LOGI("SET %p", param);
}
__attribute__((visibility("default"))) void *hook_android_dlopen_ext(const char *filename, int flags, const android_dlextinfo *extinfo) {
auto fallback{[&]() {
LOGI("hook_android_dlopen_ext: falling back!");
return android_dlopen_ext(filename, flags, extinfo);
}};
LOGI("hook_android_dlopen_ext: filename: %s", filename);
// Ignore non-vulkan libraries
if (!strstr(filename, "vulkan."))
return android_dlopen_ext(filename, flags, extinfo);
if (extinfo->library_namespace == nullptr || !(extinfo->flags & ANDROID_DLEXT_USE_NAMESPACE)) {
LOGI("hook_android_dlopen_ext: hook failed: namespace not supplied!");
return fallback();
}
// customDriverDir will be empty if ADRENOTOOLS_DRIVER_CUSTOM isn't set therefore it's fine to have either way
auto driverNs{android_create_namespace(filename, hook_params->customDriverDir.c_str(),
hook_params->hookLibDir.c_str(), ANDROID_NAMESPACE_TYPE_SHARED,
nullptr, extinfo->library_namespace)};
if (!driverNs) {
LOGI("hook_android_dlopen_ext: hook failed: namespace not supplied!");
return fallback();
}
// We depend on libandroid which is unlikely to be in the supplied driver namespace so we have to link it over
android_link_namespaces(driverNs, nullptr, "libandroid.so");
// Preload ourself, a new instance will be created since we have different linker ancestory
// If we don't preload we get a weird issue where despite being in NEEDED of the hook lib the hook's symbols will overwrite ours and cause an infinite loop
auto hookImpl{linkernsbypass_namespace_dlopen("libhook_impl.so", RTLD_NOW, driverNs)};
if (!hookImpl)
return nullptr;
// Pass parameters to ourself
auto initHookParam{reinterpret_cast<void (*)(const void *)>(dlsym(hookImpl, "init_hook_param"))};
if (!initHookParam)
return nullptr;
initHookParam(hook_params);
if (hook_params->featureFlags & ADRENOTOOLS_DRIVER_FILE_REDIRECT) {
if (!linkernsbypass_namespace_dlopen("libfile_redirect_hook.so", RTLD_GLOBAL, driverNs)) {
LOGI("hook_android_dlopen_ext: hook failed: failed to apply libfopen_redirect_hook!");
return fallback();
}
LOGI("hook_android_dlopen_ext: applied libfile_redirect_hook");
}
// Use our new namespace to load the vulkan driver
auto newExtinfo{*extinfo};
newExtinfo.library_namespace = driverNs;
// TODO: If there is already an instance of a vulkan driver loaded hooks won't be applied, this will only be the case for skiavk generally
// To fix this we would need to search /proc/self/maps for the file to a loaded instance of the library in order to read it to patch the soname and load it uniquely
if (hook_params->featureFlags & ADRENOTOOLS_DRIVER_CUSTOM) {
LOGI("hook_android_dlopen_ext: loading custom driver: %s%s", hook_params->customDriverDir.c_str(), hook_params->customDriverName.c_str());
return android_dlopen_ext(hook_params->customDriverName.c_str(), flags, &newExtinfo);
} else {
LOGI("hook_android_dlopen_ext: loading default driver: %s", filename);
return android_dlopen_ext(filename, flags, &newExtinfo);
}
}
__attribute__((visibility("default"))) void *hook_android_load_sphal_library(const char *filename, int flags) {
LOGI("hook_android_load_sphal_library: filename: %s", filename);
// https://android.googlesource.com/platform/system/core/+/master/libvndksupport/linker.cpp
for (const char *name : {"sphal", "vendor", "default"}) {
if (auto vendorNs{android_get_exported_namespace(name)}) {
android_dlextinfo dlextinfo{
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = vendorNs,
};
return hook_android_dlopen_ext(filename, flags, &dlextinfo);
}
}
return nullptr;
}
__attribute__((visibility("default"))) FILE *hook_fopen(const char *filename, const char *mode) {
if (!strncmp("/proc", filename, 5) || !strncmp("/sys", filename, 4)) {
LOGI("hook_fopen: passthrough: %s", filename);
return fopen(filename, mode);
}
LOGI("LET %p", hook_params);
auto replacement{hook_params->fileRedirectDir + filename};
LOGI("hook_fopen: %s -> %s", filename, replacement.c_str());
return fopen(replacement.c_str(), mode);
}

25
src/hook/hook_impl.h Normal file
View file

@ -0,0 +1,25 @@
// SPDX-License-Identifier: BSD-2-Clause
// Copyright © 2021 Billy Laws
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <android/dlext.h>
void init_hook_param(const void *param);
void *hook_android_dlopen_ext(const char *filename, int flags, const android_dlextinfo *extinfo);
void *hook_android_load_sphal_library(const char *filename, int flags);
FILE *hook_fopen(const char *filename, const char *mode);
#ifdef __cplusplus
}
#endif

View file

@ -7,10 +7,10 @@
#include <adrenotools/priv.h> #include <adrenotools/priv.h>
/** /**
* @brief Holds the parameters for the main libvulkan.so hook * @brief Holds the parameters needed for all hooks
* @note See comments for adrenotools_open_libvulkan as a reference for member variables * @note See comments for adrenotools_open_libvulkan as a reference for member variables
*/ */
struct MainHookParam { struct HookImplParams {
int featureFlags; int featureFlags;
std::string tmpLibDir; std::string tmpLibDir;
std::string hookLibDir; std::string hookLibDir;
@ -18,7 +18,8 @@ struct MainHookParam {
std::string customDriverName; std::string customDriverName;
std::string fileRedirectDir; std::string fileRedirectDir;
MainHookParam(int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir) HookImplParams(int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir,
const char *customDriverName, const char *fileRedirectDir)
: featureFlags(featureFlags), : featureFlags(featureFlags),
tmpLibDir(tmpLibDir ? tmpLibDir : ""), tmpLibDir(tmpLibDir ? tmpLibDir : ""),
hookLibDir(hookLibDir), hookLibDir(hookLibDir),

9
src/hook/main_hook.c Normal file
View file

@ -0,0 +1,9 @@
#include "hook_impl.h"
__attribute__((visibility("default"))) void *android_dlopen_ext(const char *filename, int flags, const android_dlextinfo *extinfo) {
return hook_android_dlopen_ext(filename, flags, extinfo);
}
__attribute__((visibility("default"))) void *android_load_sphal_library(const char *filename, int flags) {
return hook_android_load_sphal_library(filename, flags);
}

View file

@ -1,88 +0,0 @@
#include <cstring>
#include <android/dlext.h>
#include <android_linker_ns.h>
#include <android/log.h>
#include "main_hook.h"
#define TAG "driver_load_hook"
#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##__VA_ARGS__)
extern "C" {
// The namespace that should be used to load vulkan drivers, set dlopen_hooked from the hookParam argument
__attribute__((visibility("default"))) const MainHookParam *hook_param;
__attribute__((destructor)) static void free_hook_param() {
delete hook_param;
}
__attribute__((visibility("default"))) void *android_dlopen_ext(const char *filename, int flags, const android_dlextinfo *extinfo) {
auto fallback{[&]() {
LOGI("android_dlopen_ext: falling back!");
return libdl_android_dlopen_ext(filename, flags, extinfo);
}};
LOGI("android_dlopen_ext: filename: %s", filename);
// Ignore non-vulkan libraries
if (!strstr(filename, "vulkan."))
return libdl_android_dlopen_ext(filename, flags, extinfo);
if (extinfo->library_namespace == nullptr || !(extinfo->flags & ANDROID_DLEXT_USE_NAMESPACE)) {
LOGI("android_dlopen_ext: hook failed: namespace not supplied!");
return fallback();
}
// customDriverDir will be empty if ADRENOTOOLS_DRIVER_CUSTOM isn't set therefore it's fine to have either way
auto driverNs{android_create_namespace(filename, hook_param->customDriverDir.c_str(),
hook_param->hookLibDir.c_str(), ANDROID_NAMESPACE_TYPE_SHARED,
nullptr, extinfo->library_namespace)};
if (!driverNs) {
LOGI("android_dlopen_ext: hook failed: namespace not supplied!");
return fallback();
}
// Hook libs depend on libandroid which is unlikely to be in the supplied driver namespace so we have to link it over
android_link_namespaces(driverNs, nullptr, "libandroid.so");
if (hook_param->featureFlags & ADRENOTOOLS_DRIVER_FILE_REDIRECT) {
if (!linkernsbypass_namespace_apply_hook("libfile_redirect_hook.so", driverNs,
reinterpret_cast<const void *>(hook_param->fileRedirectDir.c_str()))) {
LOGI("android_dlopen_ext: hook failed: failed to apply libfopen_redirect_hook!");
return fallback();
}
LOGI("android_dlopen_ext: applied libfopen_redirect_hook");
}
// Use our new namespace to load the vulkan driver
auto newExtinfo{*extinfo};
newExtinfo.library_namespace = driverNs;
// TODO: If there is already an instance of a vulkan driver loaded hooks won't be applied, this will only be the case for skiavk generally
// To fix this we would need to search /proc/self/maps for the file to a loaded instance of the library in order to read it to patch the soname and load it uniquely
if (hook_param->featureFlags & ADRENOTOOLS_DRIVER_CUSTOM) {
LOGI("android_dlopen_ext: loading custom driver: %s%s", hook_param->customDriverDir.c_str(), hook_param->customDriverName.c_str());
return libdl_android_dlopen_ext(hook_param->customDriverName.c_str(), flags, &newExtinfo);
} else {
LOGI("android_dlopen_ext: loading default driver: %s", filename);
return libdl_android_dlopen_ext(filename, flags, &newExtinfo);
}
}
__attribute__((visibility("default"))) void *android_load_sphal_library(const char *filename, int flags) {
// https://android.googlesource.com/platform/system/core/+/master/libvndksupport/linker.cpp
for (const char *name : {"sphal", "vendor", "default"}) {
if (auto vendorNs{android_get_exported_namespace(name)}) {
android_dlextinfo dlextinfo{
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = vendorNs,
};
return android_dlopen_ext(filename, flags, &dlextinfo);
}
}
return nullptr;
}
}