imgui-mirror/main.cpp
2026-04-07 04:14:25 +08:00

548 lines
22 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 <stdio.h>
#include <algorithm>
#include <atomic>
#include <vector>
#include <mutex>
#include <thread>
#include <chrono>
#include <fcntl.h>
#include <dirent.h>
#include <cstdarg>
#include <cstring>
#include "dobby.h"
#include "imgui.h"
#include "imgui_impl_android.h"
#include "imgui_impl_opengl3.h"
#define TAG "IL2CPP_Drive"
#define GAME_MODULE "libil2cpp.so"
// 日志缓冲区(线程安全,循环队列)
#define MAX_LOG_LINES 200
#define MAX_LOG_LEN 256
static std::mutex g_LogMutex;
static char g_LogBuffer[MAX_LOG_LINES][MAX_LOG_LEN];
static int g_LogHead = 0;
static int g_LogCount = 0;
void AddLog(const char* fmt, ...) {
char buf[MAX_LOG_LEN];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
std::lock_guard<std::mutex> lock(g_LogMutex);
strncpy(g_LogBuffer[g_LogHead], buf, MAX_LOG_LEN - 1);
g_LogBuffer[g_LogHead][MAX_LOG_LEN - 1] = '\0';
g_LogHead = (g_LogHead + 1) % MAX_LOG_LINES;
if (g_LogCount < MAX_LOG_LINES) g_LogCount++;
// 同时也输出到 logcat,方便调试
__android_log_print(ANDROID_LOG_INFO, TAG, "%s", buf);
}
// 全局变量
static bool g_Initialized = false;
static bool g_FontLoaded = false;
static bool g_ShowLogWindow = true; // 默认显示日志窗口
struct {
float x = 0.0f;
float y = 0.0f;
std::atomic<bool> down{false};
float renderW = 0.0f;
float renderH = 0.0f;
float physW = 3392.0f;
float physH = 2400.0f;
bool isSizeAutoDetected = false;
float offsetX = 0.0f;
float offsetY = 0.0f;
} g_Touch;
static float g_DynamicScale = 1.0f;
static float g_BaseWindowHeight = 0.0f;
static float g_UserUIScale = 1.0f;
static uintptr_t g_GameBase = 0;
static uintptr_t g_HookAddr_InitActor = 0;
static uintptr_t g_HookAddr_ClearActor = 0;
struct DynamicOffsets {
uint32_t x0_to_addr1 = 0x20;
uint32_t addr1_to_addr2 = 0x10;
uint32_t item_to_addr3 = 0x18;
uint32_t addr3_to_addr4 = 0x30;
uint32_t addr3_to_addr5 = 0x100;
uint32_t addr3_to_addr6 = 0x70;
uint32_t prop_gold = 0x20;
uint32_t prop_hp = 0x30;
uint32_t prop_maxhp = 0x32;
uint32_t prop_level = 0x50;
uint32_t prop_mana = 0x28;
};
DynamicOffsets g_Offsets;
std::mutex g_OffsetMutex;
static int g_MemFd = -1;
template <typename T>
T SafeRead(uintptr_t address) {
T value{};
if (address < 0x10000000 || address > 0x00007FFFFFFFFFFF) return value;
if (g_MemFd == -1) g_MemFd = open("/proc/self/mem", O_RDONLY);
if (g_MemFd >= 0) pread(g_MemFd, &value, sizeof(T), address);
return value;
}
struct PlayerData {
int hp = 0, maxHp = 0, mana = 0, gold = 0, level = 0;
float worldX = 0, worldY = 0, worldZ = 0;
float mapX = 0, mapY = 0;
};
struct DebugChain {
uintptr_t baseX0 = 0;
uintptr_t addr1 = 0;
uintptr_t addr2 = 0;
struct Item {
uintptr_t base = 0, addr3 = 0, addr4 = 0, addr5 = 0, addr6 = 0;
} items[15];
};
struct GameSnapshot {
bool inMatch = false;
DynamicOffsets offsets;
DebugChain debug;
std::vector<PlayerData> players;
};
std::mutex g_SnapshotMutex;
GameSnapshot g_CurrentSnapshot;
std::atomic<uintptr_t> g_ActorManager{0};
// 后台数据读取线程
void DataWorkerThread() {
AddLog("[DataThread] 启动数据线程");
while (true) {
GameSnapshot newSnap;
DynamicOffsets currOff;
{
std::lock_guard<std::mutex> lock(g_OffsetMutex);
currOff = g_Offsets;
}
newSnap.offsets = currOff;
uintptr_t baseX0 = g_ActorManager.load();
if (baseX0 != 0) {
newSnap.inMatch = true;
newSnap.debug.baseX0 = baseX0;
uintptr_t addr1 = SafeRead<uintptr_t>(baseX0 + currOff.x0_to_addr1);
newSnap.debug.addr1 = addr1;
if (addr1) {
uintptr_t addr2 = SafeRead<uintptr_t>(addr1 + currOff.addr1_to_addr2);
newSnap.debug.addr2 = addr2;
if (addr2) {
for (int i = 0; i < 15; ++i) {
uintptr_t item = SafeRead<uintptr_t>(addr2 + i * 0x8);
newSnap.debug.items[i].base = item;
if (!item) continue;
uintptr_t addr3 = SafeRead<uintptr_t>(item + currOff.item_to_addr3);
newSnap.debug.items[i].addr3 = addr3;
if (!addr3) continue;
PlayerData p;
uintptr_t addr4 = SafeRead<uintptr_t>(addr3 + currOff.addr3_to_addr4);
newSnap.debug.items[i].addr4 = addr4;
if (addr4) {
p.gold = SafeRead<int32_t>(addr4 + currOff.prop_gold);
p.hp = SafeRead<int32_t>(addr4 + currOff.prop_hp);
p.maxHp = SafeRead<int16_t>(addr4 + currOff.prop_maxhp);
p.level = SafeRead<int32_t>(addr4 + currOff.prop_level);
}
uintptr_t addr5 = SafeRead<uintptr_t>(addr3 + currOff.addr3_to_addr5);
newSnap.debug.items[i].addr5 = addr5;
if (addr5) {
p.worldX = SafeRead<float>(addr5 + 0x0);
p.worldZ = SafeRead<float>(addr5 + 0x4);
p.worldY = SafeRead<float>(addr5 + 0x8);
p.mana = SafeRead<int32_t>(addr5 + currOff.prop_mana);
}
uintptr_t addr6 = SafeRead<uintptr_t>(addr3 + currOff.addr3_to_addr6);
newSnap.debug.items[i].addr6 = addr6;
if (addr6) {
p.mapX = SafeRead<float>(addr6 + 0x50);
p.mapY = SafeRead<float>(addr6 + 0x54);
}
if (p.maxHp > 0 && p.maxHp < 50000)
newSnap.players.push_back(p);
}
}
}
}
{
std::lock_guard<std::mutex> lock(g_SnapshotMutex);
g_CurrentSnapshot = newSnap;
}
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
}
uintptr_t GetModuleBase(const char* module_name) {
FILE* fp = fopen("/proc/self/maps", "r");
if (!fp) return 0;
char line[512];
uintptr_t base = 0;
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, module_name) && strstr(line, "r-xp")) {
sscanf(line, "%lx", &base);
break;
}
}
fclose(fp);
return base;
}
// Hook 函数
void (*old_InitActorParams)(void* x0, void* x1, void* x2, void* x3);
void hook_InitActorParams(void* x0, void* x1, void* x2, void* x3) {
AddLog("[HOOK] InitActorParams called! x0 = %p", x0);
if (x0 != nullptr) {
g_ActorManager.store((uintptr_t)x0);
uintptr_t test_addr = SafeRead<uintptr_t>((uintptr_t)x0 + g_Offsets.x0_to_addr1);
AddLog("[HOOK] x0+0x%x = %p", g_Offsets.x0_to_addr1, (void*)test_addr);
} else {
AddLog("[HOOK] x0 is null!");
}
old_InitActorParams(x0, x1, x2, x3);
}
void (*old_ClearActor)(void* x0);
void hook_ClearActor(void* x0) {
AddLog("[HOOK] ClearActor called! x0 = %p", x0);
g_ActorManager.store(0);
old_ClearActor(x0);
}
// 辅助函数
void AutoFetchDeviceResolution() {
FILE* pipe = popen("wm size", "r");
if (!pipe) return;
char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
int w = 0, h = 0;
if (sscanf(buffer, "Physical size: %dx%d", &w, &h) == 2 ||
sscanf(buffer, "Override size: %dx%d", &w, &h) == 2) {
g_Touch.physW = (float)std::max(w, h);
g_Touch.physH = (float)std::min(w, h);
g_Touch.isSizeAutoDetected = true;
AddLog("[Auto] 物理分辨率: %.0fx%.0f", g_Touch.physW, g_Touch.physH);
}
}
pclose(pipe);
}
void handle_android_event(AInputEvent* event) {
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
int32_t action = AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK;
float autoScaleX = g_Touch.renderW / g_Touch.physW;
float autoScaleY = g_Touch.renderH / g_Touch.physH;
g_Touch.x = (AMotionEvent_getX(event, 0) * autoScaleX) + g_Touch.offsetX;
g_Touch.y = (AMotionEvent_getY(event, 0) * autoScaleY) + g_Touch.offsetY;
if (action == AMOTION_EVENT_ACTION_DOWN) g_Touch.down.store(true);
else if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) g_Touch.down.store(false);
}
}
typedef int (*p_InputConsumer_consume)(void* instance, void* factory, bool consumeBatches, int64_t frameTime, uint32_t* outSeq, void** outEvent);
static p_InputConsumer_consume old_consume = nullptr;
int hook_consume(void* instance, void* factory, bool consumeBatches, int64_t frameTime, uint32_t* outSeq, void** outEvent) {
int res = old_consume(instance, factory, consumeBatches, frameTime, outSeq, outEvent);
if (res == 0 && outEvent && *outEvent) handle_android_event((AInputEvent*)(*outEvent));
return res;
}
typedef EGLBoolean (*p_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface);
static p_eglSwapBuffers old_eglSwapBuffers = nullptr;
void DrawPointerDebugger(const GameSnapshot& snap, float w, float h);
void DrawLogWindow();
EGLBoolean hook_eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
EGLint w, h;
eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
if (w <= 0 || h <= 0) return old_eglSwapBuffers(dpy, surface);
g_Touch.renderW = (float)w;
g_Touch.renderH = (float)h;
GameSnapshot snapshot;
{
std::lock_guard<std::mutex> lock(g_SnapshotMutex);
snapshot = g_CurrentSnapshot;
}
if (!g_Initialized) {
ImGui::CreateContext();
ImGui_ImplOpenGL3_Init("#version 300 es");
ImGuiIO& io = ImGui::GetIO();
const char* systemFonts[] = {
"/system/fonts/SysSans-Hans-Regular.ttf",
"/system/fonts/NotoSansCJKjp-Regular.otc",
"/system/fonts/NotoSansSC-Regular.otf",
"/system/fonts/DroidSansFallback.ttf"
};
for (int i = 0; i < 4; i++) {
if (access(systemFonts[i], R_OK) == 0) {
io.Fonts->AddFontFromFileTTF(systemFonts[i], 32.0f, NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
g_FontLoaded = true;
AddLog("[ImGui] 加载字体成功: %s", systemFonts[i]);
break;
}
}
g_Initialized = true;
AddLog("[ImGui] 初始化完成");
}
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2(g_Touch.renderW, g_Touch.renderH);
io.MousePos = ImVec2(g_Touch.x, g_Touch.y);
io.MouseDown[0] = g_Touch.down.load();
float deviceScale = (float)h / 1000.0f;
if (deviceScale < 1.0f) deviceScale = 1.0f;
float finalScale = deviceScale * g_DynamicScale * g_UserUIScale;
static ImGuiStyle default_style = ImGui::GetStyle();
ImGuiStyle& style = ImGui::GetStyle();
style = default_style;
style.ScaleAllSizes(1.2f * finalScale);
style.WindowRounding = 12.0f;
style.WindowMinSize = ImVec2(100.0f, 100.0f);
io.FontGlobalScale = 1.2f * finalScale;
ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();
if (g_Touch.down.load())
ImGui::GetForegroundDrawList()->AddCircleFilled(io.MousePos, 20.0f, IM_COL32(0, 255, 0, 200));
if (g_BaseWindowHeight == 0.0f) g_BaseWindowHeight = h * 0.45f;
ImGui::SetNextWindowPos(ImVec2(30, 30), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(w * 0.6f, g_BaseWindowHeight), ImGuiCond_FirstUseEver);
static bool show_pointer_debugger = true;
static bool show_radar = true;
if (ImGui::Begin("IL2CPP 透视框架 (偏移可调)")) {
if (!g_FontLoaded) ImGui::TextColored(ImVec4(1,0,0,1), "警告: 无法加载系统字库");
float currentHeight = ImGui::GetWindowHeight();
g_DynamicScale = currentHeight / g_BaseWindowHeight;
if (g_DynamicScale < 0.4f) g_DynamicScale = 0.4f;
if (g_DynamicScale > 3.0f) g_DynamicScale = 3.0f;
if (ImGui::CollapsingHeader("基础校准")) {
ImGui::SliderFloat("菜单缩放", &g_UserUIScale, 0.5f, 2.0f);
ImGui::Text("偏移 X:%.1f Y:%.1f", g_Touch.offsetX, g_Touch.offsetY);
if (ImGui::Button("-10##x")) g_Touch.offsetX -= 10; ImGui::SameLine();
if (ImGui::Button("+10##x")) g_Touch.offsetX += 10; ImGui::SameLine();
if (ImGui::Button("-10##y")) g_Touch.offsetY -= 10; ImGui::SameLine();
if (ImGui::Button("+10##y")) g_Touch.offsetY += 10;
}
ImGui::Separator();
ImGui::Checkbox("雷达", &show_radar); ImGui::SameLine();
ImGui::Checkbox("调试器", &show_pointer_debugger); ImGui::SameLine();
ImGui::Checkbox("日志窗口", &g_ShowLogWindow);
ImGui::Separator();
ImGui::TextColored(ImVec4(0,1,0,1), "状态: %s", snapshot.inMatch ? "局内" : "大厅");
if (ImGui::BeginTable("PlayersTable", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
ImGui::TableSetupColumn("等级");
ImGui::TableSetupColumn("血量");
ImGui::TableSetupColumn("蓝量");
ImGui::TableSetupColumn("金币");
ImGui::TableSetupColumn("雷达坐标");
ImGui::TableHeadersRow();
for (const auto& p : snapshot.players) {
ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::Text("Lv.%d", p.level);
ImGui::TableNextColumn(); ImGui::Text("%d/%d", p.hp, p.maxHp);
ImGui::TableNextColumn(); ImGui::Text("%d", p.mana);
ImGui::TableNextColumn(); ImGui::Text("%d", p.gold);
ImGui::TableNextColumn(); ImGui::Text("%.0f, %.0f", p.mapX, p.mapY);
}
ImGui::EndTable();
}
}
ImGui::End();
if (show_pointer_debugger) DrawPointerDebugger(snapshot, w, h);
if (show_radar && snapshot.inMatch) {
ImGui::SetNextWindowPos(ImVec2(w - 350, 30), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(300, 300), ImGuiCond_FirstUseEver);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0,0,0,0.5f));
if (ImGui::Begin("Radar", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize)) {
ImVec2 p0 = ImGui::GetCursorScreenPos();
ImVec2 size = ImGui::GetContentRegionAvail();
ImDrawList* dl = ImGui::GetWindowDrawList();
dl->AddRect(p0, ImVec2(p0.x+size.x, p0.y+size.y), IM_COL32(255,255,255,100));
for (const auto& p : snapshot.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)
dl->AddCircleFilled(ImVec2(rx, ry), 5.0f*finalScale, IM_COL32(255,0,0,255));
}
}
ImGui::End();
ImGui::PopStyleColor();
}
if (g_ShowLogWindow) DrawLogWindow();
ImGui::Render();
GLint v[4]; glGetIntegerv(GL_VIEWPORT, v);
GLint d; glGetIntegerv(GL_DEPTH_TEST, &d);
glDisable(GL_DEPTH_TEST);
glViewport(0, 0, w, h);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glViewport(v[0], v[1], v[2], v[3]);
if (d) glEnable(GL_DEPTH_TEST);
return old_eglSwapBuffers(dpy, surface);
}
void DrawPointerDebugger(const GameSnapshot& snap, float w, float h) {
ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(w * 0.55f, h * 0.8f), ImGuiCond_FirstUseEver);
if (ImGui::Begin("指针断点排错器 (IL2CPP)")) {
if (ImGui::CollapsingHeader("动态偏移修改面板", ImGuiTreeNodeFlags_DefaultOpen)) {
std::lock_guard<std::mutex> lock(g_OffsetMutex);
auto drawOffsetBtn = [](const char* label, uint32_t& val) {
ImGui::PushID(&val);
ImGui::TextColored(ImVec4(0.5f,0.8f,1.0f,1.0f), "%s -> %d (0x%X)", label, val, val);
if (ImGui::Button("-8")) { if(val>=8) val-=8; else val=0; } ImGui::SameLine();
if (ImGui::Button("-4")) { if(val>=4) val-=4; else val=0; } ImGui::SameLine();
if (ImGui::Button("-1")) { if(val>=1) val-=1; else val=0; } ImGui::SameLine();
if (ImGui::Button("+1")) val+=1; ImGui::SameLine();
if (ImGui::Button("+4")) val+=4; ImGui::SameLine();
if (ImGui::Button("+8")) val+=8;
ImGui::Separator();
ImGui::PopID();
};
drawOffsetBtn("addr1 (x0+?)", g_Offsets.x0_to_addr1);
drawOffsetBtn("英雄数组 (addr1+?)", g_Offsets.addr1_to_addr2);
drawOffsetBtn("实体数据 (item+?)", g_Offsets.item_to_addr3);
drawOffsetBtn("属性区 (addr3+?)", g_Offsets.addr3_to_addr4);
drawOffsetBtn("坐标区 (addr3+?)", g_Offsets.addr3_to_addr5);
drawOffsetBtn("小地图 (addr3+?)", g_Offsets.addr3_to_addr6);
drawOffsetBtn("金币偏移", g_Offsets.prop_gold);
drawOffsetBtn("血量偏移", g_Offsets.prop_hp);
drawOffsetBtn("最大血量偏移", g_Offsets.prop_maxhp);
drawOffsetBtn("等级偏移", g_Offsets.prop_level);
drawOffsetBtn("蓝量偏移", g_Offsets.prop_mana);
}
ImGui::Separator();
ImGui::TextColored(ImVec4(0,1,1,1), "[核心库] %s 基址: 0x%lx", GAME_MODULE, g_GameBase);
ImGui::TextColored(ImVec4(0,1,1,1), "[Hook] InitActor RVA 0x73507bc -> 0x%lx", g_HookAddr_InitActor);
ImGui::TextColored(ImVec4(0,1,1,1), "[Hook] ClearActor RVA 0x734bc10 -> 0x%lx", g_HookAddr_ClearActor);
ImGui::Separator();
const auto& d = snap.debug;
const auto& off = snap.offsets;
ImGui::TextColored(d.baseX0 ? ImVec4(0,1,0,1) : ImVec4(1,0,0,1), "[起点] x0: 0x%lx", d.baseX0);
ImGui::TextColored(d.addr1 ? ImVec4(0,1,0,1) : ImVec4(1,0,0,1), " └─ [+%d] addr1: 0x%lx", off.x0_to_addr1, d.addr1);
ImGui::TextColored(d.addr2 ? ImVec4(0,1,0,1) : ImVec4(1,0,0,1), " └─ [+%d] addr2: 0x%lx", off.addr1_to_addr2, d.addr2);
if (d.addr2) {
for (int i=0; i<15; i++) {
if (!d.items[i].base) continue;
char node[64]; snprintf(node, sizeof(node), "[%d] 0x%lx", i, d.items[i].base);
if (ImGui::TreeNodeEx(node, ImGuiTreeNodeFlags_Framed)) {
ImGui::TextColored(d.items[i].addr3 ? ImVec4(0,1,0,1) : ImVec4(1,0,0,1), " └─ [+%d] addr3: 0x%lx", off.item_to_addr3, d.items[i].addr3);
if (d.items[i].addr3) {
ImGui::TextColored(d.items[i].addr4 ? ImVec4(0,1,0,1) : ImVec4(1,0,0,1), " ├─ [+%d] addr4: 0x%lx", off.addr3_to_addr4, d.items[i].addr4);
ImGui::TextColored(d.items[i].addr5 ? ImVec4(0,1,0,1) : ImVec4(1,0,0,1), " ├─ [+%d] addr5: 0x%lx", off.addr3_to_addr5, d.items[i].addr5);
ImGui::TextColored(d.items[i].addr6 ? ImVec4(0,1,0,1) : ImVec4(1,0,0,1), " └─ [+%d] addr6: 0x%lx", off.addr3_to_addr6, d.items[i].addr6);
}
ImGui::TreePop();
}
}
}
}
ImGui::End();
}
void DrawLogWindow() {
ImGui::SetNextWindowPos(ImVec2(50, 200), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(500, 300), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("调试日志", &g_ShowLogWindow)) {
ImGui::End();
return;
}
ImGui::BeginChild("LogScroll", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
{
std::lock_guard<std::mutex> lock(g_LogMutex);
int start = (g_LogHead - g_LogCount + MAX_LOG_LINES) % MAX_LOG_LINES;
for (int i = 0; i < g_LogCount; i++) {
int idx = (start + i) % MAX_LOG_LINES;
ImGui::TextUnformatted(g_LogBuffer[idx]);
}
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
}
ImGui::EndChild();
ImGui::End();
}
// 延迟初始化线程
void* DelayedHookThread(void*) {
AddLog("[Init] 插件启动,开始 Hook 渲染与输入...");
void* egl = DobbySymbolResolver("libEGL.so", "eglSwapBuffers");
if (egl) {
int ret = DobbyHook(egl, (void*)hook_eglSwapBuffers, (void**)&old_eglSwapBuffers);
AddLog("[EGL] DobbyHook 结果: %d", ret);
} else {
AddLog("[EGL] 未找到 eglSwapBuffers");
}
void* ins = DobbySymbolResolver("libinput.so", "_ZN7android13InputConsumer7consumeEPNS_26InputEventFactoryInterfaceEblPjPPNS_10InputEventE");
if (ins) {
int ret = DobbyHook(ins, (void*)hook_consume, (void**)&old_consume);
AddLog("[Input] DobbyHook 结果: %d", ret);
} else {
AddLog("[Input] 未找到 InputConsumer::consume");
}
std::thread(DataWorkerThread).detach();
AddLog("[Init] 等待游戏模块加载...");
while (true) {
uintptr_t base = GetModuleBase(GAME_MODULE);
if (base) {
g_GameBase = base;
g_HookAddr_InitActor = base + 0x73507bc;
g_HookAddr_ClearActor = base + 0x734bc10;
AddLog("[Game] libil2cpp.so 基址: 0x%lx", base);
AddLog("[Game] InitActor 绝对地址: 0x%lx", g_HookAddr_InitActor);
AddLog("[Game] ClearActor 绝对地址: 0x%lx", g_HookAddr_ClearActor);
int ret1 = DobbyHook((void*)g_HookAddr_InitActor, (void*)hook_InitActorParams, (void**)&old_InitActorParams);
int ret2 = DobbyHook((void*)g_HookAddr_ClearActor, (void*)hook_ClearActor, (void**)&old_ClearActor);
AddLog("[Game] Hook InitActor 结果: %d, ClearActor: %d", ret1, ret2);
break;
}
sleep(1);
}
AutoFetchDeviceResolution();
AddLog("[Init] 所有初始化完成,等待对局...");
return nullptr;
}
__attribute__((constructor)) void init_plugin() {
pthread_t t;
pthread_create(&t, nullptr, DelayedHookThread, nullptr);
}