feat: 成功加载了so 并且调用了riru hide等逻辑

准备增加自定义linker
This commit is contained in:
jiqiu2021
2025-06-25 21:21:07 +08:00
parent f557d71874
commit 1076c1e711
8 changed files with 246 additions and 63 deletions

View File

@@ -23,8 +23,8 @@ android {
} }
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_11
} }
} }

View File

@@ -56,6 +56,8 @@ public class AppListFragment extends Fragment implements AppListAdapter.OnAppTog
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
configManager = new ConfigManager(requireContext()); configManager = new ConfigManager(requireContext());
// Ensure module directories exist
configManager.ensureModuleDirectories();
initViews(view); initViews(view);
setupRecyclerView(); setupRecyclerView();

View File

@@ -27,13 +27,17 @@ public class ConfigManager {
// Configure Shell to use root // Configure Shell to use root
Shell.enableVerboseLogging = BuildConfig.DEBUG; Shell.enableVerboseLogging = BuildConfig.DEBUG;
Shell.setDefaultBuilder(Shell.Builder.create() Shell.setDefaultBuilder(Shell.Builder.create()
.setFlags(Shell.FLAG_REDIRECT_STDERR) .setFlags(Shell.FLAG_REDIRECT_STDERR | Shell.FLAG_MOUNT_MASTER)
.setTimeout(10)); .setTimeout(30));
} }
public ConfigManager(Context context) { public ConfigManager(Context context) {
this.context = context; this.context = context;
this.gson = new GsonBuilder().setPrettyPrinting().create(); this.gson = new GsonBuilder().setPrettyPrinting().create();
// Ensure we get root shell on creation
Shell.getShell();
loadConfig(); loadConfig();
} }
@@ -42,8 +46,32 @@ public class ConfigManager {
} }
public void ensureModuleDirectories() { public void ensureModuleDirectories() {
Shell.cmd("mkdir -p " + MODULE_PATH).exec(); // Check root access first
Shell.cmd("mkdir -p " + SO_STORAGE_DIR).exec(); if (!isRootAvailable()) {
Log.e(TAG, "Root access not available!");
return;
}
// Create module directories
Shell.Result result1 = Shell.cmd("mkdir -p " + MODULE_PATH).exec();
if (!result1.isSuccess()) {
Log.e(TAG, "Failed to create module directory: " + MODULE_PATH);
}
Shell.Result result2 = Shell.cmd("mkdir -p " + SO_STORAGE_DIR).exec();
if (!result2.isSuccess()) {
Log.e(TAG, "Failed to create SO storage directory: " + SO_STORAGE_DIR);
}
// Set permissions
Shell.cmd("chmod 755 " + MODULE_PATH).exec();
Shell.cmd("chmod 755 " + SO_STORAGE_DIR).exec();
// Verify directories exist
Shell.Result verify = Shell.cmd("ls -la " + MODULE_PATH).exec();
if (verify.isSuccess()) {
Log.i(TAG, "Module directory ready: " + String.join("\n", verify.getOut()));
}
} }
private void loadConfig() { private void loadConfig() {
@@ -94,6 +122,13 @@ public class ConfigManager {
} }
appConfig.enabled = enabled; appConfig.enabled = enabled;
saveConfig(); saveConfig();
// 自动部署或清理 SO 文件
if (enabled) {
deploySoFilesToApp(packageName);
} else {
cleanupAppSoFiles(packageName);
}
} }
public List<SoFile> getAppSoFiles(String packageName) { public List<SoFile> getAppSoFiles(String packageName) {
@@ -163,6 +198,11 @@ public class ConfigManager {
// Add reference to the global SO file // Add reference to the global SO file
appConfig.soFiles.add(globalSoFile); appConfig.soFiles.add(globalSoFile);
saveConfig(); saveConfig();
// If app is enabled, deploy the new SO file
if (appConfig.enabled) {
deploySoFilesToApp(packageName);
}
} }
public void removeSoFileFromApp(String packageName, SoFile soFile) { public void removeSoFileFromApp(String packageName, SoFile soFile) {
@@ -171,6 +211,11 @@ public class ConfigManager {
appConfig.soFiles.removeIf(s -> s.storedPath.equals(soFile.storedPath)); appConfig.soFiles.removeIf(s -> s.storedPath.equals(soFile.storedPath));
saveConfig(); saveConfig();
// If app is enabled, re-deploy to update SO files
if (appConfig.enabled) {
deploySoFilesToApp(packageName);
}
} }
public boolean getHideInjection() { public boolean getHideInjection() {
@@ -182,6 +227,152 @@ public class ConfigManager {
saveConfig(); saveConfig();
} }
// Copy SO files directly to app's data directory
private void deploySoFilesToApp(String packageName) {
AppConfig appConfig = config.perAppConfig.get(packageName);
if (appConfig == null || appConfig.soFiles.isEmpty()) {
Log.w(TAG, "No SO files to deploy for: " + packageName);
return;
}
// First check if we have root access
if (!Shell.getShell().isRoot()) {
Log.e(TAG, "No root access available!");
return;
}
// Create files directory in app's data dir
String filesDir = "/data/data/" + packageName + "/files";
// Use su -c for better compatibility
Shell.Result mkdirResult = Shell.cmd("su -c 'mkdir -p " + filesDir + "'").exec();
if (!mkdirResult.isSuccess()) {
Log.e(TAG, "Failed to create directory: " + filesDir);
Log.e(TAG, "Error: " + String.join("\n", mkdirResult.getErr()));
// Try without su -c
mkdirResult = Shell.cmd("mkdir -p " + filesDir).exec();
if (!mkdirResult.isSuccess()) {
Log.e(TAG, "Also failed without su -c");
return;
}
}
// Set proper permissions and ownership
Shell.cmd("chmod 755 " + filesDir).exec();
// Get UID for the package
Shell.Result uidResult = Shell.cmd("stat -c %u /data/data/" + packageName).exec();
String uid = "";
if (uidResult.isSuccess() && !uidResult.getOut().isEmpty()) {
uid = uidResult.getOut().get(0).trim();
Log.i(TAG, "Package UID: " + uid);
}
// Copy each SO file configured for this app
for (SoFile soFile : appConfig.soFiles) {
// Extract mapped filename
String mappedName = new File(soFile.storedPath).getName();
String destPath = filesDir + "/" + mappedName;
// Check if source file exists
Shell.Result checkResult = Shell.cmd("test -f \"" + soFile.storedPath + "\" && echo 'exists'").exec();
if (!checkResult.isSuccess() || checkResult.getOut().isEmpty()) {
Log.e(TAG, "Source SO file not found: " + soFile.storedPath);
continue;
}
Log.i(TAG, "Copying: " + soFile.storedPath + " to " + destPath);
// Copy file using cat to avoid permission issues
String copyCmd = "cat \"" + soFile.storedPath + "\" > \"" + destPath + "\"";
Shell.Result result = Shell.cmd(copyCmd).exec();
if (!result.isSuccess()) {
Log.e(TAG, "Failed with cat, trying cp");
// Fallback to cp
result = Shell.cmd("cp -f \"" + soFile.storedPath + "\" \"" + destPath + "\"").exec();
}
// Set permissions
Shell.cmd("chmod 755 \"" + destPath + "\"").exec();
// Set ownership if we have the UID
if (!uid.isEmpty()) {
Shell.cmd("chown " + uid + ":" + uid + " \"" + destPath + "\"").exec();
}
// Verify the file was copied
Shell.Result verifyResult = Shell.cmd("ls -la \"" + destPath + "\" 2>/dev/null").exec();
if (verifyResult.isSuccess() && !verifyResult.getOut().isEmpty()) {
Log.i(TAG, "Successfully deployed: " + String.join(" ", verifyResult.getOut()));
} else {
Log.e(TAG, "Failed to verify SO file copy: " + destPath);
// Try another verification method
Shell.Result sizeResult = Shell.cmd("stat -c %s \"" + destPath + "\" 2>/dev/null").exec();
if (sizeResult.isSuccess() && !sizeResult.getOut().isEmpty()) {
Log.i(TAG, "File exists with size: " + sizeResult.getOut().get(0) + " bytes");
}
}
}
Log.i(TAG, "Deployment complete for: " + packageName);
}
// Clean up deployed SO files when app is disabled
private void cleanupAppSoFiles(String packageName) {
AppConfig appConfig = config.perAppConfig.get(packageName);
if (appConfig == null || appConfig.soFiles.isEmpty()) {
Log.w(TAG, "No SO files to clean up for: " + packageName);
return;
}
// First check if we have root access
if (!Shell.getShell().isRoot()) {
Log.e(TAG, "No root access available!");
return;
}
String filesDir = "/data/data/" + packageName + "/files";
// Only delete the SO files we deployed, not the entire directory
for (SoFile soFile : appConfig.soFiles) {
String mappedName = new File(soFile.storedPath).getName();
String filePath = filesDir + "/" + mappedName;
Log.i(TAG, "Cleaning up: " + filePath);
// Check if file exists before trying to delete
Shell.Result checkResult = Shell.cmd("test -f \"" + filePath + "\" && echo 'exists'").exec();
if (checkResult.isSuccess() && !checkResult.getOut().isEmpty()) {
// Try to remove the file
Shell.Result result = Shell.cmd("rm -f \"" + filePath + "\"").exec();
// Verify deletion
Shell.Result verifyResult = Shell.cmd("test -f \"" + filePath + "\" && echo 'still_exists'").exec();
if (!verifyResult.isSuccess() || verifyResult.getOut().isEmpty()) {
Log.i(TAG, "Successfully deleted SO file: " + filePath);
} else {
Log.e(TAG, "Failed to delete SO file: " + filePath);
// Try with su -c
Shell.cmd("su -c 'rm -f \"" + filePath + "\"'").exec();
}
} else {
Log.w(TAG, "SO file not found for cleanup: " + filePath);
}
}
Log.i(TAG, "Cleanup complete for: " + packageName);
}
// Deploy SO files for all enabled apps
public void deployAllSoFiles() {
for (Map.Entry<String, AppConfig> entry : config.perAppConfig.entrySet()) {
if (entry.getValue().enabled) {
deploySoFilesToApp(entry.getKey());
}
}
}
// Data classes // Data classes
public static class ModuleConfig { public static class ModuleConfig {
public boolean enabled = true; public boolean enabled = true;

View File

@@ -43,6 +43,8 @@ public class SoManagerFragment extends Fragment {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
configManager = new ConfigManager(requireContext()); configManager = new ConfigManager(requireContext());
// Ensure module directories exist
configManager.ensureModuleDirectories();
// Initialize file picker // Initialize file picker
filePickerLauncher = registerForActivityResult( filePickerLauncher = registerForActivityResult(

View File

@@ -175,8 +175,10 @@ namespace Config {
auto it = g_config.perAppConfig.find(packageName); auto it = g_config.perAppConfig.find(packageName);
if (it != g_config.perAppConfig.end()) { if (it != g_config.perAppConfig.end()) {
LOGD("Found app config for %s with %zu SO files", packageName.c_str(), it->second.soFiles.size());
return it->second.soFiles; return it->second.soFiles;
} }
LOGD("No app config found for %s", packageName.c_str());
return {}; return {};
} }

View File

@@ -7,6 +7,6 @@
#include <stddef.h> #include <stddef.h>
void hack_prepare(const char *game_data_dir, void *data, size_t length); void hack_prepare(const char *game_data_dir, const char *package_name, void *data, size_t length);
#endif //ZYGISK_IL2CPPDUMPER_HACK_H #endif //ZYGISK_IL2CPPDUMPER_HACK_H

View File

@@ -7,63 +7,51 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h>
// External function from newriruhide.cpp
extern "C" void riru_hide(const char *name);
void load_so_file(const char *game_data_dir, const Config::SoFile &soFile) { void load_so_file(const char *game_data_dir, const Config::SoFile &soFile) {
char dest_path[512]; // Extract the mapped filename from storedPath (e.g., "1750851324251_libmylib.so")
snprintf(dest_path, sizeof(dest_path), "%s/files/%s", game_data_dir, soFile.name.c_str()); const char *mapped_name = strrchr(soFile.storedPath.c_str(), '/');
if (!mapped_name) {
mapped_name = soFile.storedPath.c_str();
} else {
mapped_name++; // Skip the '/'
}
// Copy SO file from storage to app directory // The file should already be in app's files directory
int src_fd = open(soFile.storedPath.c_str(), O_RDONLY); char so_path[512];
if (src_fd < 0) { snprintf(so_path, sizeof(so_path), "%s/files/%s", game_data_dir, mapped_name);
LOGE("Failed to open source SO: %s", soFile.storedPath.c_str());
// Check if file exists
if (access(so_path, F_OK) != 0) {
LOGE("SO file not found: %s", so_path);
return; return;
} }
int dest_fd = open(dest_path, O_WRONLY | O_CREAT | O_TRUNC, 0755);
if (dest_fd < 0) {
LOGE("Failed to create dest SO: %s", dest_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 SO file");
close(src_fd);
close(dest_fd);
return;
}
}
close(src_fd);
close(dest_fd);
chmod(dest_path, 0755);
// Load the SO file // Load the SO file
void *handle = dlopen(dest_path, RTLD_NOW | RTLD_LOCAL); void *handle = dlopen(so_path, RTLD_NOW | RTLD_LOCAL);
if (handle) { if (handle) {
LOGI("Successfully loaded SO: %s", soFile.name.c_str()); LOGI("Successfully loaded SO: %s (mapped: %s)", soFile.name.c_str(), mapped_name);
// Hide if configured // Hide if configured
if (Config::shouldHideInjection()) { if (Config::shouldHideInjection()) {
// Call hide function if available // Hide using the mapped name since that's what we loaded
void (*hide_func)(const char*) = (void(*)(const char*))dlsym(handle, "riru_hide"); riru_hide(mapped_name);
if (hide_func) { LOGI("Applied riru_hide to: %s", mapped_name);
hide_func(soFile.name.c_str());
}
} }
} else { } else {
LOGE("Failed to load SO: %s - %s", dest_path, dlerror()); LOGE("Failed to load SO: %s - %s", so_path, dlerror());
} }
} }
void hack_thread_func(const char *game_data_dir, const char *package_name) { void hack_thread_func(const char *game_data_dir, const char *package_name) {
LOGI("Hack thread started for package: %s", package_name); LOGI("Hack thread started for package: %s", package_name);
// Wait a bit for app to initialize // Wait a bit for app to initialize and files to be copied
sleep(1); sleep(2);
// Get SO files for this app // Get SO files for this app
auto soFiles = Config::getAppSoFiles(package_name); auto soFiles = Config::getAppSoFiles(package_name);
@@ -71,26 +59,13 @@ void hack_thread_func(const char *game_data_dir, const char *package_name) {
// Load each SO file // Load each SO file
for (const auto &soFile : soFiles) { for (const auto &soFile : soFiles) {
LOGI("Loading SO: %s", soFile.name.c_str()); LOGI("Loading SO: %s (stored as: %s)", soFile.name.c_str(), soFile.storedPath.c_str());
load_so_file(game_data_dir, soFile); load_so_file(game_data_dir, soFile);
} }
} }
void hack_prepare(const char *game_data_dir, void *data, size_t length) { void hack_prepare(const char *game_data_dir, const char *package_name, void *data, size_t length) {
// Get package name from game_data_dir LOGI("hack_prepare called for package: %s, dir: %s", package_name, game_data_dir);
// Format: /data/user/0/com.example.app or /data/data/com.example.app
const char *package_name = nullptr;
if (strstr(game_data_dir, "/data/user/")) {
package_name = strrchr(game_data_dir, '/');
if (package_name) package_name++;
} else if (strstr(game_data_dir, "/data/data/")) {
package_name = game_data_dir + strlen("/data/data/");
}
if (!package_name) {
LOGE("Failed to extract package name from: %s", game_data_dir);
return;
}
std::thread hack_thread(hack_thread_func, game_data_dir, package_name); std::thread hack_thread(hack_thread_func, game_data_dir, package_name);
hack_thread.detach(); hack_thread.detach();

View File

@@ -6,6 +6,9 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <cinttypes> #include <cinttypes>
#include <dirent.h>
#include <errno.h>
#include <time.h>
#include "hack.h" #include "hack.h"
#include "zygisk.hpp" #include "zygisk.hpp"
#include "game.h" #include "game.h"
@@ -21,6 +24,7 @@ public:
void onLoad(Api *api, JNIEnv *env) override { void onLoad(Api *api, JNIEnv *env) override {
this->api = api; this->api = api;
this->env = env; this->env = env;
enable_hack = false;
} }
void preAppSpecialize(AppSpecializeArgs *args) override { void preAppSpecialize(AppSpecializeArgs *args) override {
@@ -38,7 +42,8 @@ public:
void postAppSpecialize(const AppSpecializeArgs *) override { void postAppSpecialize(const AppSpecializeArgs *) override {
if (enable_hack) { if (enable_hack) {
std::thread hack_thread(hack_prepare, _data_dir, data, length); // Then start hack thread
std::thread hack_thread(hack_prepare, _data_dir, _package_name, data, length);
hack_thread.detach(); hack_thread.detach();
} }
} }
@@ -48,9 +53,10 @@ private:
JNIEnv *env; JNIEnv *env;
bool enable_hack; bool enable_hack;
char *_data_dir; char *_data_dir;
char *_package_name;
void *data; void *data;
size_t length; size_t length;
void preSpecialize(const char *package_name, const char *app_data_dir) { void preSpecialize(const char *package_name, const char *app_data_dir) {
// Read configuration // Read configuration
Config::readConfig(); Config::readConfig();
@@ -61,6 +67,11 @@ private:
enable_hack = true; enable_hack = true;
_data_dir = new char[strlen(app_data_dir) + 1]; _data_dir = new char[strlen(app_data_dir) + 1];
strcpy(_data_dir, app_data_dir); strcpy(_data_dir, app_data_dir);
_package_name = new char[strlen(package_name) + 1];
strcpy(_package_name, package_name);
// ConfigApp is responsible for copying SO files
// We just need to load them
#if defined(__i386__) #if defined(__i386__)
auto path = "zygisk/armeabi-v7a.so"; auto path = "zygisk/armeabi-v7a.so";