working with new ligma-utils code

master
xerox 4 years ago
parent bf63bdab4f
commit 11571a5f10

Binary file not shown.

@ -8,10 +8,10 @@ namespace ligma
// do not call anything that will call dlopen inside of callback.... // do not call anything that will call dlopen inside of callback....
// //
__attribute__((noinline)) __attribute__((noinline))
void init(const std::function<void(std::uintptr_t)>& callback) void init(const std::function<bool(std::uintptr_t, void*)>& callback)
{ {
ligma::utils::on_image_load("libil2cpp.so", 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); LOGI("libxlua.so base = %p", module_base);
ligma::hook::make_hook( ligma::hook::make_hook(
@ -19,17 +19,18 @@ namespace ligma
reinterpret_cast<void*>(&load_bufferx_hook) reinterpret_cast<void*>(&load_bufferx_hook)
); );
LOGI("installed libxlua.so hooks!"); 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 = %p", module_base);
LOGI("libtersafe.so handle = %p", dlopen("libtersafe.so", RTLD_NOLOAD)); return false;
}); });
fopen_ptr = dlsym(dlopen("libc.so", RTLD_NOLOAD), "fopen"); fopen_ptr = dlsym(dlopen("libc.so", RTLD_NOLOAD), "fopen");
system_prop_get = dlsym(dlopen("libc.so", RTLD_NOLOAD), "__system_property_get"); system_prop_get = dlsym(dlopen("libc.so", RTLD_NOLOAD), "__system_property_get");
ligma::hook::make_hook(fopen_ptr, reinterpret_cast<void*>(&fopen_hook)); ligma::hook::make_hook(fopen_ptr, reinterpret_cast<void*>(&fopen_hook));
ligma::hook::make_hook(system_prop_get, reinterpret_cast<void*>(&system_property_hook)); ligma::hook::make_hook(system_prop_get, reinterpret_cast<void*>(&system_property_hook));
} }

@ -18,7 +18,7 @@ namespace ligma
inline std::mutex fopen_mutex; inline std::mutex fopen_mutex;
inline std::mutex system_prop_mutex; inline std::mutex system_prop_mutex;
void init(const std::function<void(std::uintptr_t)>& callback); void init(const std::function<bool(std::uintptr_t, void*)>& callback);
FILE* fopen_hook(const char* path, const char* mode); FILE* fopen_hook(const char* path, const char* mode);
int system_property_hook(const char* name, char* value); 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); int load_bufferx_hook(void* L, const char* buff, size_t sz, const char* name, const char* mode);

@ -0,0 +1,49 @@
#include "dlsym_hook.h"
#include "../ligma.h"
namespace ligma
{
namespace hook
{
auto get_dlsym_hooks() -> std::map<std::pair<void*, std::string_view>, void*>*
{
static std::map<std::pair<void*, std::string_view>, 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<decltype(&dlsym_bypass)>(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<void*, const char*>& symbol_data)
{
try
{
get_dlsym_hooks()->erase(symbol_data);
}
catch (std::out_of_range& e)
{}
}
}
}

@ -0,0 +1,45 @@
#pragma once
#include <mutex>
#include <dlfcn.h>
#include <string>
#include <android/log.h>
#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<std::pair<void*, std::string_view>, void*>*;
void dlsym_unhook(const std::pair<void*, const char*>& symbol_data);
template <class T>
inline void dlsym_hook(std::pair<void*, const char*> 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<void*>(function_ptr)
}
);
}
}
}

@ -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<std::uint32_t*>(jmp_code)[0] = ARM_JMP_CODE; // LDR PC, [PC, #-4]
reinterpret_cast<void**>(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<long>(hook_addr), reinterpret_cast<long>(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<long>(hook_addr), reinterpret_cast<long>(hook_addr) + getpagesize(), NULL);
hook_installed.exchange(false);
}
}
std::map<void*, std::unique_ptr<detour>>* get_hooks()
{
static std::map<void*, std::unique_ptr<detour>> hooks{};
return &hooks;
}
}
}

