282 lines
9.7 KiB
C++
282 lines
9.7 KiB
C++
//
|
||
// Created by Perfare on 2020/7/4.
|
||
//
|
||
|
||
#include "hack.h"
|
||
#include "log.h"
|
||
#include "xdl.h"
|
||
#include <cstring>
|
||
#include <cstdio>
|
||
#include <unistd.h>
|
||
#include <sys/system_properties.h>
|
||
#include <dlfcn.h>
|
||
#include <jni.h>
|
||
#include <thread>
|
||
#include <sys/mman.h>
|
||
#include <linux/unistd.h>
|
||
#include <array>
|
||
#include <sys/stat.h>
|
||
//#include <asm-generic/fcntl.h>
|
||
#include <fcntl.h>
|
||
#include "newriruhide.h"
|
||
void load_so(const char *game_data_dir, JavaVM *vm, const char *soname) {
|
||
bool load = false;
|
||
LOGI("hack_start %s", game_data_dir);
|
||
|
||
// 构建新文件路径,使用传入的 soname 参数
|
||
char new_so_path[256];
|
||
snprintf(new_so_path, sizeof(new_so_path), "%s/files/%s.so", game_data_dir, soname);
|
||
|
||
// 构建源文件路径
|
||
char src_path[256];
|
||
snprintf(src_path, sizeof(src_path), "/data/local/tmp/%s.so", soname);
|
||
|
||
// 打开源文件
|
||
int src_fd = open(src_path, O_RDONLY);
|
||
if (src_fd < 0) {
|
||
LOGE("Failed to open %s: %s (errno: %d)", src_path, strerror(errno), errno);
|
||
return;
|
||
}
|
||
|
||
// 打开目标文件
|
||
int dest_fd = open(new_so_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||
if (dest_fd < 0) {
|
||
LOGE("Failed to open %s", new_so_path);
|
||
close(src_fd);
|
||
return;
|
||
}
|
||
|
||
// 复制文件内容
|
||
char buffer[4096];
|
||
ssize_t bytes;
|
||
while ((bytes = read(src_fd, buffer, sizeof(buffer))) > 0) {
|
||
if (write(dest_fd, buffer, bytes) != bytes) {
|
||
LOGE("Failed to write to %s", new_so_path);
|
||
close(src_fd);
|
||
close(dest_fd);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 关闭文件描述符
|
||
close(src_fd);
|
||
close(dest_fd);
|
||
|
||
// 修改文件权限
|
||
if (chmod(new_so_path, 0755) != 0) {
|
||
LOGE("Failed to change permissions on %s: %s (errno: %d)", new_so_path, strerror(errno), errno);
|
||
return;
|
||
} else {
|
||
LOGI("Successfully changed permissions to 755 on %s", new_so_path);
|
||
}
|
||
|
||
// 加载 .so 文件
|
||
void *handle;
|
||
for (int i = 0; i < 10; i++) {
|
||
handle = dlopen(new_so_path, RTLD_NOW | RTLD_LOCAL);
|
||
if (handle) {
|
||
LOGI("Successfully loaded %s", new_so_path);
|
||
load = true;
|
||
riru_hide(soname);
|
||
break;
|
||
} else {
|
||
LOGE("Failed to load %s: %s", new_so_path, dlerror());
|
||
sleep(1);
|
||
}
|
||
}
|
||
|
||
// 如果加载失败
|
||
if (!load) {
|
||
LOGI("%s.so not found in thread %d", soname, gettid());
|
||
}
|
||
|
||
// 查找 JNI_OnLoad 并调用
|
||
// void (*setupSignalHandler)();
|
||
// *(void **) (&setupSignalHandler) = dlsym(handle, "setupSignalHandler");
|
||
//
|
||
// if (setupSignalHandler) {
|
||
// LOGI("setupSignalHandler symbol found, calling setupSignalHandler.");
|
||
// setupSignalHandler(); // 调用找到的函数
|
||
// } else {
|
||
// LOGE("setupSignalHandler symbol not found in %s", new_so_path);
|
||
// }
|
||
}
|
||
void hack_start(const char *game_data_dir,JavaVM *vm) {
|
||
load_so(game_data_dir,vm,"test");
|
||
//如果要注入多个so,那么就在这里不断的添加load_so函数即可
|
||
}
|
||
|
||
std::string GetLibDir(JavaVM *vms) {
|
||
JNIEnv *env = nullptr;
|
||
vms->AttachCurrentThread(&env, nullptr);
|
||
jclass activity_thread_clz = env->FindClass("android/app/ActivityThread");
|
||
if (activity_thread_clz != nullptr) {
|
||
jmethodID currentApplicationId = env->GetStaticMethodID(activity_thread_clz,
|
||
"currentApplication",
|
||
"()Landroid/app/Application;");
|
||
if (currentApplicationId) {
|
||
jobject application = env->CallStaticObjectMethod(activity_thread_clz,
|
||
currentApplicationId);
|
||
jclass application_clazz = env->GetObjectClass(application);
|
||
if (application_clazz) {
|
||
jmethodID get_application_info = env->GetMethodID(application_clazz,
|
||
"getApplicationInfo",
|
||
"()Landroid/content/pm/ApplicationInfo;");
|
||
if (get_application_info) {
|
||
jobject application_info = env->CallObjectMethod(application,
|
||
get_application_info);
|
||
jfieldID native_library_dir_id = env->GetFieldID(
|
||
env->GetObjectClass(application_info), "nativeLibraryDir",
|
||
"Ljava/lang/String;");
|
||
if (native_library_dir_id) {
|
||
auto native_library_dir_jstring = (jstring) env->GetObjectField(
|
||
application_info, native_library_dir_id);
|
||
auto path = env->GetStringUTFChars(native_library_dir_jstring, nullptr);
|
||
LOGI("lib dir %s", path);
|
||
std::string lib_dir(path);
|
||
env->ReleaseStringUTFChars(native_library_dir_jstring, path);
|
||
return lib_dir;
|
||
} else {
|
||
LOGE("nativeLibraryDir not found");
|
||
}
|
||
} else {
|
||
LOGE("getApplicationInfo not found");
|
||
}
|
||
} else {
|
||
LOGE("application class not found");
|
||
}
|
||
} else {
|
||
LOGE("currentApplication not found");
|
||
}
|
||
} else {
|
||
LOGE("ActivityThread not found");
|
||
}
|
||
return {};
|
||
}
|
||
|
||
static std::string GetNativeBridgeLibrary() {
|
||
auto value = std::array<char, PROP_VALUE_MAX>();
|
||
__system_property_get("ro.dalvik.vm.native.bridge", value.data());
|
||
return {value.data()};
|
||
}
|
||
|
||
struct NativeBridgeCallbacks {
|
||
uint32_t version;
|
||
void *initialize;
|
||
|
||
void *(*loadLibrary)(const char *libpath, int flag);
|
||
|
||
void *(*getTrampoline)(void *handle, const char *name, const char *shorty, uint32_t len);
|
||
|
||
void *isSupported;
|
||
void *getAppEnv;
|
||
void *isCompatibleWith;
|
||
void *getSignalHandler;
|
||
void *unloadLibrary;
|
||
void *getError;
|
||
void *isPathSupported;
|
||
void *initAnonymousNamespace;
|
||
void *createNamespace;
|
||
void *linkNamespaces;
|
||
|
||
void *(*loadLibraryExt)(const char *libpath, int flag, void *ns);
|
||
};
|
||
|
||
bool NativeBridgeLoad(const char *game_data_dir, int api_level, void *data, size_t length) {
|
||
//TODO 等待houdini初始化
|
||
sleep(5);
|
||
|
||
auto libart = dlopen("libart.so", RTLD_NOW);
|
||
auto JNI_GetCreatedJavaVMs = (jint (*)(JavaVM **, jsize, jsize *)) dlsym(libart,
|
||
"JNI_GetCreatedJavaVMs");
|
||
LOGI("JNI_GetCreatedJavaVMs %p", JNI_GetCreatedJavaVMs);
|
||
JavaVM *vms_buf[1];
|
||
JavaVM *vms;
|
||
jsize num_vms;
|
||
jint status = JNI_GetCreatedJavaVMs(vms_buf, 1, &num_vms);
|
||
if (status == JNI_OK && num_vms > 0) {
|
||
vms = vms_buf[0];
|
||
} else {
|
||
LOGE("GetCreatedJavaVMs error");
|
||
return false;
|
||
}
|
||
|
||
auto lib_dir = GetLibDir(vms);
|
||
if (lib_dir.empty()) {
|
||
LOGE("GetLibDir error");
|
||
return false;
|
||
}
|
||
if (lib_dir.find("/lib/x86") != std::string::npos) {
|
||
LOGI("no need NativeBridge");
|
||
munmap(data, length);
|
||
return false;
|
||
}
|
||
|
||
auto nb = dlopen("libhoudini.so", RTLD_NOW);
|
||
if (!nb) {
|
||
auto native_bridge = GetNativeBridgeLibrary();
|
||
LOGI("native bridge: %s", native_bridge.data());
|
||
nb = dlopen(native_bridge.data(), RTLD_NOW);
|
||
}
|
||
if (nb) {
|
||
LOGI("nb %p", nb);
|
||
auto callbacks = (NativeBridgeCallbacks *) dlsym(nb, "NativeBridgeItf");
|
||
if (callbacks) {
|
||
LOGI("NativeBridgeLoadLibrary %p", callbacks->loadLibrary);
|
||
LOGI("NativeBridgeLoadLibraryExt %p", callbacks->loadLibraryExt);
|
||
LOGI("NativeBridgeGetTrampoline %p", callbacks->getTrampoline);
|
||
int fd = syscall(__NR_memfd_create, "anon", MFD_CLOEXEC);
|
||
ftruncate(fd, (off_t) length);
|
||
void *mem = mmap(nullptr, length, PROT_WRITE, MAP_SHARED, fd, 0);
|
||
memcpy(mem, data, length);
|
||
munmap(mem, length);
|
||
munmap(data, length);
|
||
char path[PATH_MAX];
|
||
snprintf(path, PATH_MAX, "/proc/self/fd/%d", fd);
|
||
LOGI("arm path %s", path);
|
||
|
||
void *arm_handle;
|
||
if (api_level >= 26) {
|
||
arm_handle = callbacks->loadLibraryExt(path, RTLD_NOW, (void *) 3);
|
||
} else {
|
||
arm_handle = callbacks->loadLibrary(path, RTLD_NOW);
|
||
}
|
||
if (arm_handle) {
|
||
LOGI("arm handle %p", arm_handle);
|
||
auto init = (void (*)(JavaVM *, void *)) callbacks->getTrampoline(arm_handle,
|
||
"JNI_OnLoad",
|
||
nullptr, 0);
|
||
LOGI("JNI_OnLoad %p", init);
|
||
init(vms, (void *) game_data_dir);
|
||
return true;
|
||
}
|
||
close(fd);
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void hack_prepare(const char *_data_dir, void *data, size_t length) {
|
||
LOGI("hack thread: %d", gettid());
|
||
int api_level = android_get_device_api_level();
|
||
LOGI("api level: %d", api_level);
|
||
|
||
#if defined(__i386__) || defined(__x86_64__)
|
||
if (!NativeBridgeLoad(_data_dir, api_level, data, length)) {
|
||
#endif
|
||
hack_start(_data_dir, nullptr);
|
||
#if defined(__i386__) || defined(__x86_64__)
|
||
}
|
||
#endif
|
||
}
|
||
|
||
#if defined(__arm__) || defined(__aarch64__)
|
||
|
||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||
auto game_data_dir = (const char *) reserved;
|
||
std::thread hack_thread(hack_start, game_data_dir,vm);
|
||
hack_thread.detach();
|
||
return JNI_VERSION_1_6;
|
||
}
|
||
|
||
#endif |