[core] Use ElfImg instead of DobbySymbolResolver (#419)

This commit is contained in:
LoveSy 2021-03-28 15:14:57 +08:00 committed by GitHub
parent 3f80d37364
commit 69e5feffce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 448 additions and 75 deletions

View File

@ -49,8 +49,8 @@ jobs:
uses: actions/cache@v2
with:
path: ~/.ccache
key: ccache-${{ github.sha }}
restore-keys: ccache
key: ccache-cache-${{ github.sha }}
restore-keys: ccache-cache-
- name: Install ccache
run: sudo apt-get install -y ccache
- name: Build with Gradle

View File

@ -185,8 +185,6 @@ task("buildLibcxx", Exec::class) {
println("using ccache $it")
environment("NDK_CCACHE", it)
environment("USE_CCACHE", "1")
environment("CCACHE_COMPILERCHECK", "content")
environment("CCACHE_MAXSIZE", "100M")
} ?: run {
println("not using ccache")
}

View File

@ -25,8 +25,6 @@ find_program(CCACHE ccache)
if (CCACHE)
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
set(ENV{CCACHE_COMPILERCHECK} "content")
set(ENV{CCACHE_MAXSIZE} "100M")
else ()
message(WARNING "Could not find CCache program.")
endif ()

View File

@ -10,13 +10,11 @@ macro(SET_OPTION option value)
endmacro()
SET_OPTION(DOBBY_GENERATE_SHARED OFF)
SET_OPTION(Plugin.Android.BionicLinkerRestriction ON)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
SET_OPTION(DOBBY_DEBUG OFF)
endif (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_subdirectory(Dobby)
target_include_directories(dobby PUBLIC Dobby/include)
target_include_directories(dobby PUBLIC Dobby/builtin-plugin/BionicLinkerRestriction)
add_library(libcxx STATIC IMPORTED GLOBAL)
set_property(TARGET libcxx PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../../../../build/intermediates/ndk/obj/local/${CMAKE_ANDROID_ARCH_ABI}/libcxx.a)

View File

@ -32,15 +32,15 @@ namespace lspd {
//#define DEBUG
inline bool constexpr Is64() {
inline bool constexpr Is64() {
#if defined(__LP64__)
return true;
return true;
#else
return false;
return false;
#endif
}
}
inline constexpr bool is64 = Is64();
inline constexpr bool is64 = Is64();
#if defined(__LP64__)
# define LP_SELECT(lp32, lp64) lp64
@ -55,12 +55,20 @@ inline constexpr bool is64 = Is64();
static constexpr auto kLibArtName = "libart.so"_tstr;
static constexpr auto kLibFwName = "libandroidfw.so"_tstr;
static constexpr auto kLinkerName = LP_SELECT("linker"_tstr, "linker64"_tstr);
static constexpr auto kLibcName = "libc.so"_tstr;
static constexpr auto kLibbaseName = "libbase.so"_tstr;
static constexpr auto kLibBasePath =
LP_SELECT("/system/lib/"_tstr,
"/system/lib64/"_tstr);
static constexpr auto kLibArtLegacyPath = kLibBasePath + kLibArtName;
static constexpr auto kBinBasePath = "/system/bin/"_tstr;
static constexpr auto kLibFwPath = kLibBasePath + kLibFwName;
static constexpr auto kLinkerPath = kBinBasePath + kLinkerName;
static constexpr auto kLibcPath = kLibBasePath + kLibcName;
static constexpr auto kLibbasePath = kLibBasePath + kLibbaseName;
inline constexpr const char *const BoolToString(bool b) {
return b ? "true" : "false";

View File

@ -31,6 +31,7 @@
#include "jni/logger.h"
#include "jni/native_api.h"
#include "service.h"
#include "symbol_cache.h"
namespace lspd {
extern int *allowUnload;
@ -168,22 +169,24 @@ namespace lspd {
void
Context::OnNativeForkSystemServerPre(JNIEnv *env) {
Service::instance()->InitService(env);
skip_ = false;
setAllowUnload(false);
skip_ = !sym_initialized;
setAllowUnload(skip_);
}
void
Context::OnNativeForkSystemServerPost(JNIEnv *env, jint res) {
if (res != 0) return;
LoadDex(env);
Service::instance()->HookBridge(*this, env);
auto binder = Service::instance()->RequestBinderForSystemServer(env);
if (binder && !skip_) {
InstallInlineHooks();
Init(env);
FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", binder);
if (!skip_) {
LoadDex(env);
Service::instance()->HookBridge(*this, env);
auto binder = Service::instance()->RequestBinderForSystemServer(env);
if (binder) {
InstallInlineHooks();
Init(env);
FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", binder);
}
}
setAllowUnload(false);
setAllowUnload(skip_);
}
void Context::OnNativeForkAndSpecializePre(JNIEnv *env,
@ -195,7 +198,7 @@ namespace lspd {
const auto app_id = uid % PER_USER_RANGE;
nice_name_ = nice_name;
JUTFString process_name(env, nice_name);
skip_ = false;
skip_ = !sym_initialized;
if (!skip_ && !app_data_dir) {
LOGD("skip injecting into %s because it has no data dir", process_name.get());
skip_ = true;

View File

@ -0,0 +1,203 @@
// From https://github.com/ganyao114/SandHook/blob/master/hooklib/src/main/cpp/utils/elf_util.cpp
//
// Created by Swift Gan on 2019/3/14.
//
#include <malloc.h>
#include <cstring>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <cassert>
#include <filesystem>
#include <sys/stat.h>
#include "logging.h"
#include "elf_util.h"
using namespace SandHook;
namespace fs = std::filesystem;
ElfImg::ElfImg(const char *elf) {
this->elf = elf;
//load elf
int fd = open(elf, O_RDONLY);
if (fd < 0) {
LOGE("failed to open %s", elf);
return;
}
size = lseek(fd, 0, SEEK_END);
if (size <= 0) {
LOGE("lseek() failed for %s", elf);
}
header = reinterpret_cast<Elf_Ehdr *>(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0));
close(fd);
section_header = reinterpret_cast<Elf_Shdr *>(((size_t) header) + header->e_shoff);
auto shoff = reinterpret_cast<size_t>(section_header);
char *section_str = reinterpret_cast<char *>(section_header[header->e_shstrndx].sh_offset +
((size_t) header));
for (int i = 0; i < header->e_shnum; i++, shoff += header->e_shentsize) {
auto *section_h = (Elf_Shdr *) shoff;
char *sname = section_h->sh_name + section_str;
Elf_Off entsize = section_h->sh_entsize;
switch (section_h->sh_type) {
case SHT_DYNSYM:
if (bias == -4396) {
dynsym = section_h;
dynsym_offset = section_h->sh_offset;
dynsym_size = section_h->sh_size;
dynsym_count = dynsym_size / entsize;
dynsym_start = reinterpret_cast<Elf_Sym *>(((size_t) header) + dynsym_offset);
}
break;
case SHT_SYMTAB:
if (strcmp(sname, ".symtab") == 0) {
symtab = section_h;
symtab_offset = section_h->sh_offset;
symtab_size = section_h->sh_size;
symtab_count = symtab_size / entsize;
symtab_start = reinterpret_cast<Elf_Sym *>(((size_t) header) + symtab_offset);
}
break;
case SHT_STRTAB:
if (bias == -4396) {
strtab = section_h;
symstr_offset = section_h->sh_offset;
strtab_start = reinterpret_cast<Elf_Sym *>(((size_t) header) + symstr_offset);
}
if (strcmp(sname, ".strtab") == 0) {
symstr_offset_for_symtab = section_h->sh_offset;
}
break;
case SHT_PROGBITS:
if (strtab == nullptr || dynsym == nullptr) break;
if (bias == -4396) {
bias = (off_t) section_h->sh_addr - (off_t) section_h->sh_offset;
}
break;
}
}
//load module base
base = getModuleBase();
}
ElfImg::~ElfImg() {
//open elf file local
if (buffer) {
free(buffer);
buffer = nullptr;
}
//use mmap
if (header) {
munmap(header, size);
}
}
Elf_Addr ElfImg::getSymbOffset(const char *name) const {
Elf_Addr _offset;
//search dynmtab
if (dynsym_start != nullptr && strtab_start != nullptr) {
Elf_Sym *sym = dynsym_start;
char *strings = (char *) strtab_start;
int k;
for (k = 0; k < dynsym_count; k++, sym++)
if (strcmp(strings + sym->st_name, name) == 0) {
_offset = sym->st_value;
LOGD("find %s: %p", elf, reinterpret_cast<void *>(_offset));
return _offset;
}
}
//search symtab
if (symtab_start != nullptr && symstr_offset_for_symtab != 0) {
for (int i = 0; i < symtab_count; i++) {
unsigned int st_type = ELF_ST_TYPE(symtab_start[i].st_info);
char *st_name = reinterpret_cast<char *>(((size_t) header) + symstr_offset_for_symtab +
symtab_start[i].st_name);
if ((st_type == STT_FUNC || st_type == STT_OBJECT) && symtab_start[i].st_size) {
if (strcmp(st_name, name) == 0) {
_offset = symtab_start[i].st_value;
LOGD("find %s: %p", elf, reinterpret_cast<void *>(_offset));
return _offset;
}
}
}
}
return 0;
}
Elf_Addr ElfImg::getSymbAddress(const char *name) const {
Elf_Addr offset = getSymbOffset(name);
Elf_Addr res;
if (offset > 0 && base != nullptr) {
res = static_cast<Elf_Addr>((size_t) base + offset - bias);
} else {
res = 0;
}
if (res == 0) {
LOGE("fail to get symbol %s from %s ", name, elf);
} else {
LOGD("got symbol %s form %s with %p", name, elf, (void *) res);
}
return res;
}
void *ElfImg::getModuleBase() const {
char buff[256];
off_t load_addr;
int found = 0;
FILE *maps = fopen("/proc/self/maps", "r");
char name[PATH_MAX] = {'\0'};
strncpy(name, elf, PATH_MAX);
{
struct stat buf{};
while (lstat(name, &buf) == 0 && S_ISLNK(buf.st_mode)) {
if (auto s = readlink(name, name, PATH_MAX); s >= 0) {
name[s] = '\0';
} else {
fclose(maps);
LOGE("cannot read link for %s with %s", name, strerror(errno));
return nullptr;
}
}
}
// fs::path name(elf);
// std::error_code ec;
// while(fs::is_symlink(name, ec) && !ec) {
// name = fs::read_symlink(name);
// }
while (fgets(buff, sizeof(buff), maps)) {
if ((strstr(buff, "r-xp") || strstr(buff, "r--p")) && strstr(buff, name)) {
found = 1;
LOGD("found: %s", buff);
break;
}
}
if (!found) {
LOGE("failed to read load address for %s", name);
fclose(maps);
return nullptr;
}
if (sscanf(buff, "%lx", &load_addr) != 1)
LOGE("failed to read load address for %s", name);
fclose(maps);
LOGD("get module base %s: %lu", name, load_addr);
return reinterpret_cast<void *>(load_addr);
}

View File

@ -0,0 +1,74 @@
// From https://github.com/ganyao114/SandHook/blob/master/hooklib/src/main/cpp/includes/elf_util.h
//
// Created by Swift Gan on 2019/3/14.
//
#ifndef SANDHOOK_ELF_UTIL_H
#define SANDHOOK_ELF_UTIL_H
#include <linux/elf.h>
#include <sys/types.h>
#if defined(__LP64__)
typedef Elf64_Ehdr Elf_Ehdr;
typedef Elf64_Shdr Elf_Shdr;
typedef Elf64_Addr Elf_Addr;
typedef Elf64_Dyn Elf_Dyn;
typedef Elf64_Rela Elf_Rela;
typedef Elf64_Sym Elf_Sym;
typedef Elf64_Off Elf_Off;
#define ELF_R_SYM(i) ELF64_R_SYM(i)
#else
typedef Elf32_Ehdr Elf_Ehdr;
typedef Elf32_Shdr Elf_Shdr;
typedef Elf32_Addr Elf_Addr;
typedef Elf32_Dyn Elf_Dyn;
typedef Elf32_Rel Elf_Rela;
typedef Elf32_Sym Elf_Sym;
typedef Elf32_Off Elf_Off;
#define ELF_R_SYM(i) ELF32_R_SYM(i)
#endif
namespace SandHook {
class ElfImg {
public:
ElfImg(const char* elf);
Elf_Addr getSymbOffset(const char* name) const;
void* getModuleBase() const;
Elf_Addr getSymbAddress(const char* name) const;
~ElfImg();
private:
const char* elf = nullptr;
void* base = nullptr;
char* buffer = nullptr;
off_t size = 0;
off_t bias = -4396;
Elf_Ehdr* header = nullptr;
Elf_Shdr* section_header = nullptr;
Elf_Shdr* symtab = nullptr;
Elf_Shdr* strtab = nullptr;
Elf_Shdr* dynsym = nullptr;
Elf_Off dynsym_count = 0;
Elf_Sym* symtab_start = nullptr;
Elf_Sym* dynsym_start = nullptr;
Elf_Sym* strtab_start = nullptr;
Elf_Off symtab_count = 0;
Elf_Off symstr_offset = 0;
Elf_Off symstr_offset_for_symtab = 0;
Elf_Off symtab_offset = 0;
Elf_Off dynsym_offset = 0;
Elf_Off symtab_size = 0;
Elf_Off dynsym_size = 0;
};
}
#endif //SANDHOOK_ELF_UTIL_H

View File

@ -57,14 +57,15 @@ namespace lspd {
return ret;
}
void RegisterNativeLib(const std::string& library_name) {
void RegisterNativeLib(const std::string &library_name) {
LOGD("native_api: Registered %s", library_name.c_str());
moduleNativeLibs.push_back(library_name);
}
bool hasEnding(std::string_view fullString, std::string_view ending) {
if (fullString.length() >= ending.length()) {
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(),
ending));
} else {
return false;
}
@ -89,13 +90,14 @@ namespace lspd {
// the so is a module so
if (UNLIKELY(hasEnding(ns, module_lib))) {
LOGI("Loading module native library %s", module_lib.data());
void* native_init_sym = dlsym(handle, "native_init");
void *native_init_sym = dlsym(handle, "native_init");
if (UNLIKELY(native_init_sym == nullptr)) {
LOGE("Failed to get symbol \"native_init\" from library %s", module_lib.data());
LOGE("Failed to get symbol \"native_init\" from library %s",
module_lib.data());
break;
}
auto native_init = reinterpret_cast<NativeInit>(native_init_sym);
native_init(reinterpret_cast<void*>(init));
native_init(reinterpret_cast<void *>(init));
}
}
@ -107,8 +109,8 @@ namespace lspd {
});
void InstallNativeAPI() {
symbol_do_dlopen = DobbySymbolResolver(nullptr, "__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv");
LOGD("InstallNativeAPI: %p", symbol_do_dlopen);
HookSymNoHandle(symbol_do_dlopen, do_dlopen);
LOGD("InstallNativeAPI: %p", sym_do_dlopen);
if (sym_do_dlopen)
HookSymNoHandle(sym_do_dlopen, do_dlopen);
}
}
}

View File

@ -25,8 +25,8 @@
#include <dl_util.h>
#include <art/runtime/jni_env_ext.h>
#include <dobby.h>
#include "bionic_linker_restriction.h"
#include "utils.h"
#include "symbol_cache.h"
#include "logging.h"
#include "native_api.h"
#include "native_hook.h"
@ -63,28 +63,7 @@ namespace lspd {
LOGI("Using api level %d", api_level);
InstallRiruHooks();
// install ART hooks
if (api_level >= __ANDROID_API_Q__) {
// From Riru v22 we can't get ART handle by hooking dlopen, so we get libart.so from soinfo.
// Ref: https://android.googlesource.com/platform/bionic/+/master/linker/linker_soinfo.h
linker_iterate_soinfo([](auto soinfo) {
const char *real_path = linker_soinfo_get_realpath(soinfo);
if (real_path != nullptr &&
strstr(real_path, kLibArtName.c_str()) != nullptr){
InstallArtHooks(soinfo);
return 1;
}
return 0;
});
if (!art_hooks_installed) {
LOGE("Android 10+ detected and libart.so can't be found in memory.");
return;
}
} else {
// do dlopen directly in Android 9-
ScopedDlHandle art_handle(kLibArtLegacyPath.c_str());
InstallArtHooks(art_handle.Get());
}
InstallArtHooks(handle_libart);
InstallNativeAPI();
}

View File

@ -25,7 +25,7 @@
#include <logging.h>
#include "utils.h"
#include "riru_hook.h"
#include <dobby.h>
#include "symbol_cache.h"
namespace lspd {
@ -123,21 +123,18 @@ namespace lspd {
api_level = GetAndroidApiLevel();
auto *sym = DobbySymbolResolver(nullptr, "__system_property_get");
if (!sym) {
if (!sym_system_property_get) {
LOGE("Failed to get symbol of __system_property_get");
return;
}
HookSymNoHandle(sym, __system_property_get);
HookSymNoHandle(sym_system_property_get, __system_property_get);
if (GetAndroidApiLevel() >= __ANDROID_API_P__) {
sym = DobbySymbolResolver(nullptr,
"_ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_");
if (!sym) {
if (!sym_get_property) {
LOGE("Failed to get symbol of _ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_");
return;
}
HookSymNoHandle(sym, GetProperty);
HookSymNoHandle(sym_get_property, GetProperty);
}
LOGI("Riru hooks installed");

View File

@ -26,6 +26,7 @@
#include "service.h"
#include "context.h"
#include "jni_helper.h"
#include "symbol_cache.h"
namespace lspd {
jboolean
@ -84,7 +85,7 @@ namespace lspd {
"(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z");
binder_class_ = env->FindClass("android/os/Binder");
if (binder_class_) binder_class_ = (jclass)env->NewGlobalRef(binder_class_);
if (binder_class_) binder_class_ = (jclass) env->NewGlobalRef(binder_class_);
binder_ctor_ = env->GetMethodID(binder_class_, "<init>", "()V");
// Parcel
@ -133,8 +134,8 @@ namespace lspd {
exec_transact_backup_methodID_ = JNI_GetMethodID(env, binderClass.get(), "execTransact",
"(IJJI)Z");
auto set_table_override = reinterpret_cast<void (*)(
JNINativeInterface *)>(DobbySymbolResolver(nullptr,
"_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface"));
JNINativeInterface *)>(Dlsym(handle_libart,
"_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface"));
if (!set_table_override) {
LOGE("set table override not found");
}

View File

@ -23,16 +23,126 @@
//
#include "symbol_cache.h"
#include "elf_util.h"
#include <dobby.h>
#include "macros.h"
#include "config.h"
#include <vector>
#include <logging.h>
namespace lspd {
bool sym_initialized = false;
void *sym_do_dlopen = nullptr;
void *sym_system_property_get = nullptr;
void *sym_get_property = nullptr;
void *handle_libart = nullptr;
struct soinfo;
soinfo *solist = nullptr;
soinfo *somain = nullptr;
template<typename T>
inline T *getStaticVariable(const SandHook::ElfImg &linker, std::string_view name) {
auto *addr = reinterpret_cast<T **>(linker.getSymbAddress(name.data()));
return addr == nullptr ? nullptr : *addr;
}
struct soinfo {
soinfo *next() {
return *(soinfo **) ((uintptr_t) this + solist_next_offset);
}
const char *get_realpath() {
return get_realpath_sym ? get_realpath_sym(this) : ((std::string *) (
(uintptr_t) this +
solist_realpath_offset))->c_str();
}
const char *get_soname() {
return get_soname_sym ? get_soname_sym(this) : *((const char **) (
(uintptr_t) this +
solist_realpath_offset - sizeof(void *)));
}
void *to_handle() {
return to_handle_sym ? to_handle_sym(this) : nullptr;
}
static bool setup(const SandHook::ElfImg &linker) {
get_realpath_sym = reinterpret_cast<decltype(get_realpath_sym)>(linker.getSymbAddress(
"__dl__ZNK6soinfo12get_realpathEv"));
get_soname_sym = reinterpret_cast<decltype(get_soname_sym)>(linker.getSymbAddress(
"__dl__ZNK6soinfo10get_sonameEv"));
to_handle_sym = reinterpret_cast<decltype(to_handle_sym)>(linker.getSymbAddress(
"__dl__ZN6soinfo9to_handleEv"));
auto vsdo = getStaticVariable<soinfo>(linker, "__dl__ZL4vdso");
for (size_t i = 0; i < 1024 / sizeof(void *); i++) {
auto *possible_next = *(void **) ((uintptr_t) solist + i * sizeof(void *));
if (possible_next == somain || (vsdo && possible_next == vsdo)) {
solist_next_offset = i * sizeof(void *);
return (get_realpath_sym && get_soname_sym && to_handle_sym);
}
}
LOGW("%s", "failed to search next offset");
// shortcut
return false;
}
#ifdef __LP64__
inline static size_t solist_next_offset = 0x30;
constexpr static size_t solist_realpath_offset = 0x1a8;
#else
inline static size_t solist_next_offset = 0xa4;
constexpr static size_t solist_realpath_offset = 0x174;
#endif
// since Android 8
inline static const char *(*get_realpath_sym)(soinfo *) = nullptr;
inline static const char *(*get_soname_sym)(soinfo *) = nullptr;
inline static void *(*to_handle_sym)(soinfo *) = nullptr;
};
std::vector<soinfo *> linker_get_solist() {
std::vector<soinfo *> linker_solist{};
for (auto *iter = solist; iter; iter = iter->next()) {
linker_solist.push_back(iter);
}
return linker_solist;
}
void *findLibArt() {
for (const auto &soinfo : linker_get_solist()) {
if (const auto &real_path = soinfo->get_realpath(), &soname = soinfo->get_soname();
(real_path &&
std::string_view(real_path).find(kLibArtName) != std::string_view::npos) ||
(soname &&
std::string_view(soname).find(kLibArtName) != std::string_view::npos)) {
return soinfo->to_handle();
}
}
return nullptr;
}
void InitSymbolCache() {
if (LIKELY(initialized)) return;
if (UNLIKELY(sym_initialized)) return;
LOGD("InitSymbolCache");
// TODO: set image name
symbol_do_dlopen = DobbySymbolResolver(nullptr, "__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv");
initialized = true;
auto linker = SandHook::ElfImg(kLinkerPath.c_str());
auto libc = SandHook::ElfImg(kLibcPath.c_str());
auto libbase = SandHook::ElfImg(kLibbasePath.c_str());
sym_initialized = (solist = getStaticVariable<soinfo>(linker, "__dl__ZL6solist")) &&
(somain = getStaticVariable<soinfo>(linker, "__dl__ZL6somain")) &&
(sym_do_dlopen = reinterpret_cast<void *>(linker.getSymbAddress(
"__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv"))) &&
(sym_system_property_get = reinterpret_cast<void *>(libc.getSymbAddress(
"__system_property_get"))) &&
(sym_get_property = reinterpret_cast<void *>(libbase.getSymbAddress(
"_ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_"))) &&
soinfo::setup(linker) && (handle_libart = findLibArt());
if (UNLIKELY(!sym_initialized)) {
LOGE("Init symbol cache failed");
}
}
}

View File

@ -24,11 +24,13 @@
#ifndef LSPOSED_SYMBOL_CACHE_H
#define LSPOSED_SYMBOL_CACHE_H
#include <atomic>
namespace lspd {
static std::atomic_bool initialized;
static void* symbol_do_dlopen = nullptr;
extern bool sym_initialized;
extern void *sym_do_dlopen;
extern void *sym_system_property_get;
extern void *sym_get_property;
extern void *handle_libart;
void InitSymbolCache();
}