imgui-mirror/main.cpp
2026-04-08 02:27:46 +08:00

683 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <pthread.h>
#include <unistd.h>
#include <android/log.h>
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <dlfcn.h>
#include <android/input.h>
#include <fcntl.h>
#include <dirent.h>
#include <link.h>
#include <sys/mman.h>
#include <cstdarg>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <atomic>
#include <vector>
#include <deque>
#include <mutex>
#include <thread>
#include <chrono>
#include <memory>
#include <string>
#include "dobby.h"
#include "imgui.h"
#include "imgui_impl_android.h"
#include "imgui_impl_opengl3.h"
namespace Overlay {
// ============================================================================
// 常量定义
// ============================================================================
constexpr const char* kTag = "IL2CPP_Drive";
constexpr const char* kGameModule = "libil2cpp.so";
constexpr size_t kMaxLogSize = 30;
constexpr size_t kMaxPlayers = 10; // 根据要求修改为 10 个英雄玩家
// ============================================================================
// 日志系统类
// ============================================================================
class Logger {
public:
static Logger& GetInstance() {
static Logger instance;
return instance;
}
void Print(const char* level, const char* fmt, ...) {
char buf[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
__android_log_print(ANDROID_LOG_INFO, kTag, "%s%s", level, buf);
std::lock_guard<std::mutex> lock(m_mutex);
if (m_logs.size() >= kMaxLogSize) {
m_logs.pop_front();
}
m_logs.emplace_back(buf);
}
const std::deque<std::string>& GetLogs() const { return m_logs; }
std::mutex& GetMutex() { return m_mutex; }
private:
Logger() = default;
std::deque<std::string> m_logs;
std::mutex m_mutex;
};
#define LOG_INFO(...) Overlay::Logger::GetInstance().Print("", __VA_ARGS__)
#define LOG_ERROR(...) Overlay::Logger::GetInstance().Print("[ERROR] ", __VA_ARGS__)
// ============================================================================
// 内存读取工具类 (无视 SELinux 的 mincore 极速安全方案)
// ============================================================================
class MemoryReader {
public:
static bool IsValidAddress(uintptr_t address) {
if (address < 0x10000000 || address > 0x00007FFFFFFFFFFF) return false;
unsigned char vec[1];
uintptr_t pageStart = address & ~0xFFF;
if (mincore(reinterpret_cast<void*>(pageStart), 4096, vec) != 0) {
return false;
}
return true;
}
template <typename T>
static T SafeRead(uintptr_t address) {
if (!IsValidAddress(address)) return T{};
return *reinterpret_cast<volatile T*>(address);
}
template <typename T>
static T FastRead(uintptr_t address) {
return *reinterpret_cast<volatile T*>(address);
}
};
// ============================================================================
// 最新基址偏移结构体
// ============================================================================
struct DynamicOffsets {
uint32_t x0ToAddr1 = 0x18; // x0偏移18 -> 地址1
uint32_t addr1ToAddr2 = 0x8; // 地址1偏移8 -> 地址2
uint32_t arrayStart = 0x20; // 数组首个数据偏移
uint32_t arrayStep = 0x10; // 数组步长
uint32_t itemToAddr3 = 0x0; // 每条元素偏移0x0 -> 地址3(英雄指针)
uint32_t addr3ToWorld = 0x4C4; // 地址3偏移4c4 -> 世界坐标
uint32_t addr3ToAddr4 = 0x3B8; // 地址3偏移3B8 -> 地址4
uint32_t addr4ToGold = 0x30; // 地址4偏移30 -> 金币
uint32_t addr4ToHp = 0x38; // 地址4偏移38 -> 血量
uint32_t addr4ToMaxHp = 0x3C; // 地址4偏移3C -> 最大血量
uint32_t addr4ToLevel = 0x58; // 地址4偏移58 -> 等级
uint32_t addr4ToAddr5 = 0x50; // 地址4偏移50 -> 地址5
uint32_t addr5ToMana = 0x24; // 地址5偏移24 -> 蓝量
uint32_t addr5ToMaxMana = 0x28;// 地址5偏移28 -> 最大蓝量
};
struct PlayerData {
int32_t hp = 0;
int32_t maxHp = 0;
int32_t mana = 0;
int32_t maxMana = 0;
int32_t gold = 0;
int32_t level = 0;
float worldX = 0.0f, worldY = 0.0f, worldZ = 0.0f;
float mapX = 0.0f, mapY = 0.0f;
};
struct DebugItem { uintptr_t base = 0, addr3 = 0, addr4 = 0, addr5 = 0; };
struct DebugChain { uintptr_t baseX0 = 0, addr1 = 0, addr2 = 0; DebugItem items[kMaxPlayers]; };
struct GameSnapshot {
bool inMatch = false;
DynamicOffsets offsets;
DebugChain debug;
std::vector<PlayerData> players;
};
// ============================================================================
// 触控管理类
// ============================================================================
class TouchManager {
public:
static TouchManager& GetInstance() { static TouchManager instance; return instance; }
void ProcessEvent(AInputEvent* event) {
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
int32_t action = AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK;
float autoScaleX = m_renderW / m_physW;
float autoScaleY = m_renderH / m_physH;
m_x = (AMotionEvent_getX(event, 0) * autoScaleX) + m_offsetX;
m_y = (AMotionEvent_getY(event, 0) * autoScaleY) + m_offsetY;
if (action == AMOTION_EVENT_ACTION_DOWN) m_isDown.store(true);
else if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) m_isDown.store(false);
}
}
void UpdateRenderSize(float w, float h) { m_renderW = w; m_renderH = h; }
float GetX() const { return m_x; }
float GetY() const { return m_y; }
bool IsDown() const { return m_isDown.load(); }
float m_offsetX = 0.0f, m_offsetY = 0.0f;
float m_renderW = 0.0f, m_renderH = 0.0f;
private:
TouchManager() = default;
float m_x = 0.0f, m_y = 0.0f;
std::atomic<bool> m_isDown{false};
float m_physW = 3392.0f, m_physH = 2400.0f;
};
// ============================================================================
// 核心数据引擎 (依照新规则遍历)
// ============================================================================
class EngineCore {
public:
static EngineCore& GetInstance() { static EngineCore instance; return instance; }
void InitWorkerThread() { std::thread(&EngineCore::DataWorkerLoop, this).detach(); }
std::shared_ptr<GameSnapshot> GetLatestSnapshot() {
std::lock_guard<std::mutex> lock(m_snapshotMutex);
return m_currentSnapshot;
}
DynamicOffsets GetOffsets() { std::lock_guard<std::mutex> lock(m_offsetMutex); return m_offsets; }
void SetActorManager(uintptr_t address) { m_actorManager.store(address); }
std::mutex& GetOffsetMutex() { return m_offsetMutex; }
DynamicOffsets& GetOffsetsRef() { return m_offsets; }
private:
EngineCore() : m_currentSnapshot(std::make_shared<GameSnapshot>()) {}
void DataWorkerLoop() {
while (true) {
auto newSnap = std::make_shared<GameSnapshot>();
newSnap->offsets = GetOffsets();
uintptr_t baseX0 = m_actorManager.load();
if (baseX0 != 0 && MemoryReader::IsValidAddress(baseX0)) {
newSnap->inMatch = true;
newSnap->debug.baseX0 = baseX0;
// x0 -> Addr1
uintptr_t addr1 = MemoryReader::SafeRead<uintptr_t>(baseX0 + newSnap->offsets.x0ToAddr1);
newSnap->debug.addr1 = addr1;
if (addr1) {
// Addr1 -> Addr2 (英雄数组)
uintptr_t addr2 = MemoryReader::SafeRead<uintptr_t>(addr1 + newSnap->offsets.addr1ToAddr2);
newSnap->debug.addr2 = addr2;
if (addr2) ParsePlayers(newSnap, addr2);
}
}
{
std::lock_guard<std::mutex> lock(m_snapshotMutex);
m_currentSnapshot = newSnap;
}
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
}
void ParsePlayers(std::shared_ptr<GameSnapshot>& snap, uintptr_t addr2) {
for (size_t i = 0; i < kMaxPlayers; ++i) { // 遍历 10 个英雄玩家
// 数组步长 0x10, 首个数据 0x20 开始
uintptr_t itemAddr = addr2 + snap->offsets.arrayStart + (i * snap->offsets.arrayStep);
snap->debug.items[i].base = itemAddr;
// 读每一条里面 0x0 位置,获得地址3 (英雄实例指针)
uintptr_t addr3 = MemoryReader::SafeRead<uintptr_t>(itemAddr + snap->offsets.itemToAddr3);
snap->debug.items[i].addr3 = addr3;
if (!addr3) continue;
PlayerData p;
// 地址3偏移4c4获得此玩家世界坐标
p.worldX = MemoryReader::SafeRead<float>(addr3 + snap->offsets.addr3ToWorld + 0x0);
p.worldY = MemoryReader::SafeRead<float>(addr3 + snap->offsets.addr3ToWorld + 0x4);
p.worldZ = MemoryReader::SafeRead<float>(addr3 + snap->offsets.addr3ToWorld + 0x8);
// 将世界坐标映射至小地图雷达坐标 (平面雷达通常是 X,Z)
p.mapX = p.worldX;
p.mapY = p.worldZ;
// 偏移0x3B8获得地址4
uintptr_t addr4 = MemoryReader::SafeRead<uintptr_t>(addr3 + snap->offsets.addr3ToAddr4);
snap->debug.items[i].addr4 = addr4;
if (addr4) {
// 地址4里面的各项属性
p.gold = MemoryReader::FastRead<int32_t>(addr4 + snap->offsets.addr4ToGold);
p.hp = MemoryReader::FastRead<int32_t>(addr4 + snap->offsets.addr4ToHp);
p.maxHp = MemoryReader::FastRead<int32_t>(addr4 + snap->offsets.addr4ToMaxHp);
p.level = MemoryReader::FastRead<int32_t>(addr4 + snap->offsets.addr4ToLevel);
// 偏移50获得地址5
uintptr_t addr5 = MemoryReader::FastRead<uintptr_t>(addr4 + snap->offsets.addr4ToAddr5);
snap->debug.items[i].addr5 = addr5;
if (addr5) {
// 地址5里的蓝量属性
p.mana = MemoryReader::FastRead<int32_t>(addr5 + snap->offsets.addr5ToMana);
p.maxMana = MemoryReader::FastRead<int32_t>(addr5 + snap->offsets.addr5ToMaxMana);
}
}
// 过滤无效实体
if (p.maxHp > 0 && p.maxHp < 50000) {
snap->players.push_back(p);
}
}
}
std::shared_ptr<GameSnapshot> m_currentSnapshot;
std::mutex m_snapshotMutex;
DynamicOffsets m_offsets;
std::mutex m_offsetMutex;
std::atomic<uintptr_t> m_actorManager{0};
};
// ============================================================================
// UI 渲染引擎 (包含原生右下角缩放及宽容布局)
// ============================================================================
class UIRenderer {
public:
static void RenderFrame(float w, float h, std::shared_ptr<GameSnapshot>& snap) {
static bool initialized = false;
if (!initialized) {
InitializeImGui();
initialized = true;
}
auto& touch = TouchManager::GetInstance();
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2(w, h);
io.MousePos = ImVec2(touch.GetX(), touch.GetY());
io.MouseDown[0] = touch.IsDown();
float finalScale = std::max(1.0f, h / 1000.0f) * m_userScale;
ImGuiStyle& style = ImGui::GetStyle();
style = m_defaultStyle;
style.ScaleAllSizes(1.2f * finalScale);
io.FontGlobalScale = 1.2f * finalScale;
ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();
if (touch.IsDown()) {
ImGui::GetForegroundDrawList()->AddCircleFilled(io.MousePos, 20.0f * finalScale, IM_COL32(0, 255, 170, 150));
ImGui::GetForegroundDrawList()->AddCircle(io.MousePos, 28.0f * finalScale, IM_COL32(0, 255, 170, 200), 0, 2.0f);
}
DrawMainWindow(w, h, snap, finalScale);
if (m_showDebugger) DrawDebugger(w, h, snap);
if (m_showRadar && snap->inMatch) DrawRadar(w, h, snap, finalScale);
ImGui::Render();
}
static bool m_isHookInstalled;
static std::string m_hookStatusMsg;
private:
static float m_userScale;
static ImGuiStyle m_defaultStyle;
static bool m_showDebugger;
static bool m_showRadar;
static void ApplyTechStyle(ImGuiStyle& style) {
ImVec4* colors = style.Colors;
colors[ImGuiCol_Text] = ImVec4(0.85f, 0.95f, 1.00f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.09f, 0.96f);
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.25f);
colors[ImGuiCol_Border] = ImVec4(0.15f, 0.55f, 0.85f, 0.60f);
colors[ImGuiCol_FrameBg] = ImVec4(0.10f, 0.14f, 0.22f, 0.80f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.18f, 0.35f, 0.58f, 0.80f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.22f, 0.45f, 0.75f, 1.00f);
colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.06f, 1.00f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.08f, 0.20f, 0.38f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.20f, 0.55f, 0.85f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.10f, 0.30f, 0.55f, 0.80f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.15f, 0.40f, 0.70f, 1.00f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.25f, 0.55f, 0.85f, 1.00f);
colors[ImGuiCol_Header] = ImVec4(0.12f, 0.25f, 0.45f, 0.80f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.18f, 0.35f, 0.60f, 1.00f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.22f, 0.45f, 0.75f, 1.00f);
style.WindowRounding = 4.0f;
style.ChildRounding = 2.0f;
style.FrameRounding = 2.0f;
style.WindowBorderSize = 1.0f;
style.FrameBorderSize = 1.0f;
}
static void InitializeImGui() {
ImGui::CreateContext();
ImGui_ImplOpenGL3_Init("#version 300 es");
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = nullptr; // 禁用缓存防溢出
m_defaultStyle = ImGui::GetStyle();
ApplyTechStyle(m_defaultStyle);
io.Fonts->AddFontDefault();
ImFontConfig config;
config.MergeMode = true;
const char* systemFonts[] = { "/system/fonts/SysSans-Hans-Regular.ttf", "/system/fonts/NotoSansCJKjp-Regular.otc" };
for (const char* fontPath : systemFonts) {
if (access(fontPath, R_OK) == 0) {
io.Fonts->AddFontFromFileTTF(fontPath, 32.0f, &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
break;
}
}
}
static void DrawMainWindow(float w, float h, std::shared_ptr<GameSnapshot>& snap, float finalScale) {
ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver);
// 使用宽容模式布局:AlwaysAutoResize
if (ImGui::Begin("CYBER-CORE 战术终端 // HERO_RADAR", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!m_isHookInstalled) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.7f, 0.1f, 0.2f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.2f, 0.3f, 1.0f));
if (ImGui::Button("[ ⚠ ] 建立核心态神经链路 (Kernel Hook)", ImVec2(360 * finalScale, 45 * finalScale))) {
extern void InstallHooks();
InstallHooks();
}
ImGui::PopStyleColor(2);
} else {
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.4f, 1.0f), "[ ONLINE ] 核心链路稳定连接中...");
}
ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), "LOG: %s", m_hookStatusMsg.c_str());
ImGui::Separator();
ImGui::Checkbox("全息雷达 (HUD Radar)", &m_showRadar); ImGui::SameLine();
ImGui::Checkbox("内存探针 (Memory Debug)", &m_showDebugger);
ImGui::Separator();
ImGui::TextColored(ImVec4(0.0f, 1.0f, 1.0f, 1.0f), "DATA_STREAM // 战场英雄数据流");
if (!snap->inMatch) {
ImGui::TextColored(ImVec4(1, 0.5f, 0, 1), ">> 等待数据同步...");
} else {
for (const auto& p : snap->players) {
ImGui::Text("Lv.%-2d | HP: %4d/%-4d | MP: %4d/%-4d | Gold: %-4d | MAP: %.0f, %.0f",
p.level, p.hp, p.maxHp, p.mana, p.maxMana, p.gold, p.mapX, p.mapY);
}
}
// =====================================
// 底部安全全局缩放滑块 (右对齐)
// =====================================
ImGui::Spacing();
ImGui::Separator();
ImGui::AlignTextToFramePadding();
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.8f, 1.0f), "[ SYS_OPT: STABLE ]");
float sliderWidth = 250.0f * finalScale;
float rightPos = ImGui::GetWindowWidth() - sliderWidth - ImGui::GetStyle().WindowPadding.x;
if (rightPos > ImGui::GetCursorPosX()) {
ImGui::SameLine(rightPos);
} else {
ImGui::SameLine();
}
ImGui::SetNextItemWidth(sliderWidth);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.4f, 0.6f, 0.4f));
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.0f, 0.9f, 1.0f, 1.0f));
ImGui::SliderFloat("##GlobalScale", &m_userScale, 0.5f, 2.5f, "UI ZOOM: %.2fx");
ImGui::PopStyleColor(2);
}
ImGui::End();
}
static void DrawRadar(float w, float h, std::shared_ptr<GameSnapshot>& snap, float finalScale) {
ImGui::SetNextWindowPos(ImVec2(w - 380, 50), ImGuiCond_FirstUseEver);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.02f, 0.05f, 0.08f, 0.85f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.8f, 1.0f, 0.6f));
if (ImGui::Begin("HUD_Radar", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar)) {
ImGui::Dummy(ImVec2(260 * finalScale, 260 * finalScale));
ImVec2 p0 = ImGui::GetItemRectMin();
ImVec2 size = ImGui::GetItemRectSize();
ImDrawList* dl = ImGui::GetWindowDrawList();
ImVec2 center = ImVec2(p0.x + size.x * 0.5f, p0.y + size.y * 0.5f);
dl->AddRectFilled(p0, ImVec2(p0.x + size.x, p0.y + size.y), IM_COL32(10, 15, 25, 200), 8.0f);
dl->AddRect(p0, ImVec2(p0.x + size.x, p0.y + size.y), IM_COL32(0, 180, 255, 150), 8.0f, 0, 1.5f);
dl->AddLine(ImVec2(center.x, p0.y), ImVec2(center.x, p0.y + size.y), IM_COL32(0, 150, 255, 80));
dl->AddLine(ImVec2(p0.x, center.y), ImVec2(p0.x + size.x, center.y), IM_COL32(0, 150, 255, 80));
dl->AddCircle(center, size.x * 0.25f, IM_COL32(0, 200, 255, 60), 32, 1.0f);
dl->AddCircle(center, size.x * 0.45f, IM_COL32(0, 200, 255, 60), 32, 1.0f);
for (const auto& p : snap->players) {
float nX = p.mapX / 10000.0f;
float nY = p.mapY / 10000.0f;
float rX = p0.x + (nX * size.x);
float rY = p0.y + size.y - (nY * size.y);
if (nX > 0 && nX < 1 && nY > 0 && nY < 1) {
ImVec2 point(rX, rY);
dl->AddCircleFilled(point, 8.0f * finalScale, IM_COL32(255, 50, 50, 80));
dl->AddCircleFilled(point, 4.0f * finalScale, IM_COL32(255, 80, 80, 255));
}
}
}
ImGui::End();
ImGui::PopStyleColor(2);
}
static void DrawDebugger(float w, float h, std::shared_ptr<GameSnapshot>& snap) {
ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver);
if (ImGui::Begin("MEMORY_PROBE // 内存深度探查", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
auto& engine = EngineCore::GetInstance();
std::lock_guard<std::mutex> lock(engine.GetOffsetMutex());
auto& offsets = engine.GetOffsetsRef();
auto drawOffsetBtn = [](const char* label, uint32_t& val) {
ImGui::PushID(&val);
ImGui::TextColored(ImVec4(0.4f, 0.9f, 1.0f, 1.0f), "%s : %d (0x%X)", label, val, val);
if (ImGui::Button(" - 4 ", ImVec2(50, 0))) { if (val >= 4) val -= 4; else val = 0; } ImGui::SameLine();
if (ImGui::Button(" + 4 ", ImVec2(50, 0))) val += 4;
ImGui::Separator();
ImGui::PopID();
};
drawOffsetBtn("x0_to_addr1 (x0 + ?)", offsets.x0ToAddr1);
drawOffsetBtn("addr1_to_addr2 (addr1 + ?)", offsets.addr1ToAddr2);
drawOffsetBtn("arrayStart (首个数据偏移)", offsets.arrayStart);
drawOffsetBtn("item_to_addr3 (实体指针偏移)", offsets.itemToAddr3);
drawOffsetBtn("addr3ToAddr4", offsets.addr3ToAddr4);
drawOffsetBtn("addr4ToAddr5", offsets.addr4ToAddr5);
ImGui::Separator();
auto& logger = Logger::GetInstance();
std::lock_guard<std::mutex> logLock(logger.GetMutex());
for (const auto& log : logger.GetLogs()) { ImGui::TextColored(ImVec4(0.6f, 0.8f, 0.6f, 1.0f), "> %s", log.c_str()); }
}
ImGui::End();
}
};
float UIRenderer::m_userScale = 1.0f;
ImGuiStyle UIRenderer::m_defaultStyle;
bool UIRenderer::m_showDebugger = false;
bool UIRenderer::m_showRadar = true;
bool UIRenderer::m_isHookInstalled = false;
std::string UIRenderer::m_hookStatusMsg = "等待用户指令...";
// ============================================================================
// Dobby Hook 回调区
// ============================================================================
struct ModuleInfo { const char* name; uintptr_t base; };
int DlIterateCallback(struct dl_phdr_info *info, size_t size, void *data) {
auto* modInfo = reinterpret_cast<ModuleInfo*>(data);
if (info->dlpi_name && strstr(info->dlpi_name, modInfo->name)) {
modInfo->base = info->dlpi_addr;
return 1;
}
return 0;
}
uintptr_t GetModuleBase(const char* moduleName) {
ModuleInfo info = {moduleName, 0};
dl_iterate_phdr(DlIterateCallback, &info);
if (info.base == 0) {
if (FILE* f = fopen("/proc/self/maps", "r")) {
char line[512];
while (fgets(line, sizeof(line), f)) {
if (strstr(line, moduleName)) {
uintptr_t start;
if (sscanf(line, "%lx", &start) == 1) { info.base = start; break; }
}
}
fclose(f);
}
}
return info.base;
}
typedef void (*InitActorParamsFunc)(void* x0, void* x1, void* x2, void* x3, void* x4, void* x5, void* x6, void* x7);
static InitActorParamsFunc g_oldInitActorParams = nullptr;
void HookInitActorParams(void* x0, void* x1, void* x2, void* x3, void* x4, void* x5, void* x6, void* x7) {
if (x0 != nullptr && reinterpret_cast<uintptr_t>(x0) > 0x10000000) {
EngineCore::GetInstance().SetActorManager(reinterpret_cast<uintptr_t>(x0));
}
if (g_oldInitActorParams) {
g_oldInitActorParams(x0, x1, x2, x3, x4, x5, x6, x7);
}
}
void InstallHooks() {
if (UIRenderer::m_isHookInstalled) return;
system("su -c 'setenforce 0'");
uintptr_t base = GetModuleBase(kGameModule);
if (!base) {
UIRenderer::m_hookStatusMsg = "ERR: 未找到目标模块 libil2cpp.so";
LOG_ERROR("未找到目标游戏模块!");
return;
}
uintptr_t targetAddr = base + 0x73507bc;
LOG_INFO("SYS_BASE: 0x%lx, TARGET: 0x%lx", base, targetAddr);
void* pageStart = reinterpret_cast<void*>(targetAddr & ~0xFFF);
mprotect(pageStart, 0x2000, PROT_READ | PROT_WRITE | PROT_EXEC);
int ret = DobbyHook(reinterpret_cast<void*>(targetAddr),
reinterpret_cast<void*>(HookInitActorParams),
reinterpret_cast<void**>(&g_oldInitActorParams));
if (ret == 0) {
UIRenderer::m_isHookInstalled = true;
UIRenderer::m_hookStatusMsg = "OK: 神经链路挂载完毕。";
LOG_INFO("手动安装 Hook 成功!");
} else {
UIRenderer::m_hookStatusMsg = "FATAL: 挂载被拒绝(SELinux限制)";
LOG_ERROR("挂钩失败, 返回码: %d", ret);
}
}
// ---------------- EGL & Input Hook ----------------
typedef int (*InputConsumerConsumeFunc)(void* instance, void* factory, bool consumeBatches, int64_t frameTime, uint32_t* outSeq, void** outEvent);
static InputConsumerConsumeFunc g_oldConsume = nullptr;
int HookConsume(void* instance, void* factory, bool consumeBatches, int64_t frameTime, uint32_t* outSeq, void** outEvent) {
int res = g_oldConsume(instance, factory, consumeBatches, frameTime, outSeq, outEvent);
if (res == 0 && outEvent && *outEvent) {
TouchManager::GetInstance().ProcessEvent(reinterpret_cast<AInputEvent*>(*outEvent));
}
return res;
}
typedef EGLBoolean (*EglSwapBuffersFunc)(EGLDisplay dpy, EGLSurface surface);
static EglSwapBuffersFunc g_oldEglSwapBuffers = nullptr;
EGLBoolean HookEglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
EGLint w = 0, h = 0;
eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
if (w <= 0 || h <= 0) return g_oldEglSwapBuffers(dpy, surface);
TouchManager::GetInstance().UpdateRenderSize(static_cast<float>(w), static_cast<float>(h));
auto snapshot = EngineCore::GetInstance().GetLatestSnapshot();
GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport);
GLint depthTest; glGetIntegerv(GL_DEPTH_TEST, &depthTest);
glDisable(GL_DEPTH_TEST);
glViewport(0, 0, w, h);
UIRenderer::RenderFrame(static_cast<float>(w), static_cast<float>(h), snapshot);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
if (depthTest) glEnable(GL_DEPTH_TEST);
return g_oldEglSwapBuffers(dpy, surface);
}
void* BootstrapThread(void*) {
system("su -c 'setenforce 0'");
sleep(2);
void* eglSym = nullptr;
void* eglHandle = dlopen("libEGL.so", RTLD_LAZY);
if (eglHandle) {
eglSym = dlsym(eglHandle, "eglSwapBuffers");
}
if (!eglSym) {
int eglRetries = 0;
while (!(eglSym = DobbySymbolResolver("libEGL.so", "eglSwapBuffers")) && eglRetries < 5) {
sleep(1); eglRetries++;
}
}
if (eglSym) {
DobbyHook(eglSym, reinterpret_cast<void*>(HookEglSwapBuffers), reinterpret_cast<void**>(&g_oldEglSwapBuffers));
}
void* inputSym = nullptr;
int inputRetries = 0;
while (!(inputSym = DobbySymbolResolver("libinput.so", "_ZN7android13InputConsumer7consumeEPNS_26InputEventFactoryInterfaceEblPjPPNS_10InputEventE")) && inputRetries < 5) {
sleep(1); inputRetries++;
}
if (inputSym) {
DobbyHook(inputSym, reinterpret_cast<void*>(HookConsume), reinterpret_cast<void**>(&g_oldConsume));
}
EngineCore::GetInstance().InitWorkerThread();
return nullptr;
}
} // namespace Overlay
__attribute__((constructor)) void InitializePlugin() {
pthread_t threadId;
pthread_create(&threadId, nullptr, Overlay::BootstrapThread, nullptr);
}