diff --git a/anticheat/libtersafe.idb b/anticheat/libtersafe.idb index f42df06af..c41993f0a 100644 Binary files a/anticheat/libtersafe.idb and b/anticheat/libtersafe.idb differ diff --git a/ligma-cheat/ligma-cheat/bypass/bypass.cpp b/ligma-cheat/ligma-cheat/bypass/bypass.cpp index dc375a23e..ad840252e 100644 --- a/ligma-cheat/ligma-cheat/bypass/bypass.cpp +++ b/ligma-cheat/ligma-cheat/bypass/bypass.cpp @@ -8,10 +8,10 @@ namespace ligma // do not call anything that will call dlopen inside of callback.... // __attribute__((noinline)) - void init(const std::function& callback) + void init(const std::function& callback) { ligma::utils::on_image_load("libil2cpp.so", callback); - ligma::utils::on_image_load("libxlua.so", [&](const std::uintptr_t module_base) + ligma::utils::on_image_load("libxlua.so", [&](std::uintptr_t module_base, void* module_handle) -> bool { LOGI("libxlua.so base = %p", module_base); ligma::hook::make_hook( @@ -19,17 +19,18 @@ namespace ligma reinterpret_cast(&load_bufferx_hook) ); LOGI("installed libxlua.so hooks!"); + return false; }); - ligma::utils::on_image_load("libtersafe.so", [&](const std::uintptr_t module_base) + // TODO: add more hooks on libtersafe.so with dlsym_hook... + ligma::utils::on_image_load("libtersafe.so", [&](std::uintptr_t module_base, void* module_handle) -> bool { LOGI("libtersafe.so = %p", module_base); - LOGI("libtersafe.so handle = %p", dlopen("libtersafe.so", RTLD_NOLOAD)); + return false; }); fopen_ptr = dlsym(dlopen("libc.so", RTLD_NOLOAD), "fopen"); system_prop_get = dlsym(dlopen("libc.so", RTLD_NOLOAD), "__system_property_get"); - ligma::hook::make_hook(fopen_ptr, reinterpret_cast(&fopen_hook)); ligma::hook::make_hook(system_prop_get, reinterpret_cast(&system_property_hook)); } diff --git a/ligma-cheat/ligma-cheat/bypass/bypass.h b/ligma-cheat/ligma-cheat/bypass/bypass.h index 7f9524935..1c0421eb3 100644 --- a/ligma-cheat/ligma-cheat/bypass/bypass.h +++ b/ligma-cheat/ligma-cheat/bypass/bypass.h @@ -18,7 +18,7 @@ namespace ligma inline std::mutex fopen_mutex; inline std::mutex system_prop_mutex; - void init(const std::function& callback); + void init(const std::function& callback); FILE* fopen_hook(const char* path, const char* mode); int system_property_hook(const char* name, char* value); int load_bufferx_hook(void* L, const char* buff, size_t sz, const char* name, const char* mode); diff --git a/ligma-cheat/ligma-cheat/hooks/dlsym_hook.cpp b/ligma-cheat/ligma-cheat/hooks/dlsym_hook.cpp new file mode 100644 index 000000000..d93be2622 --- /dev/null +++ b/ligma-cheat/ligma-cheat/hooks/dlsym_hook.cpp @@ -0,0 +1,49 @@ +#include "dlsym_hook.h" +#include "../ligma.h" + +namespace ligma +{ + namespace hook + { + auto get_dlsym_hooks() -> std::map, void*>* + { + static std::map, void*> hooks{}; + return &hooks; + } + + __attribute__((noinline)) + void* dlsym_bypass(void* handle, const char* symbol) + { + dlsym_mutex.lock(); + ligma::hook::disable(dlsym_ptr); + const auto result = + reinterpret_cast(dlsym_ptr)(handle, symbol); + ligma::hook::enable(dlsym_ptr); + dlsym_mutex.unlock(); + return result; + } + + __attribute__((noinline)) + void* dlsym_handler(void* handle, const char* symbol) + { + LOGI("dlsym hook called! handle = %p, symbol = %s", handle, symbol); + try + { + return get_dlsym_hooks()->at({ handle, symbol }); + } + catch (std::out_of_range& e) + {} + return dlsym_bypass(handle, symbol); + } + + void dlsym_unhook(const std::pair& symbol_data) + { + try + { + get_dlsym_hooks()->erase(symbol_data); + } + catch (std::out_of_range& e) + {} + } + } +} \ No newline at end of file diff --git a/ligma-cheat/ligma-cheat/hooks/dlsym_hook.h b/ligma-cheat/ligma-cheat/hooks/dlsym_hook.h new file mode 100644 index 000000000..3498bb138 --- /dev/null +++ b/ligma-cheat/ligma-cheat/hooks/dlsym_hook.h @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include +#include +#include "shithook.h" + +#define LOGI(...) ((void)__android_log_print(4, "ligma", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(5, "ligma", __VA_ARGS__)) + +namespace ligma +{ + namespace hook + { + inline void* dlsym_ptr = nullptr; + inline std::mutex dlsym_mutex; + + void* dlsym_bypass(void* handle, const char* symbol); + void* dlsym_handler(void* handle, const char* symbol); + auto get_dlsym_hooks() -> std::map, void*>*; + void dlsym_unhook(const std::pair& symbol_data); + + template + inline void dlsym_hook(std::pair symbol_data, T* function_ptr) + { + static std::once_flag once; + std::call_once(once, [&]() + { + make_hook((dlsym_ptr = dlsym(dlopen("libdl.so", RTLD_NOLOAD), "dlsym")), &dlsym_handler); + make_hook(&dlsym, &dlsym_bypass); + }); + + get_dlsym_hooks()->insert + ( + { + { + symbol_data.first, + std::string_view{ symbol_data.second } + }, + reinterpret_cast(function_ptr) + } + ); + } + } +} \ No newline at end of file diff --git a/ligma-cheat/ligma-cheat/hooks/shithook.cpp b/ligma-cheat/ligma-cheat/hooks/shithook.cpp new file mode 100644 index 000000000..633eb5540 --- /dev/null +++ b/ligma-cheat/ligma-cheat/hooks/shithook.cpp @@ -0,0 +1,62 @@ +#include "shithook.h" + +namespace ligma +{ + namespace hook + { + detour::detour(void* addr_to_hook, void* jmp_to, bool enable) + : + hook_addr(addr_to_hook), + detour_addr(jmp_to), + hook_installed(false) + { + reinterpret_cast(jmp_code)[0] = ARM_JMP_CODE; // LDR PC, [PC, #-4] + reinterpret_cast(jmp_code)[1] = jmp_to; + memcpy(org_bytes, hook_addr, sizeof(org_bytes)); + if (enable) install(); + } + + detour::~detour() + { + uninstall(); + } + + bool detour::installed() { return hook_installed; } + void* detour::hook_address() { return hook_addr; } + void* detour::detour_address() { return detour_addr; } + + void detour::install() + { + if (hook_installed.load()) + return; + + if (!mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC)) + { + memcpy((void*)((long)hook_addr), jmp_code, sizeof(jmp_code)); + mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_EXEC); + cacheflush(reinterpret_cast(hook_addr), reinterpret_cast(hook_addr) + getpagesize(), NULL); + hook_installed.exchange(true); + } + } + + void detour::uninstall() + { + if (!hook_installed.load()) + return; + + if (!mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC)) + { + memcpy(hook_addr, org_bytes, sizeof(jmp_code)); + mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_EXEC); + cacheflush(reinterpret_cast(hook_addr), reinterpret_cast(hook_addr) + getpagesize(), NULL); + hook_installed.exchange(false); + } + } + + std::map>* get_hooks() + { + static std::map> hooks{}; + return &hooks; + } + } +} \ No newline at end of file diff --git a/ligma-cheat/ligma-cheat/hooks/shithook.h b/ligma-cheat/ligma-cheat/hooks/shithook.h index 57759f4ea..2b72049c3 100644 --- a/ligma-cheat/ligma-cheat/hooks/shithook.h +++ b/ligma-cheat/ligma-cheat/hooks/shithook.h @@ -28,8 +28,6 @@ #include #include #include -#include -#include #define PAGE_START(ptr) reinterpret_cast(reinterpret_cast(ptr) >> 12 << 12) #define ARM_JMP_CODE 0xE51FF004 @@ -41,156 +39,62 @@ namespace ligma class detour { public: - detour(void* addr_to_hook, void* jmp_to, bool enable = true) - : - hook_addr(addr_to_hook), - detour_addr(jmp_to), - hook_installed(false) - { - reinterpret_cast(jmp_code)[0] = ARM_JMP_CODE; // LDR PC, [PC, #-4] - reinterpret_cast(jmp_code)[1] = jmp_to; - memcpy(org_bytes, hook_addr, sizeof(org_bytes)); - if (enable) install(); - } - - void install() - { - if (hook_installed.load()) - return; - - if (!mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC)) - { - memcpy((void*)((long)hook_addr), jmp_code, sizeof(jmp_code)); - mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_EXEC); - cacheflush(reinterpret_cast(hook_addr), reinterpret_cast(hook_addr) + getpagesize(), NULL); - hook_installed.exchange(true); - } - } - - void uninstall() - { - if (!hook_installed.load()) - return; - - if (!mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC)) - { - memcpy(hook_addr, org_bytes, sizeof(jmp_code)); - mprotect(PAGE_START(hook_addr), getpagesize(), PROT_READ | PROT_EXEC); - cacheflush(reinterpret_cast(hook_addr), reinterpret_cast(hook_addr) + getpagesize(), NULL); - hook_installed.exchange(false); - } - } - - ~detour() { uninstall(); } - bool installed() { return hook_installed; } - void* hook_address() { return hook_addr; } - void* detour_address() { return detour_addr; } + detour(void* addr_to_hook, void* jmp_to, bool enable = true); + void install(); + void uninstall(); + ~detour(); + bool installed(); + void* hook_address(); + void* detour_address(); private: std::atomic hook_installed; void* hook_addr, * detour_addr; - unsigned char jmp_code[8]{}; std::uint8_t org_bytes[sizeof(jmp_code)]; }; - inline std::map>* get_hooks() - { - static std::map> hooks; - return &hooks; - } - - inline std::map, void*>* get_dlsym_hook() - { - static std::map, void*> hooks{}; - return &hooks; - } + std::map>* get_hooks(); - inline void make_hook(void* addr_to_hook, void* jmp_to_addr, bool enable = true) + template + void make_hook(T* addr_to_hook, U* jmp_to_addr, bool enable = true) { - if (!addr_to_hook) + if (!addr_to_hook || !jmp_to_addr) return; get_hooks()->insert({ - addr_to_hook, + (void*)addr_to_hook, std::make_unique( - addr_to_hook, - jmp_to_addr, + (void*) addr_to_hook, + (void*) jmp_to_addr, enable )} ); } - inline void enable(void* addr) - { - if (!addr) - return; - get_hooks()->at(addr)->install(); - } - - inline void disable(void* addr) + template + inline void enable(T* addr) { - if (!addr) - return; - get_hooks()->at(addr)->uninstall(); + if (!addr) return; + get_hooks()->at((void*)addr)->install(); } - inline void remove(void* addr) - { - if (!addr) - return; - get_hooks()->erase(addr); - } - - inline std::mutex dlsym_mutex; - inline void* dlsym_ptr = nullptr; - - __attribute__((noinline)) - void* dlsym_bypass(void* handle, const char* symbol) + template + inline void disable(T* addr) { - dlsym_mutex.lock(); - ligma::hook::disable(dlsym_ptr); - const auto result = - reinterpret_cast(dlsym_ptr)(handle, symbol); - ligma::hook::enable(dlsym_ptr); - dlsym_mutex.unlock(); - return result; - } - - __attribute__((noinline)) - void* dlsym_handler(void* handle, const char* symbol) - { - for (const auto& [so_data, hook_ptr] : *get_dlsym_hook()) - if (strcmp(so_data.second.c_str(), symbol)) - if (dlopen(so_data.first.c_str(), RTLD_NOLOAD) == handle) - return hook_ptr; - - dlsym_mutex.lock(); - ligma::hook::disable(dlsym_ptr); - const auto result = dlsym(handle, symbol); - ligma::hook::enable(dlsym_ptr); - dlsym_mutex.unlock(); - return result; + if (!addr) return; + try + { + get_hooks()->at((void*)addr)->uninstall(); + } + catch (std::out_of_range& e) + {} } - void dlsym_hook(std::pair symbol_data, void* function_ptr) + template + inline void remove(T* addr) { - static std::once_flag once; - std::call_once(once, [&]() - { - make_hook( - dlsym(dlopen("libdl.so", RTLD_NOLOAD), "dlsym"), - reinterpret_cast(&dlsym_handler) - ); - - // - // this allows us to use "dlsym" anywhere in the code :) - // - make_hook( - reinterpret_cast(&dlsym), - reinterpret_cast(&dlsym_bypass) - ); - }); - get_dlsym_hook()->insert({ symbol_data, function_ptr }); + if (!addr) return; + get_hooks()->erase((void*)addr); } } } diff --git a/ligma-cheat/ligma-cheat/ligma-cheat.vcxproj b/ligma-cheat/ligma-cheat/ligma-cheat.vcxproj index 73e989052..555faceb1 100644 --- a/ligma-cheat/ligma-cheat/ligma-cheat.vcxproj +++ b/ligma-cheat/ligma-cheat/ligma-cheat.vcxproj @@ -36,11 +36,14 @@ + + + diff --git a/ligma-cheat/ligma-cheat/ligma-cheat.vcxproj.filters b/ligma-cheat/ligma-cheat/ligma-cheat.vcxproj.filters index 6d53f559f..a5df519b9 100644 --- a/ligma-cheat/ligma-cheat/ligma-cheat.vcxproj.filters +++ b/ligma-cheat/ligma-cheat/ligma-cheat.vcxproj.filters @@ -22,6 +22,9 @@ {83d12913-a2bb-4372-a9a4-387cd3b158ad} + + {f084f5b1-c94a-4e1b-ac6d-6fdeecb34ba4} + @@ -33,6 +36,12 @@ source\utils + + source\hooks + + + source\hooks + @@ -47,5 +56,8 @@ headers + + headers\hooks + \ No newline at end of file diff --git a/ligma-cheat/ligma-cheat/ligma.h b/ligma-cheat/ligma-cheat/ligma.h index 1d7fef398..0f7329ec7 100644 --- a/ligma-cheat/ligma-cheat/ligma.h +++ b/ligma-cheat/ligma-cheat/ligma.h @@ -2,6 +2,7 @@ #include #include "utils/utils.h" #include "hooks/shithook.h" +#include "hooks/dlsym_hook.h" #define LOGI(...) ((void)__android_log_print(4, "ligma", __VA_ARGS__)) #define LOGW(...) ((void)__android_log_print(5, "ligma", __VA_ARGS__)) \ No newline at end of file diff --git a/ligma-cheat/ligma-cheat/main.cpp b/ligma-cheat/ligma-cheat/main.cpp index 66de01915..50e8a3aa3 100644 --- a/ligma-cheat/ligma-cheat/main.cpp +++ b/ligma-cheat/ligma-cheat/main.cpp @@ -1,14 +1,11 @@ -#include -#include -#include -#include #include "bypass/bypass.h" __attribute__((constructor)) void init() { - ligma::bypass::init([&](const std::uintptr_t il2cpp_base) + ligma::bypass::init([&](std::uintptr_t il2cpp_base, void* module_handle) -> bool { - LOGI("il2cpp base address = %p", il2cpp_base); + LOGI("il2cpp base address = %p, module_handle = %p", il2cpp_base, module_handle); + return false; }); } \ No newline at end of file diff --git a/ligma-cheat/ligma-cheat/utils/utils.cpp b/ligma-cheat/ligma-cheat/utils/utils.cpp index 56ab70507..c465c3869 100644 --- a/ligma-cheat/ligma-cheat/utils/utils.cpp +++ b/ligma-cheat/ligma-cheat/utils/utils.cpp @@ -1,69 +1,63 @@ #include "utils.h" +#include "../ligma.h" namespace ligma { namespace utils { - void on_image_load(const std::string& module_name, const std::function& callback) + std::uintptr_t get_module_base(const char* module_name) { - static std::once_flag once; - std::call_once(once, [&]() { - ligma::hook::make_hook( - dlsym(dlopen("libdl.so", RTLD_NOLOAD), "dlopen"), - reinterpret_cast(&dlopen_handler) - ); - }); - get_callbacks()->insert({ module_name, callback }); + std::unique_ptr maps_handle(fopen("/proc/self/maps", "r"), &fclose); + char line[512], mod_name[64]; + std::uintptr_t base; + while (fgets(line, sizeof(line), maps_handle.get())) + if (std::sscanf(line, "%" PRIXPTR "-%*" PRIXPTR " %*s %*s %*s %*s %s", &base, mod_name)) + if (std::strstr(mod_name, module_name)) + return base; + return NULL; } - auto get_callbacks() -> std::map>* + auto get_callbacks() -> std::map>* { - static std::map> callback_map{}; + static std::map> callback_map{}; return &callback_map; } - std::uintptr_t get_module_base(const char* module_name) - { - const auto maps_handle = fopen("/proc/self/maps", "r"); - char line[512]; - while (fgets(line, sizeof line, maps_handle)) - { - std::uintptr_t base; - char tmp[64]; - sscanf(line, "%" PRIXPTR "-%*" PRIXPTR " %*s %*s %*s %*s %s", &base, tmp); - if (strstr(tmp, module_name)) - { - fclose(maps_handle); - return base; - } - } - fclose(maps_handle); - return NULL; - } - __attribute__((noinline)) void* dlopen_handler(const char* filename, int flags) { - dlopen_hook.second.lock(); - ligma::hook::disable(dlopen_hook.first); - const auto result = dlopen(filename, reinterpret_cast(RTLD_NEXT)); - if (ligma::utils::get_callbacks()->size()) - ligma::hook::enable(dlopen_hook.first); - dlopen_hook.second.unlock(); + dlopen_mutex.lock(); + ligma::hook::disable(dlopen_ptr); + const auto result = dlopen(filename, flags); + ligma::hook::enable(dlopen_ptr); + dlopen_mutex.unlock(); - // - // if there is a callback for this module, call it and then remove it. - // for (const auto& [file_key, callback] : *ligma::utils::get_callbacks()) { - if (strstr(filename, file_key.c_str())) + if (std::strstr(filename, file_key.c_str())) { + // remove the callback before calling it, this prevents loops... ligma::utils::get_callbacks()->erase(file_key); - callback(ligma::utils::get_module_base(file_key.c_str())); + if (callback(ligma::utils::get_module_base(file_key.c_str()), result)) + on_image_load(file_key, callback); // add it back if we still want the callback... break; } } return result; } + + void on_image_load(const std::string& module_name, const std::function& callback) + { + static std::once_flag once; + std::call_once(once, [&]() + { + ligma::hook::make_hook + ( + (dlopen_ptr = dlsym(dlopen("libdl.so", RTLD_NOLOAD), "dlopen")), + &dlopen_handler + ); + }); + get_callbacks()->insert({ module_name, callback }); + } } } \ No newline at end of file diff --git a/ligma-cheat/ligma-cheat/utils/utils.h b/ligma-cheat/ligma-cheat/utils/utils.h index a2af3b307..6e0a0e06f 100644 --- a/ligma-cheat/ligma-cheat/utils/utils.h +++ b/ligma-cheat/ligma-cheat/utils/utils.h @@ -1,20 +1,23 @@ #pragma once -#include -#include #include +#include #include #include +#include +#include +#include #include "../hooks/shithook.h" namespace ligma { namespace utils { - inline std::pair dlopen_hook; + inline void* dlopen_ptr = nullptr; + inline std::mutex dlopen_mutex; + std::uintptr_t get_module_base(const char* module_name); - auto get_callbacks() -> std::map>*; - void on_image_load(const std::string& module_name, const std::function& callback); - void dlsym_hook(const std::string& symbol_name, void* function_ptr); + auto get_callbacks() -> std::map>*; void* dlopen_handler(const char* filename, int flags); + void on_image_load(const std::string& module_name, const std::function& callback); } } \ No newline at end of file