feat:add so manager and config manager

This commit is contained in:
jiqiu2021
2025-06-25 18:08:46 +08:00
parent 499a26feec
commit 7d8b86f374
18 changed files with 1029 additions and 6 deletions

View File

@@ -35,7 +35,8 @@ aux_source_directory(xdl xdl-src)
add_library(${MODULE_NAME} SHARED
main.cpp
hack.cpp
hack_new.cpp
config.cpp
newriruhide.cpp
pmparser.cpp
${xdl-src})

View File

@@ -0,0 +1,189 @@
#include "config.h"
#include <fstream>
#include <sstream>
#include <android/log.h>
#define LOG_TAG "MyInjector"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
namespace Config {
static ModuleConfig g_config;
static bool g_configLoaded = false;
// Simple JSON parser for our specific format
std::string extractValue(const std::string& json, const std::string& key) {
size_t keyPos = json.find("\"" + key + "\"");
if (keyPos == std::string::npos) return "";
size_t colonPos = json.find(":", keyPos);
if (colonPos == std::string::npos) return "";
size_t valueStart = json.find_first_not_of(" \t\n", colonPos + 1);
if (valueStart == std::string::npos) return "";
if (json[valueStart] == '"') {
// String value
size_t valueEnd = json.find('"', valueStart + 1);
if (valueEnd == std::string::npos) return "";
return json.substr(valueStart + 1, valueEnd - valueStart - 1);
} else if (json[valueStart] == 't' || json[valueStart] == 'f') {
// Boolean value
return (json.substr(valueStart, 4) == "true") ? "true" : "false";
}
return "";
}
void parseAppConfig(const std::string& packageName, const std::string& appJson) {
AppConfig appConfig;
// Parse enabled
std::string enabledStr = extractValue(appJson, "enabled");
appConfig.enabled = (enabledStr == "true");
// Parse soFiles array
size_t soFilesPos = appJson.find("\"soFiles\"");
if (soFilesPos != std::string::npos) {
size_t arrayStart = appJson.find("[", soFilesPos);
size_t arrayEnd = appJson.find("]", arrayStart);
if (arrayStart != std::string::npos && arrayEnd != std::string::npos) {
std::string soFilesArray = appJson.substr(arrayStart + 1, arrayEnd - arrayStart - 1);
// Parse each SO file object
size_t objStart = 0;
while ((objStart = soFilesArray.find("{", objStart)) != std::string::npos) {
size_t objEnd = soFilesArray.find("}", objStart);
if (objEnd == std::string::npos) break;
std::string soFileObj = soFilesArray.substr(objStart, objEnd - objStart + 1);
SoFile soFile;
soFile.name = extractValue(soFileObj, "name");
soFile.storedPath = extractValue(soFileObj, "storedPath");
soFile.originalPath = extractValue(soFileObj, "originalPath");
if (!soFile.storedPath.empty()) {
appConfig.soFiles.push_back(soFile);
LOGD("Added SO file: %s at %s", soFile.name.c_str(), soFile.storedPath.c_str());
}
objStart = objEnd + 1;
}
}
}
g_config.perAppConfig[packageName] = appConfig;
LOGD("Loaded config for app: %s, enabled: %d, SO files: %zu",
packageName.c_str(), appConfig.enabled, appConfig.soFiles.size());
}
ModuleConfig readConfig() {
if (g_configLoaded) {
return g_config;
}
const char* configPath = "/data/adb/modules/zygisk-myinjector/config.json";
std::ifstream file(configPath);
if (!file.is_open()) {
LOGE("Failed to open config file: %s", configPath);
g_configLoaded = true;
return g_config;
}
std::stringstream buffer;
buffer << file.rdbuf();
std::string json = buffer.str();
file.close();
// Parse global settings
std::string enabledStr = extractValue(json, "enabled");
g_config.enabled = (enabledStr != "false");
std::string hideStr = extractValue(json, "hideInjection");
g_config.hideInjection = (hideStr == "true");
LOGD("Module enabled: %d, hide injection: %d", g_config.enabled, g_config.hideInjection);
// Parse perAppConfig
size_t perAppPos = json.find("\"perAppConfig\"");
if (perAppPos != std::string::npos) {
size_t objStart = json.find("{", perAppPos + 14);
size_t objEnd = json.rfind("}");
if (objStart != std::string::npos && objEnd != std::string::npos) {
std::string perAppObj = json.substr(objStart + 1, objEnd - objStart - 1);
// Find each package config
size_t pos = 0;
while (pos < perAppObj.length()) {
// Find package name
size_t pkgStart = perAppObj.find("\"", pos);
if (pkgStart == std::string::npos) break;
size_t pkgEnd = perAppObj.find("\"", pkgStart + 1);
if (pkgEnd == std::string::npos) break;
std::string packageName = perAppObj.substr(pkgStart + 1, pkgEnd - pkgStart - 1);
// Find app config object
size_t appObjStart = perAppObj.find("{", pkgEnd);
if (appObjStart == std::string::npos) break;
// Find matching closing brace
int braceCount = 1;
size_t appObjEnd = appObjStart + 1;
while (appObjEnd < perAppObj.length() && braceCount > 0) {
if (perAppObj[appObjEnd] == '{') braceCount++;
else if (perAppObj[appObjEnd] == '}') braceCount--;
appObjEnd++;
}
if (braceCount == 0) {
std::string appConfigStr = perAppObj.substr(appObjStart, appObjEnd - appObjStart);
parseAppConfig(packageName, appConfigStr);
}
pos = appObjEnd;
}
}
}
g_configLoaded = true;
return g_config;
}
bool isAppEnabled(const std::string& packageName) {
if (!g_configLoaded) {
readConfig();
}
auto it = g_config.perAppConfig.find(packageName);
if (it != g_config.perAppConfig.end()) {
return it->second.enabled;
}
return false;
}
std::vector<SoFile> getAppSoFiles(const std::string& packageName) {
if (!g_configLoaded) {
readConfig();
}
auto it = g_config.perAppConfig.find(packageName);
if (it != g_config.perAppConfig.end()) {
return it->second.soFiles;
}
return {};
}
bool shouldHideInjection() {
if (!g_configLoaded) {
readConfig();
}
return g_config.hideInjection;
}
}