@ -28,8 +28,6 @@
#include <memory> #include <memory>
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <mutex>
#include <string.h>
#define PAGE_START(ptr) reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(ptr) >> 12 << 12) #define PAGE_START(ptr) reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(ptr) >> 12 << 12)
#define ARM_JMP_CODE 0xE51FF004 #define ARM_JMP_CODE 0xE51FF004
@ -41,156 +39,62 @@ namespace ligma
class detour class detour
{ {
public: public:
detour(void* addr_to_hook, void* jmp_to, bool enable = true) detour(void* addr_to_hook, void* jmp_to, bool enable = true);
: void install();
hook_addr(addr_to_hook), void uninstall();
detour_addr(jmp_to), ~detour();
hook_installed(false) bool installed();
{ void* hook_address();
reinterpret_cast<std::uint32_t*>(jmp_code)[0] = ARM_JMP_CODE; // LDR PC, [PC, #-4] void* detour_address();
reinterpret_cast<void**>(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<long>(hook_addr), reinterpret_cast<long>(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<long>(hook_addr), reinterpret_cast<long>(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; }
private: private:
std::atomic<bool> hook_installed; std::atomic<bool> hook_installed;
void* hook_addr, * detour_addr; void* hook_addr, * detour_addr;
unsigned char jmp_code[8]{}; unsigned char jmp_code[8]{};
std::uint8_t org_bytes[sizeof(jmp_code)]; std::uint8_t org_bytes[sizeof(jmp_code)];
}; };
inline std::map<void*, std::unique_ptr<detour>>* get_hooks() std::map<void*, std::unique_ptr<detour>>* get_hooks();
{
static std::map<void*, std::unique_ptr<detour>> hooks;
return &hooks;
}
inline std::map<std::pair<const std::string, const std::string>, void*>* get_dlsym_hook() template <class T, class U>
void make_hook(T* addr_to_hook, U* jmp_to_addr, bool enable = true)
{ {
static std::map<std::pair<const std::string, const std::string>, void*> hooks{}; if (!addr_to_hook || !jmp_to_addr)
return &hooks;
}
inline void make_hook(void* addr_to_hook, void* jmp_to_addr, bool enable = true)
{
if (!addr_to_hook)
return; return;
get_hooks()->insert({ get_hooks()->insert({
addr_to_hook, (void*)addr_to_hook,
std::make_unique<detour>( std::make_unique<detour>(
addr_to_hook, (void*) addr_to_hook,
jmp_to_addr, (void*) jmp_to_addr,
enable enable
)} )}
); );
} }
inline void enable(void* addr) template<class T>
inline void enable(T* addr)
{ {
if (!addr) if (!addr) return;
return; get_hooks()->at((void*)addr)->install();
get_hooks()->at(addr)->install();
} }
inline void disable(void* addr) template <class T>
inline void disable(T* addr)
{ {
if (!addr) if (!addr) return;
return; try
get_hooks()->at(addr)->uninstall(); {
} get_hooks()->at((void*)addr)->uninstall();
}
inline void remove(void* addr) catch (std::out_of_range& e)
{ {}
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)
{
dlsym_mutex.lock();
ligma::hook::disable(dlsym_ptr);
const auto result =
reinterpret_cast<decltype(&dlsym_bypass)>(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;
} }
void dlsym_hook(std::pair<const std::string&, const std::string&> symbol_data, void* function_ptr) template <class T>
inline void remove(T* addr)
{ {
static std::once_flag once; if (!addr) return;
std::call_once(once, [&]() get_hooks()->erase((void*)addr);
{
make_hook(
dlsym(dlopen("libdl.so", RTLD_NOLOAD), "dlsym"),
reinterpret_cast<void*>(&dlsym_handler)
);
//
// this allows us to use "dlsym" anywhere in the code :)
//
make_hook(
reinterpret_cast<void*>(&dlsym),
reinterpret_cast<void*>(&dlsym_bypass)
);
});
get_dlsym_hook()->insert({ symbol_data, function_ptr });
} }
} }
} }

@ -36,11 +36,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="bypass\bypass.cpp" /> <ClCompile Include="bypass\bypass.cpp" />
<ClCompile Include="hooks\dlsym_hook.cpp" />
<ClCompile Include="hooks\shithook.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="utils\utils.cpp" /> <ClCompile Include="utils\utils.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="bypass\bypass.h" /> <ClInclude Include="bypass\bypass.h" />
<ClInclude Include="hooks\dlsym_hook.h" />
<ClInclude Include="hooks\shithook.h" /> <ClInclude Include="hooks\shithook.h" />
<ClInclude Include="ligma.h" /> <ClInclude Include="ligma.h" />
<ClInclude Include="utils\utils.h" /> <ClInclude Include="utils\utils.h" />

@ -22,6 +22,9 @@
<Filter Include="source\utils"> <Filter Include="source\utils">
<UniqueIdentifier>{83d12913-a2bb-4372-a9a4-387cd3b158ad}</UniqueIdentifier> <UniqueIdentifier>{83d12913-a2bb-4372-a9a4-387cd3b158ad}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="source\hooks">
<UniqueIdentifier>{f084f5b1-c94a-4e1b-ac6d-6fdeecb34ba4}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="main.cpp"> <ClCompile Include="main.cpp">
@ -33,6 +36,12 @@
<ClCompile Include="utils\utils.cpp"> <ClCompile Include="utils\utils.cpp">
<Filter>source\utils</Filter> <Filter>source\utils</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="hooks\dlsym_hook.cpp">
<Filter>source\hooks</Filter>
</ClCompile>
<ClCompile Include="hooks\shithook.cpp">
<Filter>source\hooks</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="bypass\bypass.h"> <ClInclude Include="bypass\bypass.h">
@ -47,5 +56,8 @@
<ClInclude Include="ligma.h"> <ClInclude Include="ligma.h">
<Filter>headers</Filter> <Filter>headers</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="hooks\dlsym_hook.h">
<Filter>headers\hooks</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -2,6 +2,7 @@
#include <android/log.h> #include <android/log.h>
#include "utils/utils.h" #include "utils/utils.h"
#include "hooks/shithook.h" #include "hooks/shithook.h"
#include "hooks/dlsym_hook.h"
#define LOGI(...) ((void)__android_log_print(4, "ligma", __VA_ARGS__)) #define LOGI(...) ((void)__android_log_print(4, "ligma", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(5, "ligma", __VA_ARGS__)) #define LOGW(...) ((void)__android_log_print(5, "ligma", __VA_ARGS__))

