imgui-mirror/main.cpp
2026-04-08 02:32:51 +08:00

749 lines
30 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;
// ============================================================================
// 日志系统类
// ============================================================================
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__)
// ============================================================================
// 内存读取工具类 (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;
uint32_t addr1ToAddr2 = 0x8;
uint32_t arrayStart = 0x20;
uint32_t arrayStep = 0x10;
uint32_t itemToAddr3 = 0x0;
uint32_t addr3ToWorld = 0x4C4;
uint32_t addr3ToAddr4 = 0x3B8;
uint32_t addr4ToGold = 0x30;
uint32_t addr4ToHp = 0x38;
uint32_t addr4ToMaxHp = 0x3C;
uint32_t addr4ToLevel = 0x58;
uint32_t addr4ToAddr5 = 0x50;
uint32_t addr5ToMana = 0x24;
uint32_t addr5ToMaxMana = 0x28;
};
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;
uintptr_t addr1 = MemoryReader::SafeRead<uintptr_t>(baseX0 + newSnap->offsets.x0ToAddr1);
newSnap->debug.addr1 = addr1;
if (addr1) {
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) {
uintptr_t itemAddr = addr2 + snap->offsets.arrayStart + (i * snap->offsets.arrayStep);
snap->debug.items[i].base = itemAddr;
uintptr_t addr3 = MemoryReader::SafeRead<uintptr_t>(itemAddr + snap->offsets.itemToAddr3);
snap->debug.items[i].addr3 = addr3;
if (!addr3) continue;
PlayerData p;
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);
p.mapX = p.worldX;
p.mapY = p.worldZ;
uintptr_t addr4 = MemoryReader::SafeRead<uintptr_t>(addr3 + snap->offsets.addr3ToAddr4);
snap->debug.items[i].addr4 = addr4;
if (addr4) {
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);
uintptr_t addr5 = MemoryReader::FastRead<uintptr_t>(addr4 + snap->offsets.addr4ToAddr5);
snap->debug.items[i].addr5 = addr5;
if (addr5) {
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();
// 基础缩放:按 1080p 标准屏幕进行基准推算
float deviceScale = std::min(2.0f, std::max(0.5f, h / 1080.0f));
float finalScale = deviceScale * m_userScale;
ImGuiStyle& style = ImGui::GetStyle();
style = m_defaultStyle;
style.ScaleAllSizes(finalScale); // 缩放 UI 边距
io.FontGlobalScale = finalScale; // 缩放字体
ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();
// 绘制鼠标触控波纹 (全局可见)
if (touch.IsDown()) {
ImGui::GetForegroundDrawList()->AddCircleFilled(io.MousePos, 15.0f * finalScale, IM_COL32(0, 255, 255, 120));
ImGui::GetForegroundDrawList()->AddCircle(io.MousePos, 22.0f * finalScale, IM_COL32(0, 255, 255, 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.90f, 0.95f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.40f, 0.50f, 0.60f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.05f, 0.05f, 0.08f, 0.94f); // 更深、略微透明的背景
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.20f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.12f, 0.98f);
colors[ImGuiCol_Border] = ImVec4(0.00f, 0.60f, 0.80f, 0.60f); // 优雅的科技蓝边框
colors[ImGuiCol_FrameBg] = ImVec4(0.10f, 0.14f, 0.22f, 0.70f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.15f, 0.35f, 0.55f, 0.80f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.20f, 0.45f, 0.75f, 1.00f);
colors[ImGuiCol_TitleBg] = ImVec4(0.03f, 0.03f, 0.05f, 1.00f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.05f, 0.15f, 0.30f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
colors[ImGuiCol_CheckMark] = ImVec4(0.00f, 0.95f, 1.00f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.00f, 0.75f, 1.00f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.00f, 0.95f, 1.00f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.08f, 0.25f, 0.45f, 0.80f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.12f, 0.40f, 0.65f, 1.00f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.20f, 0.55f, 0.85f, 1.00f);
colors[ImGuiCol_Header] = ImVec4(0.10f, 0.25f, 0.45f, 0.80f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.15f, 0.35f, 0.60f, 1.00f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.20f, 0.45f, 0.75f, 1.00f);
colors[ImGuiCol_Separator] = ImVec4(0.15f, 0.30f, 0.50f, 0.80f);
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.08f, 0.15f, 0.25f, 1.00f);
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.10f, 0.30f, 0.50f, 1.00f);
colors[ImGuiCol_TableBorderLight] = ImVec4(0.10f, 0.20f, 0.35f, 0.50f);
// 合理的内边距,告别拥挤
style.WindowPadding = ImVec2(16.0f, 16.0f);
style.FramePadding = ImVec2(12.0f, 8.0f);
style.CellPadding = ImVec2(10.0f, 8.0f);
style.ItemSpacing = ImVec2(12.0f, 10.0f);
// 专业的硬边设计
style.WindowRounding = 4.0f; // 极其微小的圆角提升精致感
style.ChildRounding = 2.0f;
style.FrameRounding = 2.0f;
style.WindowBorderSize = 1.0f;
style.FrameBorderSize = 0.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);
// --- 【致命 Bug 修复】彻底抛弃 MergeMode ---
// 直接加载单源字体,并设定基准大小为 24.0f。
io.Fonts->Clear();
bool fontLoaded = false;
const char* systemFonts[] = {
"/system/fonts/MiSans-Regular.ttf", // 小米/HyperOS
"/system/fonts/SysSans-Hans-Regular.ttf", // 一加/OPPO
"/system/fonts/NotoSansCJKjp-Regular.otc", // 谷歌原生
"/system/fonts/NotoSansSC-Regular.otf",
"/system/fonts/DroidSansFallback.ttf"
};
for (const char* fontPath : systemFonts) {
if (access(fontPath, R_OK) == 0) {
// 加载字体时直接包含常用中文区间
io.Fonts->AddFontFromFileTTF(fontPath, 24.0f, nullptr, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
fontLoaded = true;
break;
}
}
if (!fontLoaded) {
io.Fonts->AddFontDefault(); // 仅当找不到中文字体时才兜底
}
}
static void DrawMainWindow(float w, float h, std::shared_ptr<GameSnapshot>& snap, float finalScale) {
ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver);
// 使用 FirstUseEver 允许用户自己拉伸窗口尺寸,默认给一个漂亮的比例
ImGui::SetNextWindowSize(ImVec2(w * 0.45f, h * 0.55f), ImGuiCond_FirstUseEver);
if (ImGui::Begin("CYBER-CORE // 战术数据终端", nullptr, ImGuiWindowFlags_NoCollapse)) {
// 1. 注入控制区
if (!m_isHookInstalled) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.15f, 0.25f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.2f, 0.35f, 1.0f));
// 使用 -1 让按钮自动撑满全宽,极其规整
if (ImGui::Button("[ ⚠ ] 核心态神经链路未连接 - 点击注入", ImVec2(-1, 0))) {
extern void InstallHooks();
InstallHooks();
}
ImGui::PopStyleColor(2);
} else {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.1f, 0.6f, 0.2f, 0.4f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.1f, 0.6f, 0.2f, 0.4f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.6f, 0.2f, 0.4f));
ImGui::Button("✅ [ ONLINE ] 链路已建立", ImVec2(-1, 0));
ImGui::PopStyleColor(3);
}
// 提示回执
if (!m_hookStatusMsg.empty() && m_hookStatusMsg != "等待用户指令...") {
ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), "SYS_LOG: %s", m_hookStatusMsg.c_str());
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// 2. 战术配置开关
ImGui::Checkbox("HUD 全息雷达", &m_showRadar); ImGui::SameLine(0, 30.0f * finalScale);
ImGui::Checkbox("DBG 内存探针", &m_showDebugger);
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// 3. 核心数据表 (专业复活)
ImGui::TextColored(ImVec4(0.0f, 1.0f, 1.0f, 1.0f), "DATA_STREAM // 实战数据解析");
if (!snap->inMatch || snap->players.empty()) {
ImGui::TextColored(ImVec4(1, 0.5f, 0, 1), ">> 等待战场实体同步...");
} else {
// 使用 ImGui 官方 Table,自带列对齐、斑马纹,极度专业
if (ImGui::BeginTable("PlayersTable", 5,
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp))
{
ImGui::TableSetupColumn("LVL", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("HP");
ImGui::TableSetupColumn("MP");
ImGui::TableSetupColumn("GOLD");
ImGui::TableSetupColumn("MAP(X,Y)");
ImGui::TableHeadersRow();
for (const auto& p : snap->players) {
ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(1, 0.8f, 0, 1), "%d", p.level);
ImGui::TableNextColumn(); ImGui::Text("%d/%d", p.hp, p.maxHp);
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(0, 0.8f, 1, 1), "%d/%d", p.mana, p.maxMana);
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1), "%d", p.gold);
ImGui::TableNextColumn(); ImGui::Text("%.0f, %.0f", p.mapX, p.mapY);
}
ImGui::EndTable();
}
}
// 4. 右下角全局缩放滑块 (完美布局版)
ImGui::Dummy(ImVec2(0.0f, ImGui::GetContentRegionAvail().y - ImGui::GetFrameHeightWithSpacing() - ImGui::GetStyle().ItemSpacing.y)); // 把内容挤到底部
ImGui::Separator();
ImGui::AlignTextToFramePadding();
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.8f, 1.0f), "[ STABLE ]");
ImGui::SameLine();
ImGui::TextDisabled("|");
ImGui::SameLine();
// 自动占据剩余全部空间的滑动条
ImGui::PushItemWidth(-1);
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::PopItemWidth();
}
ImGui::End();
}
static void DrawRadar(float w, float h, std::shared_ptr<GameSnapshot>& snap, float finalScale) {
// 雷达尺寸严格锁定为正方形
float radarSize = 300.0f * finalScale;
ImGui::SetNextWindowPos(ImVec2(w - radarSize - 40, 50), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(radarSize, radarSize), ImGuiCond_Always);
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_NoTitleBar | ImGuiWindowFlags_NoResize)) {
ImVec2 p0 = ImGui::GetWindowPos();
ImVec2 size = ImGui::GetWindowSize();
ImDrawList* dl = ImGui::GetWindowDrawList();
ImVec2 center = ImVec2(p0.x + size.x * 0.5f, p0.y + size.y * 0.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;
if (nX > 0 && nX < 1 && nY > 0 && nY < 1) {
float rX = p0.x + (nX * size.x);
float rY = p0.y + size.y - (nY * size.y); // Y轴反转,适应小地图习惯
ImVec2 point(rX, rY);
dl->AddCircleFilled(point, 6.0f * finalScale, IM_COL32(255, 50, 50, 80)); // 荧光拖尾
dl->AddCircleFilled(point, 3.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);
ImGui::SetNextWindowSize(ImVec2(w * 0.4f, h * 0.6f), ImGuiCond_FirstUseEver);
if (ImGui::Begin("MEMORY_PROBE // 内存深度探查")) {
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), "%-20s: %d (0x%X)", label, val, val);
ImGui::SameLine(ImGui::GetWindowWidth() - 120.0f);
if (ImGui::Button("-4", ImVec2(40, 0))) { if (val >= 4) val -= 4; else val = 0; } ImGui::SameLine();
if (ImGui::Button("+4", ImVec2(40, 0))) val += 4;
ImGui::PopID();
};
ImGui::TextColored(ImVec4(1, 0.8f, 0, 1), "当前 x0 基址: 0x%lx", snap->debug.baseX0);
ImGui::Separator();
drawOffsetBtn("x0_to_addr1", offsets.x0ToAddr1);
drawOffsetBtn("addr1_to_addr2", 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());
ImGui::BeginChild("LogArea", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar);
for (const auto& log : logger.GetLogs()) {
ImGui::TextColored(ImVec4(0.6f, 0.8f, 0.6f, 1.0f), "> %s", log.c_str());
}
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
}
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);
}