diff --git a/configapp/build.gradle b/configapp/build.gradle index 56e0e18..8593884 100644 --- a/configapp/build.gradle +++ b/configapp/build.gradle @@ -29,8 +29,8 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 coreLibraryDesugaringEnabled false } } diff --git a/configapp/src/main/java/com/jiqiu/configapp/AppListFragment.java b/configapp/src/main/java/com/jiqiu/configapp/AppListFragment.java index 0916b71..e50778c 100644 --- a/configapp/src/main/java/com/jiqiu/configapp/AppListFragment.java +++ b/configapp/src/main/java/com/jiqiu/configapp/AppListFragment.java @@ -13,6 +13,8 @@ import android.widget.ProgressBar; import android.app.Dialog; import android.widget.CheckBox; import android.widget.ImageView; +import android.widget.RadioButton; +import android.widget.RadioGroup; import android.widget.TextView; import androidx.annotation.NonNull; @@ -134,15 +136,24 @@ public class AppListFragment extends Fragment implements AppListAdapter.OnAppTog TextView packageName = dialogView.findViewById(R.id.packageName); RecyclerView soListRecyclerView = dialogView.findViewById(R.id.soListRecyclerView); TextView emptyText = dialogView.findViewById(R.id.emptyText); - SwitchMaterial switchHideInjection = dialogView.findViewById(R.id.switchHideInjection); + RadioGroup injectionMethodGroup = dialogView.findViewById(R.id.injectionMethodGroup); + RadioButton radioStandardInjection = dialogView.findViewById(R.id.radioStandardInjection); + RadioButton radioRiruInjection = dialogView.findViewById(R.id.radioRiruInjection); + RadioButton radioCustomLinkerInjection = dialogView.findViewById(R.id.radioCustomLinkerInjection); appIcon.setImageDrawable(appInfo.getAppIcon()); appName.setText(appInfo.getAppName()); packageName.setText(appInfo.getPackageName()); // Load current config - boolean hideInjection = configManager.getHideInjection(); - switchHideInjection.setChecked(hideInjection); + String injectionMethod = configManager.getAppInjectionMethod(appInfo.getPackageName()); + if ("custom_linker".equals(injectionMethod)) { + radioCustomLinkerInjection.setChecked(true); + } else if ("riru".equals(injectionMethod)) { + radioRiruInjection.setChecked(true); + } else { + radioStandardInjection.setChecked(true); + } // Setup SO list List globalSoFiles = configManager.getAllSoFiles(); @@ -165,8 +176,16 @@ public class AppListFragment extends Fragment implements AppListAdapter.OnAppTog .setTitle("配置注入") .setView(dialogView) .setPositiveButton("保存", (dialog, which) -> { - // Save hide injection setting - configManager.setHideInjection(switchHideInjection.isChecked()); + // Save injection method + String selectedMethod; + if (radioCustomLinkerInjection.isChecked()) { + selectedMethod = "custom_linker"; + } else if (radioRiruInjection.isChecked()) { + selectedMethod = "riru"; + } else { + selectedMethod = "standard"; + } + configManager.setAppInjectionMethod(appInfo.getPackageName(), selectedMethod); // Save SO selection if (soListRecyclerView.getAdapter() != null) { diff --git a/configapp/src/main/java/com/jiqiu/configapp/ConfigManager.java b/configapp/src/main/java/com/jiqiu/configapp/ConfigManager.java index e1c2e7e..b85dacd 100644 --- a/configapp/src/main/java/com/jiqiu/configapp/ConfigManager.java +++ b/configapp/src/main/java/com/jiqiu/configapp/ConfigManager.java @@ -227,6 +227,24 @@ public class ConfigManager { saveConfig(); } + public String getAppInjectionMethod(String packageName) { + AppConfig appConfig = config.perAppConfig.get(packageName); + if (appConfig == null) { + return "standard"; // Default to standard + } + return appConfig.injectionMethod != null ? appConfig.injectionMethod : "standard"; + } + + public void setAppInjectionMethod(String packageName, String method) { + AppConfig appConfig = config.perAppConfig.get(packageName); + if (appConfig == null) { + appConfig = new AppConfig(); + config.perAppConfig.put(packageName, appConfig); + } + appConfig.injectionMethod = method; + saveConfig(); + } + // Copy SO files directly to app's data directory private void deploySoFilesToApp(String packageName) { AppConfig appConfig = config.perAppConfig.get(packageName); @@ -384,6 +402,7 @@ public class ConfigManager { public static class AppConfig { public boolean enabled = false; public List soFiles = new ArrayList<>(); + public String injectionMethod = "standard"; // "standard", "riru" or "custom_linker" } public static class SoFile { diff --git a/configapp/src/main/res/layout/dialog_app_config.xml b/configapp/src/main/res/layout/dialog_app_config.xml index bc1df5e..af1eb05 100644 --- a/configapp/src/main/res/layout/dialog_app_config.xml +++ b/configapp/src/main/res/layout/dialog_app_config.xml @@ -77,20 +77,66 @@ android:textColor="?android:attr/textColorTertiary" android:visibility="gone" /> - - + android:layout_marginTop="16dp" + android:layout_marginBottom="8dp" /> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/module/src/main/cpp/CMakeLists.txt b/module/src/main/cpp/CMakeLists.txt index 84b4a40..f6af69f 100644 --- a/module/src/main/cpp/CMakeLists.txt +++ b/module/src/main/cpp/CMakeLists.txt @@ -29,10 +29,14 @@ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") include_directories( xdl/include + mylinker/include ) aux_source_directory(xdl xdl-src) +# Build mylinker as a subdirectory +add_subdirectory(mylinker) + add_library(${MODULE_NAME} SHARED main.cpp hack_new.cpp @@ -40,7 +44,7 @@ add_library(${MODULE_NAME} SHARED newriruhide.cpp pmparser.cpp ${xdl-src}) -target_link_libraries(${MODULE_NAME} log) +target_link_libraries(${MODULE_NAME} log mylinker) if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") add_custom_command(TARGET ${MODULE_NAME} POST_BUILD diff --git a/module/src/main/cpp/config.cpp b/module/src/main/cpp/config.cpp index 1f3cab3..fd7209a 100644 --- a/module/src/main/cpp/config.cpp +++ b/module/src/main/cpp/config.cpp @@ -43,6 +43,16 @@ namespace Config { std::string enabledStr = extractValue(appJson, "enabled"); appConfig.enabled = (enabledStr == "true"); + // Parse injection method + std::string methodStr = extractValue(appJson, "injectionMethod"); + if (methodStr == "2" || methodStr == "custom_linker") { + appConfig.injectionMethod = InjectionMethod::CUSTOM_LINKER; + } else if (methodStr == "1" || methodStr == "riru") { + appConfig.injectionMethod = InjectionMethod::RIRU; + } else { + appConfig.injectionMethod = InjectionMethod::STANDARD; + } + // Parse soFiles array size_t soFilesPos = appJson.find("\"soFiles\""); if (soFilesPos != std::string::npos) { @@ -76,8 +86,10 @@ namespace Config { } g_config.perAppConfig[packageName] = appConfig; - LOGD("Loaded config for app: %s, enabled: %d, SO files: %zu", - packageName.c_str(), appConfig.enabled, appConfig.soFiles.size()); + const char* methodName = appConfig.injectionMethod == InjectionMethod::CUSTOM_LINKER ? "custom_linker" : + appConfig.injectionMethod == InjectionMethod::RIRU ? "riru" : "standard"; + LOGD("Loaded config for app: %s, enabled: %d, method: %s, SO files: %zu", + packageName.c_str(), appConfig.enabled, methodName, appConfig.soFiles.size()); } ModuleConfig readConfig() { @@ -188,4 +200,16 @@ namespace Config { } return g_config.hideInjection; } + + InjectionMethod getAppInjectionMethod(const std::string& packageName) { + if (!g_configLoaded) { + readConfig(); + } + + auto it = g_config.perAppConfig.find(packageName); + if (it != g_config.perAppConfig.end()) { + return it->second.injectionMethod; + } + return InjectionMethod::STANDARD; + } } \ No newline at end of file diff --git a/module/src/main/cpp/config.h b/module/src/main/cpp/config.h index 7401555..86b4583 100644 --- a/module/src/main/cpp/config.h +++ b/module/src/main/cpp/config.h @@ -13,8 +13,15 @@ namespace Config { std::string originalPath; }; + enum class InjectionMethod { + STANDARD = 0, + RIRU = 1, + CUSTOM_LINKER = 2 + }; + struct AppConfig { bool enabled = false; + InjectionMethod injectionMethod = InjectionMethod::STANDARD; std::vector soFiles; }; @@ -35,6 +42,9 @@ namespace Config { // Get hide injection setting bool shouldHideInjection(); + + // Get injection method for specific app + InjectionMethod getAppInjectionMethod(const std::string& packageName); } #endif // CONFIG_H \ No newline at end of file diff --git a/module/src/main/cpp/hack.h b/module/src/main/cpp/hack.h index 0f787bf..29fbdce 100644 --- a/module/src/main/cpp/hack.h +++ b/module/src/main/cpp/hack.h @@ -6,7 +6,8 @@ #define ZYGISK_IL2CPPDUMPER_HACK_H #include +#include -void hack_prepare(const char *game_data_dir, const char *package_name, void *data, size_t length); +void hack_prepare(const char *game_data_dir, const char *package_name, void *data, size_t length, JavaVM *vm); #endif //ZYGISK_IL2CPPDUMPER_HACK_H diff --git a/module/src/main/cpp/hack_new.cpp b/module/src/main/cpp/hack_new.cpp index 68c5f21..a1c18a4 100644 --- a/module/src/main/cpp/hack_new.cpp +++ b/module/src/main/cpp/hack_new.cpp @@ -1,6 +1,7 @@ #include "hack.h" #include "config.h" #include "log.h" +#include "mylinker.h" #include #include #include @@ -8,11 +9,12 @@ #include #include #include +#include // 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_standard(const char *game_data_dir, const Config::SoFile &soFile) { // Extract the mapped filename from storedPath (e.g., "1750851324251_libmylib.so") const char *mapped_name = strrchr(soFile.storedPath.c_str(), '/'); if (!mapped_name) { @@ -31,10 +33,38 @@ void load_so_file(const char *game_data_dir, const Config::SoFile &soFile) { return; } - // Load the SO file + // Load the SO file using standard dlopen (no hiding) void *handle = dlopen(so_path, RTLD_NOW | RTLD_LOCAL); if (handle) { - LOGI("Successfully loaded SO: %s (mapped: %s)", soFile.name.c_str(), mapped_name); + LOGI("Successfully loaded SO via standard dlopen: %s (mapped: %s)", soFile.name.c_str(), mapped_name); + } else { + LOGE("Failed to load SO via standard dlopen: %s - %s", so_path, dlerror()); + } +} + +void load_so_file_riru(const char *game_data_dir, const Config::SoFile &soFile) { + // Extract the mapped filename from storedPath (e.g., "1750851324251_libmylib.so") + const char *mapped_name = strrchr(soFile.storedPath.c_str(), '/'); + if (!mapped_name) { + mapped_name = soFile.storedPath.c_str(); + } else { + mapped_name++; // Skip the '/' + } + + // The file should already be in app's files directory + char so_path[512]; + snprintf(so_path, sizeof(so_path), "%s/files/%s", game_data_dir, mapped_name); + + // Check if file exists + if (access(so_path, F_OK) != 0) { + LOGE("SO file not found: %s", so_path); + return; + } + + // Load the SO file using dlopen (Riru method) + void *handle = dlopen(so_path, RTLD_NOW | RTLD_LOCAL); + if (handle) { + LOGI("Successfully loaded SO via Riru: %s (mapped: %s)", soFile.name.c_str(), mapped_name); // Hide if configured if (Config::shouldHideInjection()) { @@ -43,30 +73,81 @@ void load_so_file(const char *game_data_dir, const Config::SoFile &soFile) { LOGI("Applied riru_hide to: %s", mapped_name); } } else { - LOGE("Failed to load SO: %s - %s", so_path, dlerror()); + LOGE("Failed to load SO via Riru: %s - %s", so_path, dlerror()); } } -void hack_thread_func(const char *game_data_dir, const char *package_name) { +void load_so_file_custom_linker(const char *game_data_dir, const Config::SoFile &soFile, JavaVM *vm) { + // Extract the mapped filename from storedPath + const char *mapped_name = strrchr(soFile.storedPath.c_str(), '/'); + if (!mapped_name) { + mapped_name = soFile.storedPath.c_str(); + } else { + mapped_name++; // Skip the '/' + } + + // The file should already be in app's files directory + char so_path[512]; + snprintf(so_path, sizeof(so_path), "%s/files/%s", game_data_dir, mapped_name); + + // Check if file exists + if (access(so_path, F_OK) != 0) { + LOGE("SO file not found: %s", so_path); + return; + } + + // Load the SO file using custom linker + if (mylinker_load_library(so_path, vm)) { + LOGI("Successfully loaded SO via custom linker: %s (mapped: %s)", soFile.name.c_str(), mapped_name); + + // Custom linker doesn't appear in maps, so no need to hide + if (Config::shouldHideInjection()) { + LOGI("Custom linker injection is inherently hidden"); + } + } else { + LOGE("Failed to load SO via custom linker: %s", so_path); + } +} + +void hack_thread_func(const char *game_data_dir, const char *package_name, JavaVM *vm) { LOGI("Hack thread started for package: %s", package_name); // Wait a bit for app to initialize and files to be copied sleep(2); + // Get injection method for this app + Config::InjectionMethod method = Config::getAppInjectionMethod(package_name); + const char* methodName = method == Config::InjectionMethod::CUSTOM_LINKER ? "Custom Linker" : + method == Config::InjectionMethod::RIRU ? "Riru" : "Standard"; + LOGI("Using injection method: %s", methodName); + // 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 + // Load each SO file using the configured method for (const auto &soFile : soFiles) { LOGI("Loading SO: %s (stored as: %s)", soFile.name.c_str(), soFile.storedPath.c_str()); - load_so_file(game_data_dir, soFile); + + if (method == Config::InjectionMethod::CUSTOM_LINKER) { + load_so_file_custom_linker(game_data_dir, soFile, vm); + } else if (method == Config::InjectionMethod::RIRU) { + load_so_file_riru(game_data_dir, soFile); + } else { + load_so_file_standard(game_data_dir, soFile); + } + } + + // Cleanup custom linker resources when done (if used) + if (method == Config::InjectionMethod::CUSTOM_LINKER) { + // Keep libraries loaded, don't cleanup + LOGI("Custom linker injection completed, libraries remain loaded"); } } -void hack_prepare(const char *game_data_dir, const char *package_name, void *data, size_t length) { +void hack_prepare(const char *game_data_dir, const char *package_name, void *data, size_t length, JavaVM *vm) { LOGI("hack_prepare called for package: %s, dir: %s", package_name, game_data_dir); - std::thread hack_thread(hack_thread_func, game_data_dir, package_name); + std::thread hack_thread(hack_thread_func, game_data_dir, package_name, vm); hack_thread.detach(); } \ No newline at end of file diff --git a/module/src/main/cpp/main.cpp b/module/src/main/cpp/main.cpp index 59755dd..4025c52 100644 --- a/module/src/main/cpp/main.cpp +++ b/module/src/main/cpp/main.cpp @@ -42,9 +42,15 @@ public: void postAppSpecialize(const AppSpecializeArgs *) override { if (enable_hack) { - // Then start hack thread - std::thread hack_thread(hack_prepare, _data_dir, _package_name, data, length); - hack_thread.detach(); + // Get JavaVM + JavaVM *vm = nullptr; + if (env->GetJavaVM(&vm) == JNI_OK) { + // Then start hack thread with JavaVM + std::thread hack_thread(hack_prepare, _data_dir, _package_name, data, length, vm); + hack_thread.detach(); + } else { + LOGE("Failed to get JavaVM"); + } } } diff --git a/module/src/main/cpp/mylinker/CMakeLists.txt b/module/src/main/cpp/mylinker/CMakeLists.txt new file mode 100644 index 0000000..a7259ac --- /dev/null +++ b/module/src/main/cpp/mylinker/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.18.1) +project(ElfLoader) + +set(CMAKE_CXX_STANDARD 17) + +include_directories(include) + +set(SOURCES + mylinker.cpp + elf_loader.cpp + elf_reader.cpp + memory_manager.cpp + relocator.cpp + soinfo_manager.cpp + utils.cpp +) + +find_library(log-lib log) + +# Build as shared library +add_library(mylinker SHARED ${SOURCES}) + +target_link_libraries(mylinker ${log-lib}) + +# Only build executable for standalone testing +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + add_executable(elf_loader_test main.cpp elf_loader.cpp elf_reader.cpp memory_manager.cpp relocator.cpp soinfo_manager.cpp utils.cpp) + target_link_libraries(elf_loader_test ${log-lib}) +endif() diff --git a/module/src/main/cpp/mylinker/elf_loader.cpp b/module/src/main/cpp/mylinker/elf_loader.cpp new file mode 100644 index 0000000..0692a95 --- /dev/null +++ b/module/src/main/cpp/mylinker/elf_loader.cpp @@ -0,0 +1,187 @@ +#include "elf_loader.h" + +ElfLoader::ElfLoader() : loaded_si_(nullptr) { + reader_ = std::make_unique(); + memory_manager_ = std::make_unique(); + soinfo_manager_ = std::make_unique(); + relocator_ = std::make_unique(); +} + +ElfLoader::~ElfLoader() { +} + +bool ElfLoader::LoadLibrary(const char* path) { + LOGI("Loading library: %s", path); + + if (!reader_->Open(path)) { + LOGE("Failed to open %s", path); + return false; + } + + if (!reader_->Read()) { + LOGE("Failed to read ELF file"); + return false; + } + + if (!memory_manager_->ReserveAddressSpace(reader_->GetProgramHeaders(), + reader_->GetProgramHeaderCount())) { + LOGE("Failed to reserve address space"); + return false; + } + + if (!memory_manager_->LoadSegments(reader_->GetProgramHeaders(), + reader_->GetProgramHeaderCount(), + reader_->GetMappedAddr(), + reader_->GetFileSize())) { + LOGE("Failed to load segments"); + return false; + } + + if (!memory_manager_->FindPhdr(reader_->GetProgramHeaders(), + reader_->GetProgramHeaderCount())) { + LOGE("Failed to find program headers"); + return false; + } + + const char* basename = strrchr(path, '/'); + basename = basename ? basename + 1 : path; + loaded_si_ = soinfo_manager_->GetOrCreateSoinfo(basename); + + if (!soinfo_manager_->UpdateSoinfo(loaded_si_, memory_manager_.get(), reader_.get())) { + LOGE("Failed to update soinfo"); + return false; + } + + if (!soinfo_manager_->PrelinkImage(loaded_si_)) { + LOGE("Failed to prelink image"); + return false; + } + + if (!memory_manager_->ProtectSegments(reader_->GetProgramHeaders(), + reader_->GetProgramHeaderCount())) { + LOGE("Failed to protect segments"); + return false; + } + + if (!relocator_->LinkImage(loaded_si_)) { + LOGE("Failed to link image"); + return false; + } + + reader_->Close(); + + LOGI("Successfully loaded %s", path); + return true; +} + +void ElfLoader::CallConstructors() { + if (loaded_si_ == nullptr) { + return; + } + + LOGD("Constructors already called during linking"); +} + +void* ElfLoader::GetSymbol(const char* name) { + if (loaded_si_ == nullptr) { + LOGE("loaded_si_ is null"); + return nullptr; + } + + if (name == nullptr) { + LOGE("Symbol name is null"); + return nullptr; + } + + LOGD("Looking for symbol: %s", name); + LOGD("soinfo state: symtab=%p, strtab=%p, gnu_bucket=%p, bucket=%p", + loaded_si_->symtab, loaded_si_->strtab, loaded_si_->gnu_bucket, loaded_si_->bucket); + + if (loaded_si_->symtab != nullptr) { + if (loaded_si_->gnu_bucket != nullptr) { + LOGD("Trying GNU hash lookup for %s", name); + uint32_t hash = relocator_->gnu_hash(name); + LOGD("GNU hash for %s: 0x%x", name, hash); + + ElfW(Sym)* sym = relocator_->gnu_lookup(hash, name, loaded_si_); + if (sym != nullptr && sym->st_shndx != SHN_UNDEF) { + ElfW(Addr) addr = sym->st_value + loaded_si_->load_bias; + LOGD("Found symbol %s via GNU hash: st_value=0x%llx, load_bias=0x%llx, final_addr=0x%llx", + name, (unsigned long long)sym->st_value, (unsigned long long)loaded_si_->load_bias, (unsigned long long)addr); + + if (addr >= loaded_si_->base && addr < loaded_si_->base + loaded_si_->size) { + return reinterpret_cast(addr); + } else { + LOGE("Symbol %s address 0x%llx out of range [0x%llx, 0x%llx)", + name, (unsigned long long)addr, (unsigned long long)loaded_si_->base, + (unsigned long long)(loaded_si_->base + loaded_si_->size)); + } + } else { + LOGD("Symbol %s not found via GNU hash", name); + } + } + + if (loaded_si_->bucket != nullptr) { + LOGD("Trying ELF hash lookup for %s", name); + unsigned hash = relocator_->elf_hash(name); + LOGD("ELF hash for %s: 0x%x", name, hash); + + ElfW(Sym)* sym = relocator_->elf_lookup(hash, name, loaded_si_); + if (sym != nullptr && sym->st_shndx != SHN_UNDEF) { + ElfW(Addr) addr = sym->st_value + loaded_si_->load_bias; + LOGD("Found symbol %s via ELF hash: st_value=0x%llx, load_bias=0x%llx, final_addr=0x%llx", + name, (unsigned long long)sym->st_value, (unsigned long long)loaded_si_->load_bias, (unsigned long long)addr); + + if (addr >= loaded_si_->base && addr < loaded_si_->base + loaded_si_->size) { + return reinterpret_cast(addr); + } else { + LOGE("Symbol %s address 0x%llx out of range [0x%llx, 0x%llx)", + name, (unsigned long long)addr, (unsigned long long)loaded_si_->base, + (unsigned long long)(loaded_si_->base + loaded_si_->size)); + } + } else { + LOGD("Symbol %s not found via ELF hash", name); + } + } + + if (loaded_si_->gnu_bucket == nullptr && loaded_si_->bucket == nullptr) { + LOGD("No hash tables available, trying linear search"); + + if (loaded_si_->strtab != nullptr) { + size_t sym_count = 0; + if (loaded_si_->nchain > 0) { + sym_count = loaded_si_->nchain; + } else { + LOGD("Cannot determine symbol table size"); + return nullptr; + } + + LOGD("Trying linear search with %zu symbols", sym_count); + for (size_t i = 0; i < sym_count; ++i) { + ElfW(Sym)* sym = &loaded_si_->symtab[i]; + if (sym->st_name != 0 && sym->st_shndx != SHN_UNDEF) { + const char* sym_name = loaded_si_->strtab + sym->st_name; + if (strcmp(sym_name, name) == 0) { + ElfW(Addr) addr = sym->st_value + loaded_si_->load_bias; + LOGD("Found symbol %s via linear search: st_value=0x%llx, load_bias=0x%llx, final_addr=0x%llx", + name, (unsigned long long)sym->st_value, (unsigned long long)loaded_si_->load_bias, (unsigned long long)addr); + + if (addr >= loaded_si_->base && addr < loaded_si_->base + loaded_si_->size) { + return reinterpret_cast(addr); + } else { + LOGE("Symbol %s address 0x%llx out of range [0x%llx, 0x%llx)", + name, (unsigned long long)addr, (unsigned long long)loaded_si_->base, (unsigned long long)(loaded_si_->base + loaded_si_->size)); + } + } + } + } + LOGD("Symbol %s not found via linear search", name); + } + } + } else { + LOGE("Symbol table is null"); + } + + LOGE("Symbol %s not found in any method", name); + return nullptr; +} \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/elf_reader.cpp b/module/src/main/cpp/mylinker/elf_reader.cpp new file mode 100644 index 0000000..1b5ff82 --- /dev/null +++ b/module/src/main/cpp/mylinker/elf_reader.cpp @@ -0,0 +1,149 @@ +#include "elf_reader.h" +#include + +ElfReader::ElfReader() : fd_(-1), file_size_(0), file_offset_(0), + mapped_file_(nullptr), phdr_table_(nullptr), phdr_num_(0) { + memset(&header_, 0, sizeof(header_)); +} + +ElfReader::~ElfReader() { + Close(); +} + +bool ElfReader::Open(const char* path) { + path_ = path; + + struct stat sb; + fd_ = open(path, O_RDONLY | O_CLOEXEC); + if (fd_ < 0) { + LOGE("Cannot open %s: %s", path, strerror(errno)); + return false; + } + + if (fstat(fd_, &sb) < 0) { + LOGE("Cannot stat %s: %s", path, strerror(errno)); + close(fd_); + fd_ = -1; + return false; + } + + file_size_ = sb.st_size; + + // 映射整个文件到内存 + mapped_file_ = mmap(nullptr, file_size_, PROT_READ, MAP_PRIVATE, fd_, 0); + if (mapped_file_ == MAP_FAILED) { + LOGE("Cannot mmap %s: %s", path, strerror(errno)); + close(fd_); + fd_ = -1; + return false; + } + + return true; +} + +bool ElfReader::Read() { + if (!ReadElfHeader()) { + return false; + } + + if (!VerifyElfHeader()) { + return false; + } + + if (!ReadProgramHeaders()) { + return false; + } + + return true; +} + +void ElfReader::Close() { + if (mapped_file_ != nullptr && mapped_file_ != MAP_FAILED) { + munmap(mapped_file_, file_size_); + mapped_file_ = nullptr; + } + + if (fd_ >= 0) { + close(fd_); + fd_ = -1; + } + + if (phdr_table_ != nullptr) { + free(phdr_table_); + phdr_table_ = nullptr; + } +} + +bool ElfReader::ReadElfHeader() { + if (file_size_ < sizeof(ElfW(Ehdr))) { + LOGE("File too small for ELF header"); + return false; + } + + memcpy(&header_, mapped_file_, sizeof(header_)); + return true; +} + +bool ElfReader::VerifyElfHeader() { + if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) { + LOGE("Invalid ELF magic"); + return false; + } + + if (header_.e_ident[EI_CLASS] != ELFCLASS64) { + LOGE("Not a 64-bit ELF file"); + return false; + } + + if (header_.e_machine != EM_AARCH64) { + LOGE("Not an ARM64 ELF file"); + return false; + } + + if (header_.e_version != EV_CURRENT) { + LOGE("Invalid ELF version"); + return false; + } + + if (header_.e_type != ET_DYN) { + LOGE("Not a shared object"); + return false; + } + + LOGD("ELF Header: type=%d, machine=%d, entry=0x%llx, phoff=0x%llx, phnum=%d", + header_.e_type, header_.e_machine, (unsigned long long)header_.e_entry, + (unsigned long long)header_.e_phoff, header_.e_phnum); + + return true; +} + +bool ElfReader::ReadProgramHeaders() { + phdr_num_ = header_.e_phnum; + + if (phdr_num_ == 0) { + LOGE("No program headers"); + return false; + } + + if (header_.e_phentsize != sizeof(ElfW(Phdr))) { + LOGE("Invalid program header size"); + return false; + } + + size_t size = phdr_num_ * sizeof(ElfW(Phdr)); + + if (header_.e_phoff + size > file_size_) { + LOGE("Program headers out of file bounds"); + return false; + } + + phdr_table_ = static_cast(malloc(size)); + if (phdr_table_ == nullptr) { + LOGE("Cannot allocate memory for program headers"); + return false; + } + + memcpy(phdr_table_, static_cast(mapped_file_) + header_.e_phoff, size); + + return true; +} diff --git a/module/src/main/cpp/mylinker/include/common.h b/module/src/main/cpp/mylinker/include/common.h new file mode 100644 index 0000000..a200dcc --- /dev/null +++ b/module/src/main/cpp/mylinker/include/common.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define LOG_TAG "CustomLinker" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) + +#if defined(__LP64__) +#define ELFW(what) ELF64_ ## what +#else +#define ELFW(what) ELF32_ ## what +#endif + +#define PAGE_SIZE 4096 +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#define PAGE_START(addr) ((addr) & PAGE_MASK) +#define PAGE_END(addr) PAGE_START((addr) + PAGE_SIZE - 1) +#define PAGE_OFFSET(addr) ((addr) & (PAGE_SIZE - 1)) + +// 权限标志转换 +#define PFLAGS_TO_PROT(x) (((x) & PF_R) ? PROT_READ : 0) | \ + (((x) & PF_W) ? PROT_WRITE : 0) | \ + (((x) & PF_X) ? PROT_EXEC : 0) + +struct soinfo; +class ElfReader; +class MemoryManager; +class Relocator; \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/include/elf_loader.h b/module/src/main/cpp/mylinker/include/elf_loader.h new file mode 100644 index 0000000..9c4d9fd --- /dev/null +++ b/module/src/main/cpp/mylinker/include/elf_loader.h @@ -0,0 +1,27 @@ +#pragma once + +#include "common.h" +#include "elf_reader.h" +#include "memory_manager.h" +#include "soinfo_manager.h" +#include "relocator.h" + +class ElfLoader { +public: + ElfLoader(); + ~ElfLoader(); + + bool LoadLibrary(const char* path); + + void CallConstructors(); + + void* GetSymbol(const char* name); + +private: + std::unique_ptr reader_; + std::unique_ptr memory_manager_; + std::unique_ptr soinfo_manager_; + std::unique_ptr relocator_; + + soinfo* loaded_si_; +}; \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/include/elf_reader.h b/module/src/main/cpp/mylinker/include/elf_reader.h new file mode 100644 index 0000000..d212847 --- /dev/null +++ b/module/src/main/cpp/mylinker/include/elf_reader.h @@ -0,0 +1,38 @@ +#pragma once + +#include "common.h" + +class ElfReader { +public: + ElfReader(); + ~ElfReader(); + + bool Open(const char* path); + bool Read(); + void Close(); + + const ElfW(Ehdr)* GetHeader() const { return &header_; } + const ElfW(Phdr)* GetProgramHeaders() const { return phdr_table_; } + size_t GetProgramHeaderCount() const { return phdr_num_; } + + const char* GetPath() const { return path_.c_str(); } + int GetFd() const { return fd_; } + size_t GetFileSize() const { return file_size_; } + void* GetMappedAddr() const { return mapped_file_; } + +private: + bool ReadElfHeader(); + bool ReadProgramHeaders(); + bool VerifyElfHeader(); + + std::string path_; + int fd_; + size_t file_size_; + off64_t file_offset_; + + void* mapped_file_; + + ElfW(Ehdr) header_; + ElfW(Phdr)* phdr_table_; + size_t phdr_num_; +}; diff --git a/module/src/main/cpp/mylinker/include/memory_manager.h b/module/src/main/cpp/mylinker/include/memory_manager.h new file mode 100644 index 0000000..4f99cc2 --- /dev/null +++ b/module/src/main/cpp/mylinker/include/memory_manager.h @@ -0,0 +1,34 @@ +#pragma once + +#include "common.h" + +class MemoryManager { +public: + MemoryManager(); + ~MemoryManager(); + + bool ReserveAddressSpace(const ElfW(Phdr)* phdr_table, size_t phdr_num); + + bool LoadSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num, + void* mapped_file, size_t file_size); + + bool FindPhdr(const ElfW(Phdr)* phdr_table, size_t phdr_num); + + bool ProtectSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num); + + void* GetLoadStart() const { return load_start_; } + size_t GetLoadSize() const { return load_size_; } + ElfW(Addr) GetLoadBias() const { return load_bias_; } + const ElfW(Phdr)* GetLoadedPhdr() const { return loaded_phdr_; } + +private: + bool CheckPhdr(ElfW(Addr) loaded, const ElfW(Phdr)* phdr_table, size_t phdr_num); + size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, + size_t phdr_count, + ElfW(Addr)* min_vaddr); + + void* load_start_; + size_t load_size_; + ElfW(Addr) load_bias_; + const ElfW(Phdr)* loaded_phdr_; +}; diff --git a/module/src/main/cpp/mylinker/include/mylinker.h b/module/src/main/cpp/mylinker/include/mylinker.h new file mode 100644 index 0000000..c6e9121 --- /dev/null +++ b/module/src/main/cpp/mylinker/include/mylinker.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +__attribute__((visibility("default"))) bool mylinker_load_library(const char* library_path, JavaVM* vm); + +__attribute__((visibility("default"))) void* mylinker_get_symbol(const char* library_path, const char* symbol_name); + +__attribute__((visibility("default"))) void mylinker_cleanup(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/include/relocator.h b/module/src/main/cpp/mylinker/include/relocator.h new file mode 100644 index 0000000..b5d2ab9 --- /dev/null +++ b/module/src/main/cpp/mylinker/include/relocator.h @@ -0,0 +1,25 @@ +#pragma once + +#include "common.h" +#include "soinfo_manager.h" + +class Relocator { +public: + Relocator(); + ~Relocator(); + + bool RelocateImage(soinfo* si); + + bool LinkImage(soinfo* si); + + uint32_t gnu_hash(const char* name); + unsigned elf_hash(const char* name); + + ElfW(Sym)* gnu_lookup(uint32_t hash, const char* name, soinfo* si); + ElfW(Sym)* elf_lookup(unsigned hash, const char* name, soinfo* si); + +private: + bool ProcessRelaRelocation(soinfo* si, const ElfW(Rela)* rela); + + ElfW(Addr) FindSymbolAddress(const char* name, soinfo* si); +}; \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/include/soinfo_manager.h b/module/src/main/cpp/mylinker/include/soinfo_manager.h new file mode 100644 index 0000000..b0b3f48 --- /dev/null +++ b/module/src/main/cpp/mylinker/include/soinfo_manager.h @@ -0,0 +1,75 @@ +#pragma once + +#include "common.h" + +// soinfo结构体定义(简化版) +struct soinfo { + const char* name; + ElfW(Addr) base; + size_t size; + ElfW(Addr) load_bias; + + const ElfW(Phdr)* phdr; + size_t phnum; + + ElfW(Addr) entry; + + // Dynamic段信息 + ElfW(Dyn)* dynamic; + size_t dynamic_count; + + // 符号表相关 + const char* strtab; + ElfW(Sym)* symtab; + size_t nbucket; + size_t nchain; + uint32_t* bucket; + uint32_t* chain; + + // 重定位相关 + ElfW(Rela)* plt_rela; + size_t plt_rela_count; + ElfW(Rela)* rela; + size_t rela_count; + + // GNU hash + size_t gnu_nbucket; + uint32_t* gnu_bucket; + uint32_t* gnu_chain; + uint32_t gnu_maskwords; + uint32_t gnu_shift2; + ElfW(Addr)* gnu_bloom_filter; + + // 初始化函数 + void (*init_func)(); + void (**init_array)(); + size_t init_array_count; + void (**fini_array)(); + size_t fini_array_count; + + // 依赖库 + std::vector needed_libs; + + uint32_t flags; +}; + +class SoinfoManager { +public: + SoinfoManager(); + ~SoinfoManager(); + + soinfo* GetOrCreateSoinfo(const char* name); + + bool UpdateSoinfo(soinfo* si, MemoryManager* mm, ElfReader* reader); + + bool PrelinkImage(soinfo* si); + + soinfo* FindSoinfo(const char* name); + soinfo* GetCurrentSoinfo(); + +private: + bool ParseDynamic(soinfo* si); + void ApplyRelaSections(soinfo* si); + + std::unordered_map> soinfo_map_; +}; diff --git a/module/src/main/cpp/mylinker/include/utils.h b/module/src/main/cpp/mylinker/include/utils.h new file mode 100644 index 0000000..a81f027 --- /dev/null +++ b/module/src/main/cpp/mylinker/include/utils.h @@ -0,0 +1,21 @@ +#pragma once + +#include "common.h" + +namespace Utils { + bool safe_add(off64_t* out, off64_t a, size_t b); + + soinfo* get_soinfo(const char* so_name); + + void* getMapData(int fd, off64_t base_offset, size_t elf_offset, size_t size); + + ElfW(Addr) get_export_func(const char* path, const char* func_name); + + inline size_t page_start(size_t addr) { + return addr & ~(PAGE_SIZE - 1); + } + + inline size_t page_offset(size_t addr) { + return addr & (PAGE_SIZE - 1); + } +} \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/main.cpp b/module/src/main/cpp/mylinker/main.cpp new file mode 100644 index 0000000..0dde8b9 --- /dev/null +++ b/module/src/main/cpp/mylinker/main.cpp @@ -0,0 +1,49 @@ +#include "elf_loader.h" + +int (*yuuki_test_func) (int, int) = nullptr; + +int main(int argc, char* argv[]) { + + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + LOGI("Starting custom linker for: %s", argv[1]); + + // 检查文件是否存在 + if (access(argv[1], F_OK) != 0) { + LOGE("File does not exist: %s", argv[1]); + return 1; + } + + if (access(argv[1], R_OK) != 0) { + LOGE("File is not readable: %s", argv[1]); + return 1; + } + + ElfLoader loader; + if (loader.LoadLibrary(argv[1])) { + printf("Successfully loaded %s\n", argv[1]); + + void* test_func = loader.GetSymbol("yuuki_test"); + if (test_func) { + printf("Found yuuki_test function at %p\n", test_func); + yuuki_test_func = (int (*)(int, int)) test_func; + + // 测试函数调用 + printf("Testing function call: 1 + 1 = %d\n", yuuki_test_func(1, 1)); + printf("Testing function call: 5 + 3 = %d\n", yuuki_test_func(5, 3)); + } else { + printf("Failed to find yuuki_test function\n"); + } + + return 0; + } else { + printf("Failed to load %s\n", argv[1]); + return 1; + } +} +// logcat | grep "CustomLinker" +// logcat | grep "TEST_SO" +// ./data/local/tmp/elf_loader /storage/emulated/0/yuuki/test.so \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/memory_manager.cpp b/module/src/main/cpp/mylinker/memory_manager.cpp new file mode 100644 index 0000000..933ca3d --- /dev/null +++ b/module/src/main/cpp/mylinker/memory_manager.cpp @@ -0,0 +1,241 @@ +#include "memory_manager.h" + +MemoryManager::MemoryManager() : load_start_(nullptr), load_size_(0), + load_bias_(0), loaded_phdr_(nullptr) { +} + +MemoryManager::~MemoryManager() { +} + +bool MemoryManager::ReserveAddressSpace(const ElfW(Phdr)* phdr_table, size_t phdr_num) { + ElfW(Addr) min_vaddr; + load_size_ = phdr_table_get_load_size(phdr_table, phdr_num, &min_vaddr); + + if (load_size_ == 0) { + LOGE("No loadable segments"); + return false; + } + + LOGD("Load size: 0x%zx, min_vaddr: 0x%llx", load_size_, (unsigned long long)min_vaddr); + + void* start = mmap(nullptr, load_size_, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (start == MAP_FAILED) { + LOGE("Cannot reserve %zu bytes: %s", load_size_, strerror(errno)); + return false; + } + + load_start_ = start; + load_bias_ = reinterpret_cast(start) - min_vaddr; + + LOGD("Reserved address space at %p, bias: 0x%llx", start, (unsigned long long)load_bias_); + + return true; +} + +bool MemoryManager::LoadSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num, + void* mapped_file, size_t file_size) { + LOGD("Starting LoadSegments: phdr_num=%zu, file_size=%zu", phdr_num, file_size); + + for (size_t i = 0; i < phdr_num; ++i) { + const ElfW(Phdr)* phdr = &phdr_table[i]; + + if (phdr->p_type != PT_LOAD) { + continue; + } + + LOGD("Processing LOAD segment %zu: vaddr=0x%llx, memsz=0x%llx, filesz=0x%llx, offset=0x%llx", + i, (unsigned long long)phdr->p_vaddr, (unsigned long long)phdr->p_memsz, + (unsigned long long)phdr->p_filesz, (unsigned long long)phdr->p_offset); + + ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; + ElfW(Addr) seg_end = seg_start + phdr->p_memsz; + + ElfW(Addr) seg_page_start = PAGE_START(seg_start); + ElfW(Addr) seg_page_end = PAGE_END(seg_end); + + ElfW(Addr) seg_file_end = seg_start + phdr->p_filesz; + + ElfW(Addr) file_start = phdr->p_offset; + ElfW(Addr) file_end = file_start + phdr->p_filesz; + + ElfW(Addr) file_page_start = PAGE_START(file_start); + + if (file_end > file_size) { + LOGE("Invalid file size: file_end=0x%llx > file_size=0x%zx", (unsigned long long)file_end, file_size); + return false; + } + + if (phdr->p_filesz > 0) { + void* seg_addr = reinterpret_cast(seg_page_start); + size_t seg_size = seg_page_end - seg_page_start; + + if (mprotect(seg_addr, seg_size, PROT_READ | PROT_WRITE) < 0) { + LOGE("Cannot mprotect for loading: %s", strerror(errno)); + return false; + } + + void* src = static_cast(mapped_file) + phdr->p_offset; + void* dst = reinterpret_cast(seg_start); + + LOGD("Copying segment %zu: src=%p (offset=0x%llx), dst=%p, size=0x%llx", + i, src, (unsigned long long)phdr->p_offset, dst, (unsigned long long)phdr->p_filesz); + + if (static_cast(src) + phdr->p_filesz > static_cast(mapped_file) + file_size) { + LOGE("Source copy would exceed file bounds"); + return false; + } + + if (reinterpret_cast(dst) + phdr->p_filesz > seg_page_end) { + LOGE("Destination copy would exceed segment bounds"); + return false; + } + + memcpy(dst, src, phdr->p_filesz); + + LOGD("Successfully copied segment %zu", i); + } + + if (phdr->p_memsz > phdr->p_filesz) { + ElfW(Addr) bss_start = seg_start + phdr->p_filesz; + ElfW(Addr) bss_end = seg_start + phdr->p_memsz; + size_t bss_size = bss_end - bss_start; + + LOGD("Zeroing BSS: start=0x%llx, size=0x%zx", (unsigned long long)bss_start, bss_size); + memset(reinterpret_cast(bss_start), 0, bss_size); + } + + ElfW(Addr) aligned_file_end = PAGE_END(seg_file_end); + if (seg_page_end > aligned_file_end) { + size_t zeromap_size = seg_page_end - aligned_file_end; + void* zeromap = mmap(reinterpret_cast(aligned_file_end), + zeromap_size, + PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, + -1, 0); + if (zeromap == MAP_FAILED) { + LOGE("Cannot zero fill gap: %s", strerror(errno)); + return false; + } + LOGD("Zero-filled gap: addr=%p, size=0x%zx", zeromap, zeromap_size); + } + } + + LOGD("LoadSegments complete"); + return true; +} + +bool MemoryManager::FindPhdr(const ElfW(Phdr)* phdr_table, size_t phdr_num) { + const ElfW(Phdr)* phdr_limit = phdr_table + phdr_num; + + for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_limit; ++phdr) { + if (phdr->p_type == PT_PHDR) { + return CheckPhdr(load_bias_ + phdr->p_vaddr, phdr_table, phdr_num); + } + } + + for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_limit; ++phdr) { + if (phdr->p_type == PT_LOAD) { + if (phdr->p_offset == 0) { + ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr; + const ElfW(Ehdr)* ehdr = reinterpret_cast(elf_addr); + ElfW(Addr) offset = ehdr->e_phoff; + return CheckPhdr(reinterpret_cast(ehdr) + offset, phdr_table, phdr_num); + } + break; + } + } + + LOGD("Using original phdr_table as loaded_phdr"); + loaded_phdr_ = phdr_table; + return true; +} + +bool MemoryManager::ProtectSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num) { + for (size_t i = 0; i < phdr_num; ++i) { + const ElfW(Phdr)* phdr = &phdr_table[i]; + + if (phdr->p_type != PT_LOAD) { + continue; + } + + ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; + ElfW(Addr) seg_page_start = PAGE_START(seg_start); + ElfW(Addr) seg_page_end = PAGE_END(seg_start + phdr->p_memsz); + + int prot = PFLAGS_TO_PROT(phdr->p_flags); + + if (mprotect(reinterpret_cast(seg_page_start), + seg_page_end - seg_page_start, prot) < 0) { + LOGE("Cannot protect segment %zu: %s", i, strerror(errno)); + return false; + } + + LOGD("Protected segment %zu: 0x%llx-0x%llx, prot: %d", + i, (unsigned long long)seg_page_start, (unsigned long long)seg_page_end, prot); + } + + return true; +} + +bool MemoryManager::CheckPhdr(ElfW(Addr) loaded, const ElfW(Phdr)* phdr_table, size_t phdr_num) { + const ElfW(Phdr)* phdr_limit = phdr_table + phdr_num; + ElfW(Addr) loaded_end = loaded + (phdr_num * sizeof(ElfW(Phdr))); + + for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_limit; ++phdr) { + if (phdr->p_type != PT_LOAD) { + continue; + } + + ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; + ElfW(Addr) seg_end = phdr->p_filesz + seg_start; + + if (seg_start <= loaded && loaded_end <= seg_end) { + loaded_phdr_ = reinterpret_cast(loaded); + return true; + } + } + + LOGE("Loaded phdr %p not in loadable segment", reinterpret_cast(loaded)); + return false; +} + +size_t MemoryManager::phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, + size_t phdr_count, + ElfW(Addr)* min_vaddr) { + ElfW(Addr) min_addr = UINTPTR_MAX; + ElfW(Addr) max_addr = 0; + + bool found_pt_load = false; + + for (size_t i = 0; i < phdr_count; ++i) { + const ElfW(Phdr)* phdr = &phdr_table[i]; + + if (phdr->p_type != PT_LOAD) { + continue; + } + + found_pt_load = true; + + if (phdr->p_vaddr < min_addr) { + min_addr = phdr->p_vaddr; + } + + if (phdr->p_vaddr + phdr->p_memsz > max_addr) { + max_addr = phdr->p_vaddr + phdr->p_memsz; + } + } + + if (!found_pt_load) { + return 0; + } + + min_addr = PAGE_START(min_addr); + max_addr = PAGE_END(max_addr); + + if (min_vaddr != nullptr) { + *min_vaddr = min_addr; + } + + return max_addr - min_addr; +} \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/mylinker.cpp b/module/src/main/cpp/mylinker/mylinker.cpp new file mode 100644 index 0000000..4e20894 --- /dev/null +++ b/module/src/main/cpp/mylinker/mylinker.cpp @@ -0,0 +1,61 @@ +#include "mylinker.h" +#include "elf_loader.h" +#include "common.h" +#include +#include +#include + +static std::unordered_map> loaded_libraries; + +bool mylinker_load_library(const char* library_path, JavaVM* vm) { + if (!library_path) { + LOGE("Invalid library path"); + return false; + } + + std::string path(library_path); + + if (loaded_libraries.find(path) != loaded_libraries.end()) { + LOGI("Library already loaded: %s", library_path); + return true; + } + + auto loader = std::make_unique(); + if (!loader->LoadLibrary(library_path)) { + LOGE("Failed to load library: %s", library_path); + return false; + } + + JNIEnv* env = nullptr; + if (vm && vm->GetEnv((void**)&env, JNI_VERSION_1_6) == JNI_OK) { + typedef jint (*JNI_OnLoad_t)(JavaVM*, void*); + auto jni_onload = reinterpret_cast(loader->GetSymbol("JNI_OnLoad")); + if (jni_onload) { + LOGI("Calling JNI_OnLoad"); + jni_onload(vm, nullptr); + } + } + + loaded_libraries[path] = std::move(loader); + LOGI("Successfully loaded library: %s", library_path); + return true; +} + +void* mylinker_get_symbol(const char* library_path, const char* symbol_name) { + if (!library_path || !symbol_name) { + return nullptr; + } + + auto it = loaded_libraries.find(library_path); + if (it == loaded_libraries.end()) { + LOGE("Library not loaded: %s", library_path); + return nullptr; + } + + return it->second->GetSymbol(symbol_name); +} + +void mylinker_cleanup() { + loaded_libraries.clear(); + LOGI("Cleaned up all loaded libraries"); +} \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/relocator.cpp b/module/src/main/cpp/mylinker/relocator.cpp new file mode 100644 index 0000000..7735367 --- /dev/null +++ b/module/src/main/cpp/mylinker/relocator.cpp @@ -0,0 +1,342 @@ +#include "relocator.h" + +// Only define if not already defined +#ifndef R_AARCH64_NONE +#define R_AARCH64_NONE 0 +#endif +#ifndef R_AARCH64_ABS64 +#define R_AARCH64_ABS64 257 +#endif +#ifndef R_AARCH64_GLOB_DAT +#define R_AARCH64_GLOB_DAT 1025 +#endif +#ifndef R_AARCH64_JUMP_SLOT +#define R_AARCH64_JUMP_SLOT 1026 +#endif +#ifndef R_AARCH64_RELATIVE +#define R_AARCH64_RELATIVE 1027 +#endif +#ifndef R_AARCH64_TLS_TPREL64 +#define R_AARCH64_TLS_TPREL64 1030 +#endif +#ifndef R_AARCH64_TLS_DTPREL32 +#define R_AARCH64_TLS_DTPREL32 1031 +#endif +#ifndef R_AARCH64_IRELATIVE +#define R_AARCH64_IRELATIVE 1032 +#endif + +Relocator::Relocator() { +} + +Relocator::~Relocator() { +} + +bool Relocator::RelocateImage(soinfo* si) { + LOGD("Starting relocation for %s", si->name); + + if (!si) { + LOGE("soinfo is null"); + return false; + } + + if (si->rela != nullptr && si->rela_count > 0) { + LOGD("Processing %zu RELA relocations", si->rela_count); + + if (si->rela_count > 100000) { + LOGE("RELA count too large: %zu", si->rela_count); + return false; + } + + for (size_t i = 0; i < si->rela_count; ++i) { + if (!ProcessRelaRelocation(si, &si->rela[i])) { + LOGE("Failed to process RELA relocation %zu", i); + // 继续处理其他重定位,不要因为一个失败就退出 + // return false; + } + } + } else { + LOGD("No RELA relocations to process"); + } + + if (si->plt_rela != nullptr && si->plt_rela_count > 0) { + LOGD("Processing %zu PLT RELA relocations", si->plt_rela_count); + + if (si->plt_rela_count > 10000) { + LOGE("PLT RELA count too large: %zu", si->plt_rela_count); + return false; + } + + for (size_t i = 0; i < si->plt_rela_count; ++i) { + if (!ProcessRelaRelocation(si, &si->plt_rela[i])) { + LOGE("Failed to process PLT RELA relocation %zu", i); + // 继续处理其他重定位 + // return false; + } + } + } else { + LOGD("No PLT RELA relocations to process"); + } + + LOGD("Relocation complete for %s", si->name); + return true; +} + +bool Relocator::LinkImage(soinfo* si) { + if (!si) { + LOGE("soinfo is null in LinkImage"); + return false; + } + + if (!RelocateImage(si)) { + LOGE("Failed to relocate image"); + return false; + } + + if (si->init_func != nullptr) { + LOGD("Calling init function at %p", si->init_func); + si->init_func(); + } + + if (si->init_array != nullptr && si->init_array_count > 0) { + LOGD("Calling %zu init_array functions", si->init_array_count); + + if (si->init_array_count > 1000) { + LOGE("init_array_count too large: %zu", si->init_array_count); + return false; + } + + for (size_t i = 0; i < si->init_array_count; ++i) { + void (*func)() = si->init_array[i]; + if (func != nullptr) { + LOGD("Calling init_array[%zu] at %p", i, func); + func(); + } + } + } + + return true; +} + +bool Relocator::ProcessRelaRelocation(soinfo* si, const ElfW(Rela)* rela) { + if (!si || !rela) { + LOGE("Invalid parameters in ProcessRelaRelocation"); + return false; + } + + ElfW(Addr) reloc = static_cast(rela->r_offset + si->load_bias); + ElfW(Word) type = ELFW(R_TYPE)(rela->r_info); + ElfW(Word) sym = ELFW(R_SYM)(rela->r_info); + + LOGD("Processing relocation: offset=0x%llx, type=%d, sym=%d, addend=0x%llx", + (unsigned long long)rela->r_offset, type, sym, (long long)rela->r_addend); + + if (reloc < si->base || reloc >= si->base + si->size) { + LOGE("Relocation address 0x%llx out of range [0x%llx, 0x%llx)", + (unsigned long long)reloc, (unsigned long long)si->base, (unsigned long long)(si->base + si->size)); + return false; + } + + ElfW(Addr) sym_addr = 0; + const char* sym_name = nullptr; + + if (sym != 0) { + if (!si->symtab) { + LOGE("Symbol table is null"); + return false; + } + + const ElfW(Sym)* s = &si->symtab[sym]; + + if (si->strtab && s->st_name != 0) { + sym_name = si->strtab + s->st_name; + LOGD("Symbol name: %s", sym_name); + } + + if (s->st_shndx != SHN_UNDEF) { + sym_addr = s->st_value + si->load_bias; + LOGD("Local symbol: addr=0x%llx", (unsigned long long)sym_addr); + } else if (sym_name) { + sym_addr = FindSymbolAddress(sym_name, si); + if (sym_addr == 0) { + LOGD("Cannot find symbol: %s (may be optional)", sym_name); + } + } + } + + void* page_start = reinterpret_cast(PAGE_START(reloc)); + size_t page_size = PAGE_SIZE; + + int old_prot = PROT_READ | PROT_WRITE; + if (mprotect(page_start, page_size, old_prot) != 0) { + LOGD("mprotect failed for relocation, trying anyway: %s", strerror(errno)); + } + + switch (type) { + case R_AARCH64_NONE: + LOGD("R_AARCH64_NONE"); + break; + + case R_AARCH64_ABS64: + LOGD("R_AARCH64_ABS64: writing 0x%llx to 0x%llx", + (unsigned long long)(sym_addr + rela->r_addend), (unsigned long long)reloc); + *reinterpret_cast(reloc) = sym_addr + rela->r_addend; + break; + + case R_AARCH64_GLOB_DAT: + LOGD("R_AARCH64_GLOB_DAT: writing 0x%llx to 0x%llx", + (unsigned long long)(sym_addr + rela->r_addend), (unsigned long long)reloc); + *reinterpret_cast(reloc) = sym_addr + rela->r_addend; + break; + + case R_AARCH64_JUMP_SLOT: + LOGD("R_AARCH64_JUMP_SLOT: writing 0x%llx to 0x%llx", + (unsigned long long)(sym_addr + rela->r_addend), (unsigned long long)reloc); + *reinterpret_cast(reloc) = sym_addr + rela->r_addend; + break; + + case R_AARCH64_RELATIVE: + LOGD("R_AARCH64_RELATIVE: writing 0x%llx to 0x%llx", + (unsigned long long)(si->load_bias + rela->r_addend), (unsigned long long)reloc); + *reinterpret_cast(reloc) = si->load_bias + rela->r_addend; + break; + + case R_AARCH64_IRELATIVE: + { + ElfW(Addr) resolver = si->load_bias + rela->r_addend; + LOGD("R_AARCH64_IRELATIVE: resolver at 0x%llx", (unsigned long long)resolver); + + if (resolver < si->base || resolver >= si->base + si->size) { + LOGE("Invalid resolver address: 0x%llx", (unsigned long long)resolver); + return false; + } + + ElfW(Addr) resolved = ((ElfW(Addr) (*)())resolver)(); + *reinterpret_cast(reloc) = resolved; + LOGD("R_AARCH64_IRELATIVE: resolved to 0x%llx", (unsigned long long)resolved); + } + break; + + default: + LOGD("Unknown relocation type %d, skipping", type); + break; + } + + return true; +} + +ElfW(Addr) Relocator::FindSymbolAddress(const char* name, soinfo* si) { + if (!name || !si) { + return 0; + } + + if (si->symtab != nullptr) { + if (si->gnu_bucket != nullptr) { + uint32_t hash = gnu_hash(name); + ElfW(Sym)* sym = gnu_lookup(hash, name, si); + if (sym != nullptr && sym->st_shndx != SHN_UNDEF) { + ElfW(Addr) addr = sym->st_value + si->load_bias; + LOGD("Found symbol %s in current SO at 0x%llx", name, (unsigned long long)addr); + return addr; + } + } + + if (si->bucket != nullptr) { + unsigned hash = elf_hash(name); + ElfW(Sym)* sym = elf_lookup(hash, name, si); + if (sym != nullptr && sym->st_shndx != SHN_UNDEF) { + ElfW(Addr) addr = sym->st_value + si->load_bias; + LOGD("Found symbol %s in current SO at 0x%llx", name, (unsigned long long)addr); + return addr; + } + } + } + + for (const auto& lib : si->needed_libs) { + void* handle = dlopen(lib.c_str(), RTLD_NOW | RTLD_NOLOAD); + if (handle != nullptr) { + void* addr = dlsym(handle, name); + if (addr != nullptr) { + LOGD("Found symbol %s in %s at %p", name, lib.c_str(), addr); + dlclose(handle); + return reinterpret_cast(addr); + } + dlclose(handle); + } + } + + void* addr = dlsym(RTLD_DEFAULT, name); + if (addr != nullptr) { + LOGD("Found symbol %s globally at %p", name, addr); + return reinterpret_cast(addr); + } + + LOGD("Symbol %s not found", name); + return 0; +} + +ElfW(Sym)* Relocator::gnu_lookup(uint32_t hash, const char* name, soinfo* si) { + if (!si->gnu_bucket || !si->gnu_chain || !si->symtab || !si->strtab) { + return nullptr; + } + + uint32_t h2 = hash >> si->gnu_shift2; + + uint32_t bloom_mask_bits = sizeof(ElfW(Addr)) * 8; + uint32_t word_num = (hash / bloom_mask_bits) & si->gnu_maskwords; + ElfW(Addr) bloom_word = si->gnu_bloom_filter[word_num]; + + if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & + (bloom_word >> (h2 % bloom_mask_bits))) == 0) { + return nullptr; + } + + uint32_t n = si->gnu_bucket[hash % si->gnu_nbucket]; + + if (n == 0) { + return nullptr; + } + + do { + ElfW(Sym)* s = si->symtab + n; + if (((si->gnu_chain[n] ^ hash) >> 1) == 0 && + strcmp(si->strtab + s->st_name, name) == 0) { + return s; + } + } while ((si->gnu_chain[n++] & 1) == 0); + + return nullptr; +} + +ElfW(Sym)* Relocator::elf_lookup(unsigned hash, const char* name, soinfo* si) { + if (!si->bucket || !si->chain || !si->symtab || !si->strtab) { + return nullptr; + } + + for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) { + ElfW(Sym)* s = si->symtab + n; + if (s->st_name != 0 && strcmp(si->strtab + s->st_name, name) == 0) { + return s; + } + } + return nullptr; +} + +uint32_t Relocator::gnu_hash(const char* name) { + uint32_t h = 5381; + for (const uint8_t* c = reinterpret_cast(name); *c != '\0'; c++) { + h += (h << 5) + *c; + } + return h; +} + +unsigned Relocator::elf_hash(const char* name) { + unsigned h = 0, g; + for (const unsigned char* p = reinterpret_cast(name); *p; p++) { + h = (h << 4) + *p; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + return h; +} \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/soinfo_manager.cpp b/module/src/main/cpp/mylinker/soinfo_manager.cpp new file mode 100644 index 0000000..ca8a8a7 --- /dev/null +++ b/module/src/main/cpp/mylinker/soinfo_manager.cpp @@ -0,0 +1,312 @@ +#include "soinfo_manager.h" +#include "utils.h" +#include "memory_manager.h" +#include "elf_reader.h" + +SoinfoManager::SoinfoManager() { +} + +SoinfoManager::~SoinfoManager() { +} + +soinfo* SoinfoManager::GetOrCreateSoinfo(const char* name) { + auto it = soinfo_map_.find(name); + if (it != soinfo_map_.end()) { + return it->second.get(); + } + + auto si = std::make_unique(); + memset(si.get(), 0, sizeof(soinfo)); + si->name = strdup(name); + + soinfo* result = si.get(); + soinfo_map_[name] = std::move(si); + + return result; +} + +bool SoinfoManager::UpdateSoinfo(soinfo* si, MemoryManager* mm, ElfReader* reader) { + if (!si || !mm || !reader) { + return false; + } + + si->base = reinterpret_cast(mm->GetLoadStart()); + si->size = mm->GetLoadSize(); + si->load_bias = mm->GetLoadBias(); + + const ElfW(Phdr)* loaded_phdr = mm->GetLoadedPhdr(); + if (loaded_phdr != nullptr) { + si->phdr = loaded_phdr; + } else { + si->phdr = reader->GetProgramHeaders(); + LOGD("Using original program headers"); + } + + si->phnum = reader->GetProgramHeaderCount(); + + const ElfW(Ehdr)* header = reader->GetHeader(); + si->entry = si->load_bias + header->e_entry; + + LOGD("Updated soinfo: base=0x%llx, size=0x%zx, bias=0x%llx, entry=0x%llx, phdr=%p", + (unsigned long long)si->base, si->size, (unsigned long long)si->load_bias, + (unsigned long long)si->entry, si->phdr); + + return true; +} + +bool SoinfoManager::PrelinkImage(soinfo* si) { + LOGD("Starting PrelinkImage for %s", si->name); + + if (!ParseDynamic(si)) { + LOGE("Failed to parse dynamic section"); + return false; + } + + if (si->strtab != nullptr && si->dynamic != nullptr) { + for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) { + if (d->d_tag == DT_NEEDED && si->needed_libs.empty()) { + const char* needed = si->strtab + d->d_un.d_val; + si->needed_libs.push_back(needed); + LOGD("Processing deferred DT_NEEDED: %s", needed); + } + } + } + + ApplyRelaSections(si); + + LOGD("PrelinkImage complete for %s", si->name); + return true; +} + +soinfo* SoinfoManager::FindSoinfo(const char* name) { + auto it = soinfo_map_.find(name); + if (it != soinfo_map_.end()) { + return it->second.get(); + } + return nullptr; +} + +soinfo* SoinfoManager::GetCurrentSoinfo() { + return Utils::get_soinfo("libcustom_linker.so"); +} + +bool SoinfoManager::ParseDynamic(soinfo* si) { + if (!si || !si->phdr) { + LOGE("Invalid soinfo or phdr is null"); + return false; + } + + LOGD("Starting ParseDynamic: phdr=%p, phnum=%zu", si->phdr, si->phnum); + + const ElfW(Phdr)* phdr_limit = si->phdr + si->phnum; + bool found_dynamic = false; + + for (const ElfW(Phdr)* phdr = si->phdr; phdr < phdr_limit; ++phdr) { + if (phdr->p_type == PT_DYNAMIC) { + si->dynamic = reinterpret_cast(si->load_bias + phdr->p_vaddr); + si->dynamic_count = phdr->p_memsz / sizeof(ElfW(Dyn)); + found_dynamic = true; + LOGD("Found PT_DYNAMIC at vaddr=0x%llx, memsz=0x%llx", + (unsigned long long)phdr->p_vaddr, (unsigned long long)phdr->p_memsz); + break; + } + } + + if (!found_dynamic || !si->dynamic) { + LOGE("No PT_DYNAMIC segment found"); + return false; + } + + LOGD("Dynamic section at %p, count=%zu", si->dynamic, si->dynamic_count); + + if (si->dynamic_count == 0 || si->dynamic_count > 1000) { + LOGE("Invalid dynamic count: %zu", si->dynamic_count); + return false; + } + + size_t dyn_count = 0; + for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL && dyn_count < si->dynamic_count; ++d, ++dyn_count) { + + LOGD("Processing dynamic entry %zu: tag=0x%llx, val/ptr=0x%llx", + dyn_count, (unsigned long long)d->d_tag, (unsigned long long)d->d_un.d_val); + + switch (d->d_tag) { + case DT_SYMTAB: + si->symtab = reinterpret_cast(si->load_bias + d->d_un.d_ptr); + LOGD("DT_SYMTAB: raw_ptr=0x%llx, final_addr=%p", + (unsigned long long)d->d_un.d_ptr, si->symtab); + break; + + case DT_STRTAB: + si->strtab = reinterpret_cast(si->load_bias + d->d_un.d_ptr); + LOGD("DT_STRTAB: raw_ptr=0x%llx, final_addr=%p", + (unsigned long long)d->d_un.d_ptr, si->strtab); + break; + + case DT_STRSZ: + LOGD("DT_STRSZ: %lu", (unsigned long)d->d_un.d_val); + break; + + case DT_HASH: { + uint32_t* hash = reinterpret_cast(si->load_bias + d->d_un.d_ptr); + si->nbucket = hash[0]; + si->nchain = hash[1]; + si->bucket = hash + 2; + si->chain = si->bucket + si->nbucket; + LOGD("DT_HASH: raw_ptr=0x%llx, nbucket=%zu, nchain=%zu", + (unsigned long long)d->d_un.d_ptr, si->nbucket, si->nchain); + break; + } + + case DT_GNU_HASH: { + uint32_t* hash = reinterpret_cast(si->load_bias + d->d_un.d_ptr); + si->gnu_nbucket = hash[0]; + uint32_t symbias = hash[1]; + si->gnu_maskwords = hash[2]; + si->gnu_shift2 = hash[3]; + si->gnu_bloom_filter = reinterpret_cast(hash + 4); + si->gnu_bucket = reinterpret_cast(si->gnu_bloom_filter + si->gnu_maskwords); + si->gnu_chain = si->gnu_bucket + si->gnu_nbucket - symbias; + LOGD("DT_GNU_HASH: raw_ptr=0x%llx, nbucket=%zu, symbias=%u", + (unsigned long long)d->d_un.d_ptr, si->gnu_nbucket, symbias); + break; + } + + case DT_JMPREL: + si->plt_rela = reinterpret_cast(si->load_bias + d->d_un.d_ptr); + LOGD("DT_JMPREL: raw_ptr=0x%llx, final_addr=%p", + (unsigned long long)d->d_un.d_ptr, si->plt_rela); + break; + + case DT_PLTRELSZ: + si->plt_rela_count = d->d_un.d_val / sizeof(ElfW(Rela)); + LOGD("DT_PLTRELSZ: raw_val=%lu, count=%zu", + (unsigned long)d->d_un.d_val, si->plt_rela_count); + break; + + case DT_PLTREL: + LOGD("DT_PLTREL: %lu", (unsigned long)d->d_un.d_val); + break; + + case DT_RELA: + si->rela = reinterpret_cast(si->load_bias + d->d_un.d_ptr); + LOGD("DT_RELA: raw_ptr=0x%llx, final_addr=%p", + (unsigned long long)d->d_un.d_ptr, si->rela); + break; + + case DT_RELASZ: + si->rela_count = d->d_un.d_val / sizeof(ElfW(Rela)); + LOGD("DT_RELASZ: raw_val=%lu, count=%zu", + (unsigned long)d->d_un.d_val, si->rela_count); + break; + + case DT_RELAENT: + LOGD("DT_RELAENT: %lu", (unsigned long)d->d_un.d_val); + break; + + case DT_INIT: + si->init_func = reinterpret_cast(si->load_bias + d->d_un.d_ptr); + LOGD("DT_INIT: raw_ptr=0x%llx, final_addr=%p", + (unsigned long long)d->d_un.d_ptr, si->init_func); + break; + + case DT_INIT_ARRAY: + si->init_array = reinterpret_cast(si->load_bias + d->d_un.d_ptr); + LOGD("DT_INIT_ARRAY: raw_ptr=0x%llx, final_addr=%p", + (unsigned long long)d->d_un.d_ptr, si->init_array); + break; + + case DT_INIT_ARRAYSZ: + si->init_array_count = d->d_un.d_val / sizeof(void*); + LOGD("DT_INIT_ARRAYSZ: raw_val=%lu, count=%zu", + (unsigned long)d->d_un.d_val, si->init_array_count); + break; + + case DT_FINI: + LOGD("DT_FINI: 0x%llx", (unsigned long long)d->d_un.d_ptr); + break; + + case DT_FINI_ARRAY: + si->fini_array = reinterpret_cast(si->load_bias + d->d_un.d_ptr); + LOGD("DT_FINI_ARRAY: raw_ptr=0x%llx, final_addr=%p", + (unsigned long long)d->d_un.d_ptr, si->fini_array); + break; + + case DT_FINI_ARRAYSZ: + si->fini_array_count = d->d_un.d_val / sizeof(void*); + LOGD("DT_FINI_ARRAYSZ: raw_val=%lu, count=%zu", + (unsigned long)d->d_un.d_val, si->fini_array_count); + break; + + case DT_FLAGS: + si->flags = d->d_un.d_val; + LOGD("DT_FLAGS: 0x%x", si->flags); + break; + + case DT_FLAGS_1: + LOGD("DT_FLAGS_1: 0x%llx", (unsigned long long)d->d_un.d_val); + break; + + case DT_SONAME: + LOGD("DT_SONAME: offset=%lu", (unsigned long)d->d_un.d_val); + break; + + case DT_RUNPATH: + LOGD("DT_RUNPATH: offset=%lu", (unsigned long)d->d_un.d_val); + break; + + case DT_NEEDED: + // 跳过,稍后处理 + LOGD("DT_NEEDED: offset=%lu (deferred)", (unsigned long)d->d_un.d_val); + break; + + default: + LOGD("Unknown dynamic tag: 0x%llx, value=0x%llx", + (unsigned long long)d->d_tag, (unsigned long long)d->d_un.d_val); + break; + } + + // 添加安全检查,防止无限循环 + if (dyn_count > si->dynamic_count) { + LOGE("Dynamic parsing exceeded expected count"); + break; + } + } + + if (si->symtab == nullptr) { + LOGD("Warning: DT_SYMTAB not found or is null"); + } + + if (si->strtab == nullptr) { + LOGD("Warning: DT_STRTAB not found or is null"); + } + + if (si->strtab != nullptr) { + dyn_count = 0; + for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL && dyn_count < si->dynamic_count; ++d, ++dyn_count) { + if (d->d_tag == DT_NEEDED) { + if (d->d_un.d_val < 65536) { + const char* needed = si->strtab + d->d_un.d_val; + if (strlen(needed) > 0 && strlen(needed) < 256) { + si->needed_libs.push_back(needed); + LOGD("DT_NEEDED: %s", needed); + } else { + LOGD("DT_NEEDED: invalid string at offset %lu", (unsigned long)d->d_un.d_val); + } + } else { + LOGD("DT_NEEDED: offset too large: %lu", (unsigned long)d->d_un.d_val); + } + } + } + } + + LOGD("Dynamic parsing complete: symtab=%p, strtab=%p, needed_libs=%zu", + si->symtab, si->strtab, si->needed_libs.size()); + + return true; +} + +void SoinfoManager::ApplyRelaSections(soinfo* si) { + LOGD("RELA sections: rela_count=%zu, plt_rela_count=%zu", + si->rela_count, si->plt_rela_count); +} \ No newline at end of file diff --git a/module/src/main/cpp/mylinker/utils.cpp b/module/src/main/cpp/mylinker/utils.cpp new file mode 100644 index 0000000..169533f --- /dev/null +++ b/module/src/main/cpp/mylinker/utils.cpp @@ -0,0 +1,157 @@ +#include "utils.h" + +namespace Utils { + + bool safe_add(off64_t* out, off64_t a, size_t b) { + if (a < 0 || __builtin_add_overflow(a, b, out)) { + return false; + } + return true; + } + + soinfo* get_soinfo(const char* so_name) { + typedef soinfo* (*FunctionPtr)(ElfW(Addr)); + + char line[1024]; + ElfW(Addr) linker_base = 0; + ElfW(Addr) so_addr = 0; + + FILE* fp = fopen("/proc/self/maps", "r"); + if (!fp) { + LOGE("Cannot open /proc/self/maps"); + return nullptr; + } + + while (fgets(line, sizeof(line), fp)) { + if (strstr(line, "linker64") && !linker_base) { + char* addr = strtok(line, "-"); + linker_base = strtoull(addr, nullptr, 16); + } else if (strstr(line, so_name) && !so_addr) { + char* addr = strtok(line, "-"); + so_addr = strtoull(addr, nullptr, 16); + } + + if (linker_base && so_addr) { + break; + } + } + + fclose(fp); + + if (!linker_base || !so_addr) { + LOGE("Cannot find addresses"); + return nullptr; + } + + ElfW(Addr) func_offset = get_export_func("/system/bin/linker64", "find_containing_library"); + if (!func_offset) { + LOGE("Cannot find find_containing_library"); + return nullptr; + } + + ElfW(Addr) find_containing_library_addr = linker_base + func_offset; + FunctionPtr find_containing_library = reinterpret_cast(find_containing_library_addr); + + return find_containing_library(so_addr); + } + + void* getMapData(int fd, off64_t base_offset, size_t elf_offset, size_t size) { + off64_t offset; + if (!safe_add(&offset, base_offset, elf_offset)) { + return nullptr; + } + + off64_t page_min = page_start(offset); + off64_t end_offset; + if (!safe_add(&end_offset, offset, size)) { + return nullptr; + } + if (!safe_add(&end_offset, end_offset, page_offset(offset))) { + return nullptr; + } + + size_t map_size = static_cast(end_offset - page_min); + + uint8_t* map_start = static_cast( + mmap(nullptr, map_size, PROT_READ, MAP_PRIVATE, fd, page_min)); + + if (map_start == MAP_FAILED) { + return nullptr; + } + + return map_start + page_offset(offset); + } + + ElfW(Addr) get_export_func(const char* path, const char* func_name) { + struct stat sb; + int fd = open(path, O_RDONLY); + if (fd < 0) { + return 0; + } + + if (fstat(fd, &sb) < 0) { + close(fd); + return 0; + } + + void* base = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (base == MAP_FAILED) { + close(fd); + return 0; + } + + ElfW(Ehdr) header; + memcpy(&header, base, sizeof(header)); + + size_t shdr_size = header.e_shnum * sizeof(ElfW(Shdr)); + ElfW(Shdr)* shdr_table = static_cast(malloc(shdr_size)); + memcpy(shdr_table, static_cast(base) + header.e_shoff, shdr_size); + + char* shstrtab = static_cast(base) + shdr_table[header.e_shstrndx].sh_offset; + + void* symtab = nullptr; + char* strtab = nullptr; + uint32_t symtab_size = 0; + + for (size_t i = 0; i < header.e_shnum; ++i) { + const ElfW(Shdr)* shdr = &shdr_table[i]; + char* section_name = shstrtab + shdr->sh_name; + + if (strcmp(section_name, ".symtab") == 0) { + symtab = static_cast(base) + shdr->sh_offset; + symtab_size = shdr->sh_size; + } + if (strcmp(section_name, ".strtab") == 0) { + strtab = static_cast(base) + shdr->sh_offset; + } + + if (strtab && symtab) { + break; + } + } + + ElfW(Addr) result = 0; + + if (symtab && strtab) { + ElfW(Sym)* sym_table = static_cast(symtab); + int sym_num = symtab_size / sizeof(ElfW(Sym)); + + for (int i = 0; i < sym_num; i++) { + const ElfW(Sym)* sym = &sym_table[i]; + char* sym_name = strtab + sym->st_name; + + if (strstr(sym_name, func_name)) { + result = sym->st_value; + break; + } + } + } + + free(shdr_table); + munmap(base, sb.st_size); + close(fd); + + return result; + } + +} \ No newline at end of file