View File

@@ -0,0 +1,40 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <string>
#include <vector>
#include <unordered_map>
namespace Config {
struct SoFile {
std::string name;
std::string storedPath;
std::string originalPath;
};
struct AppConfig {
bool enabled = false;
std::vector<SoFile> soFiles;
};
struct ModuleConfig {
bool enabled = true;
bool hideInjection = false;
std::unordered_map<std::string, AppConfig> perAppConfig;
};
// Read configuration from file
ModuleConfig readConfig();
// Check if app is enabled for injection
bool isAppEnabled(const std::string& packageName);
// Get SO files for specific app
std::vector<SoFile> getAppSoFiles(const std::string& packageName);
// Get hide injection setting
bool shouldHideInjection();
}
#endif // CONFIG_H

View File

@@ -0,0 +1,97 @@
#include "hack.h"
#include "config.h"
#include "log.h"
#include <cstring>
#include <thread>
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
void load_so_file(const char *game_data_dir, const Config::SoFile &soFile) {
char dest_path[512];
snprintf(dest_path, sizeof(dest_path), "%s/files/%s", game_data_dir, soFile.name.c_str());
// Copy SO file from storage to app directory
int src_fd = open(soFile.storedPath.c_str(), O_RDONLY);
if (src_fd < 0) {
LOGE("Failed to open source SO: %s", soFile.storedPath.c_str());
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
void *handle = dlopen(dest_path, RTLD_NOW | RTLD_LOCAL);
if (handle) {
LOGI("Successfully loaded SO: %s", soFile.name.c_str());
// Hide if configured
if (Config::shouldHideInjection()) {
// Call hide function if available
void (*hide_func)(const char*) = (void(*)(const char*))dlsym(handle, "riru_hide");
if (hide_func) {
hide_func(soFile.name.c_str());
}
}
} else {
LOGE("Failed to load SO: %s - %s", dest_path, dlerror());
}
}
void hack_thread_func(const char *game_data_dir, const char *package_name) {
LOGI("Hack thread started for package: %s", package_name);
// Wait a bit for app to initialize
sleep(1);
// Get SO files for this app
auto soFiles = Config::getAppSoFiles(package_name);
LOGI("Found %zu SO files to load", soFiles.size());
// Load each SO file
for (const auto &soFile : soFiles) {
LOGI("Loading SO: %s", soFile.name.c_str());
load_so_file(game_data_dir, soFile);
}
}
void hack_prepare(const char *game_data_dir, void *data, size_t length) {
// Get package name from 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);
hack_thread.detach();
}

View File

@@ -11,6 +11,7 @@
#include "game.h"
#include "log.h"
#include "dlfcn.h"
#include "config.h"
using zygisk::Api;
using zygisk::AppSpecializeArgs;
using zygisk::ServerSpecializeArgs;
@@ -51,7 +52,11 @@ private:
size_t length;
void preSpecialize(const char *package_name, const char *app_data_dir) {
if (strcmp(package_name, AimPackageName) == 0) {
// Read configuration
Config::readConfig();
// Check if this app is enabled for injection
if (Config::isAppEnabled(package_name)) {
LOGI("成功注入目标进程: %s", package_name);
enable_hack = true;
_data_dir = new char[strlen(app_data_dir) + 1];