@ -1,14 +1,11 @@
#include <chrono>
#include <thread>
#include <assembly-csharp.dll/gamebase.h>
#include <assembly-csharp.dll/gameengine.h>
#include "bypass/bypass.h" #include "bypass/bypass.h"
__attribute__((constructor)) __attribute__((constructor))
void init() 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;
}); });
} }

@ -1,69 +1,63 @@
#include "utils.h" #include "utils.h"
#include "../ligma.h"
namespace ligma namespace ligma
{ {
namespace utils namespace utils
{ {
void on_image_load(const std::string& module_name, const std::function<void(const std::uintptr_t)>& callback) std::uintptr_t get_module_base(const char* module_name)
{ {
static std::once_flag once; std::unique_ptr<FILE, decltype(&fclose)> maps_handle(fopen("/proc/self/maps", "r"), &fclose);
std::call_once(once, [&]() { char line[512], mod_name[64];
ligma::hook::make_hook( std::uintptr_t base;
dlsym(dlopen("libdl.so", RTLD_NOLOAD), "dlopen"), while (fgets(line, sizeof(line), maps_handle.get()))
reinterpret_cast<void*>(&dlopen_handler) if (std::sscanf(line, "%" PRIXPTR "-%*" PRIXPTR " %*s %*s %*s %*s %s", &base, mod_name))
); if (std::strstr(mod_name, module_name))
}); return base;
get_callbacks()->insert({ module_name, callback }); return NULL;
} }
auto get_callbacks() -> std::map<std::string, std::function<void(const std::uintptr_t)>>* auto get_callbacks() -> std::map<std::string, std::function<bool(std::uintptr_t, void*)>>*
{ {
static std::map<std::string, std::function<void(const std::uintptr_t)>> callback_map{}; static std::map<std::string, std::function<bool(std::uintptr_t, void*)>> callback_map{};
return &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)) __attribute__((noinline))
void* dlopen_handler(const char* filename, int flags) void* dlopen_handler(const char* filename, int flags)
{ {
dlopen_hook.second.lock(); dlopen_mutex.lock();
ligma::hook::disable(dlopen_hook.first); ligma::hook::disable(dlopen_ptr);
const auto result = dlopen(filename, reinterpret_cast<int>(RTLD_NEXT)); const auto result = dlopen(filename, flags);
if (ligma::utils::get_callbacks()->size()) ligma::hook::enable(dlopen_ptr);
ligma::hook::enable(dlopen_hook.first); dlopen_mutex.unlock();
dlopen_hook.second.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()) 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); 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; break;
} }
} }
return result; return result;
} }
void on_image_load(const std::string& module_name, const std::function<bool (std::uintptr_t, void*)>& 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 });
}
} }
} }

@ -1,20 +1,23 @@
#pragma once #pragma once
#include <functional>
#include <fstream>
#include <map> #include <map>
#include <functional>
#include <inttypes.h> #include <inttypes.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <mutex>
#include <string>
#include <fstream>
#include "../hooks/shithook.h" #include "../hooks/shithook.h"
namespace ligma namespace ligma
{ {
namespace utils namespace utils
{ {
inline std::pair<void*, std::mutex> dlopen_hook; inline void* dlopen_ptr = nullptr;
inline std::mutex dlopen_mutex;
std::uintptr_t get_module_base(const char* module_name); std::uintptr_t get_module_base(const char* module_name);
auto get_callbacks() -> std::map<std::string, std::function<void(const std::uintptr_t)>>*; auto get_callbacks() -> std::map<std::string, std::function<bool(std::uintptr_t, void*)>>*;
void on_image_load(const std::string& module_name, const std::function<void(const std::uintptr_t)>& callback);
void dlsym_hook(const std::string& symbol_name, void* function_ptr);
void* dlopen_handler(const char* filename, int flags); void* dlopen_handler(const char* filename, int flags);
void on_image_load(const std::string& module_name, const std::function<bool(std::uintptr_t, void*)>& callback);
} }
} }
Loading…
Cancel
Save