/**
* Frida综合Hook脚本 - 字符串比较绕过 + Lua解密监控
* 功能1: 强制sub_7F9EF字符串比较函数返回1 (优先执行)
* 功能2: 监控sub_13860A函数的LuaState结构体和Lua字节码解密
* 目标进程: com.test.mlgm:sc
* 执行顺序: 先绕过字符串验证,再监控Lua解密过程
*/
console.log("[+] ========================================");
console.log("[+] 综合Hook脚本启动");
console.log("[+] 功能: 字符串绕过 + Lua解密监控");
console.log("[+] ========================================");
// ==================== 全局配置 ====================
const GLOBAL_CONFIG = {
// 模块配置
TARGET_MODULE_NAMES: [
"libengine.so",
"libnative.so",
"libengine_exported_functions.so"
],
// 函数配置
STRING_COMPARE_FUNCTION: {
name: "sub_7F9EF",
offset: 0x7F9EF,
description: "字符串比较函数"
},
LUA_DECRYPT_FUNCTION: {
name: "sub_13860A",
offset: 0x13860A,
description: "Lua解密函数"
},
// 日志配置
LOGGING: {
enableDetailedStringLog: false, // 减少字符串hook的详细日志
enableDetailedLuaLog: true, // 启用Lua详细日志
enableHexDump: true, // 启用hex dump
maxDumpSize: 500 // 最大dump大小
},
// 安全配置
SAFETY: {
maxStringReadLength: 32, // 字符串读取最大长度
maxMemoryDumpSize: 1000, // 内存dump最大大小
enableSafeMemoryAccess: true // 启用安全内存访问
}
};
// ==================== 全局状态管理 ====================
const GLOBAL_STATE = {
targetModule: null,
stringHookInstalled: false,
luaHookInstalled: false,
hookStats: {
stringCompareCalls: 0,
luaDecryptCalls: 0,
startTime: Date.now()
}
};
// ==================== 通用工具函数 ====================
// 查找目标模块
function findTargetModule() {
console.log("[+] 正在查找目标模块...");
for (const moduleName of GLOBAL_CONFIG.TARGET_MODULE_NAMES) {
const module = Process.findModuleByName(moduleName);
if (module) {
console.log(`[+] 找到目标模块: ${moduleName}`);
console.log(`[+] 模块基址: ${module.base}`);
console.log(`[+] 模块大小: 0x${module.size.toString(16)}`);
return module;
}
}
console.log("[!] 未找到任何目标模块,尝试的模块名:");
GLOBAL_CONFIG.TARGET_MODULE_NAMES.forEach(name => console.log(`[!] - ${name}`));
// 列出相关模块
console.log("[+] 当前已加载的相关模块:");
Process.enumerateModules().forEach(mod => {
if (mod.name.toLowerCase().includes('engine') ||
mod.name.toLowerCase().includes('native') ||
mod.name.toLowerCase().includes('lib')) {
console.log(`[+] - ${mod.name} (${mod.base})`);
}
});
return null;
}
// 等待模块加载
function waitForModule() {
return new Promise((resolve) => {
const checkModule = () => {
const module = findTargetModule();
if (module) {
resolve(module);
} else {
setTimeout(checkModule, 500);
}
};
checkModule();
});
}
// 安全内存读取函数
function safeReadMemory(address, size, description) {
try {
if (!address || address.isNull()) {
if (GLOBAL_CONFIG.LOGGING.enableDetailedLuaLog) {
console.log(`[!] ${description}: 地址为NULL`);
}
return null;
}
return Memory.readByteArray(address, size);
} catch (e) {
if (GLOBAL_CONFIG.LOGGING.enableDetailedLuaLog) {
console.log(`[!] ${description}: 内存读取失败 - ${e.message}`);
}
return null;
}
}
function safeReadPointer(address, description) {
try {
if (!address || address.isNull()) {
return ptr(0);
}
return address.readPointer();
} catch (e) {
if (GLOBAL_CONFIG.LOGGING.enableDetailedLuaLog) {
console.log(`[!] ${description}: 指针读取失败 - ${e.message}`);
}
return ptr(0);
}
}
function safeReadU32(address, description) {
try {
if (!address || address.isNull()) {
return 0;
}
return address.readU32();
} catch (e) {
if (GLOBAL_CONFIG.LOGGING.enableDetailedLuaLog) {
console.log(`[!] ${description}: U32读取失败 - ${e.message}`);
}
return 0;
}
}
function safeHexdump(address, size, description) {
if (!GLOBAL_CONFIG.LOGGING.enableHexDump) return;
try {
if (!address || address.isNull()) {
console.log(`[!] ${description}: 地址为NULL,无法进行hexdump`);
return;
}
console.log(`[+] ${description} hexdump:`);
console.log(hexdump(address, {
offset: 0,
length: Math.min(size, GLOBAL_CONFIG.SAFETY.maxMemoryDumpSize),
header: true,
ansi: true
}));
} catch (e) {
console.log(`[!] ${description}: hexdump失败 - ${e.message}`);
}
}
// ==================== 字符串比较Hook (优先级1) ====================
function installStringCompareHook(module) {
try {
const functionOffset = GLOBAL_CONFIG.STRING_COMPARE_FUNCTION.offset;
const functionAddress = module.base.add(functionOffset);
console.log(`[+] 安装字符串比较Hook: ${GLOBAL_CONFIG.STRING_COMPARE_FUNCTION.name}`);
console.log(`[+] Hook地址: ${functionAddress} (偏移: 0x${functionOffset.toString(16)})`);
Interceptor.attach(functionAddress, {
onEnter: function(args) {
GLOBAL_STATE.hookStats.stringCompareCalls++;
// 保存参数
this.str1 = args[0];
this.str2 = args[1];
if (GLOBAL_CONFIG.LOGGING.enableDetailedStringLog) {
console.log(`[STR] ${GLOBAL_CONFIG.STRING_COMPARE_FUNCTION.name} 调用 #${GLOBAL_STATE.hookStats.stringCompareCalls}`);
console.log(`[STR] 参数1: ${this.str1}, 参数2: ${this.str2}`);
// 获取调用者信息(简化版)
try {
const callerAddress = this.returnAddress;
const callerModule = Process.findModuleByAddress(callerAddress);
if (callerModule) {
const offset = callerAddress.sub(callerModule.base);
console.log(`[STR] 调用者: ${callerModule.name}+0x${offset.toString(16)}`);
}
} catch (e) {
// 忽略调用者信息获取错误
}
// 尝试读取字符串内容(简化版)
const readSimpleString = (strPtr, label) => {
try {
if (!strPtr || strPtr.isNull()) return;
const firstByte = strPtr.readU8();
const isLongString = firstByte & 1;
let dataPtr = isLongString ? strPtr.add(8).readPointer() : strPtr.add(1);
let content = dataPtr.readCString(16) || "无法读取";
console.log(`[STR] ${label}: "${content}" (${isLongString ? '长' : '短'}字符串)`);
} catch (e) {
console.log(`[STR] ${label}: 读取失败`);
}
};
readSimpleString(this.str1, "字符串1");
readSimpleString(this.str2, "字符串2");
}
},
onLeave: function(retval) {
const originalReturn = retval.toInt32();
// 强制返回1
retval.replace(ptr(1));
if (GLOBAL_CONFIG.LOGGING.enableDetailedStringLog) {
console.log(`[STR] 原始返回值: ${originalReturn} -> 强制返回值: 1`);
} else {
// 简化日志:只在返回值不为1时输出
if (originalReturn !== 1) {
console.log(`[STR] 字符串比较被绕过 (${originalReturn} -> 1) [调用#${GLOBAL_STATE.hookStats.stringCompareCalls}]`);
}
}
}
});
GLOBAL_STATE.stringHookInstalled = true;
console.log(`[+] 字符串比较Hook安装成功!`);
return true;
} catch (error) {
console.log(`[!] 字符串比较Hook安装失败: ${error.message}`);
return false;
}
}
// ==================== LuaState结构体定义 ====================
const LuaStateStruct = {
PADDING_OFFSET: 0x00,
BYTECODE_PTR_OFFSET: 0x1C,
BYTECODE_SIZE_OFFSET: 0x20,
CURRENT_EXEC_POS_OFFSET: 0x24,
PADDING2_OFFSET: 0x28,
INIT_FLAG_OFFSET: 0x2C,
SCRIPT_START_PTR_OFFSET: 0x30,
TOTAL_SCRIPT_SIZE_OFFSET: 0x34
};
// ==================== Lua解密Hook (优先级2) ====================
function parseLuaState(luaStatePtr, paramName, callNumber) {
if (!GLOBAL_CONFIG.LOGGING.enableDetailedLuaLog) return;
console.log(`\n[LUA] ========== 解析${paramName} LuaState结构体 (调用#${callNumber}) ==========`);
console.log(`[LUA] LuaState地址: ${luaStatePtr}`);
if (luaStatePtr.isNull()) {
console.log(`[LUA] ${paramName} LuaState指针为NULL`);
return;
}
try {
// 读取结构体各字段
const bytecodePtr = safeReadPointer(luaStatePtr.add(LuaStateStruct.BYTECODE_PTR_OFFSET), "字节码指针");
const bytecodeSize = safeReadU32(luaStatePtr.add(LuaStateStruct.BYTECODE_SIZE_OFFSET), "字节码大小");
const currentExecPos = safeReadPointer(luaStatePtr.add(LuaStateStruct.CURRENT_EXEC_POS_OFFSET), "当前执行位置");
const initFlag = safeReadU32(luaStatePtr.add(LuaStateStruct.INIT_FLAG_OFFSET), "初始化标志");
const scriptStartPtr = safeReadPointer(luaStatePtr.add(LuaStateStruct.SCRIPT_START_PTR_OFFSET), "脚本开始指针");
const totalScriptSize = safeReadU32(luaStatePtr.add(LuaStateStruct.TOTAL_SCRIPT_SIZE_OFFSET), "脚本总大小");
console.log(`[LUA] 字节码指针 (+0x1C): ${bytecodePtr}`);
console.log(`[LUA] 字节码大小 (+0x20): ${bytecodeSize} bytes`);
console.log(`[LUA] 当前执行位置 (+0x24): ${currentExecPos}`);
console.log(`[LUA] 初始化标志 (+0x2C): 0x${initFlag.toString(16)}`);
console.log(`[LUA] 脚本开始指针 (+0x30): ${scriptStartPtr}`);
console.log(`[LUA] 脚本总大小 (+0x34): ${totalScriptSize} bytes`);
// 输出LuaState结构体的内存dump
console.log(`\n[LUA] ${paramName} LuaState结构体内存dump:`);
safeHexdump(luaStatePtr, GLOBAL_CONFIG.LOGGING.maxDumpSize, `${paramName} LuaState结构体`);
// 输出字节码内容
if (!bytecodePtr.isNull() && bytecodeSize > 0 && bytecodeSize < 10000) {
const dumpSize = Math.min(bytecodeSize, GLOBAL_CONFIG.LOGGING.maxDumpSize);
console.log(`\n[LUA] ${paramName} Lua字节码内容 (前${dumpSize}字节):`);
safeHexdump(bytecodePtr, dumpSize, `${paramName} Lua字节码`);
// 尝试识别Lua字节码头部
try {
const header = bytecodePtr.readByteArray(12);
if (header) {
const headerBytes = new Uint8Array(header);
const headerHex = Array.from(headerBytes).map(b => b.toString(16).padStart(2, '0')).join(' ');
console.log(`[LUA] 字节码头部: ${headerHex}`);
// 检查Lua签名
if (headerBytes[0] === 0x1B && headerBytes[1] === 0x4C && headerBytes[2] === 0x75 && headerBytes[3] === 0x61) {
console.log(`[LUA] ✓ 检测到标准Lua字节码签名`);
} else {
console.log(`[LUA] ⚠ 非标准Lua字节码或已加密`);
}
}
} catch (e) {
console.log(`[LUA] 字节码头部分析失败: ${e.message}`);
}
}
// 输出脚本内容
if (!scriptStartPtr.isNull() && totalScriptSize > 0 && totalScriptSize < 10000) {
const dumpSize = Math.min(totalScriptSize, GLOBAL_CONFIG.LOGGING.maxDumpSize);
console.log(`\n[LUA] ${paramName} 脚本内容 (前${dumpSize}字节):`);
safeHexdump(scriptStartPtr, dumpSize, `${paramName} 脚本内容`);
}
} catch (e) {
console.log(`[LUA] 解析${paramName} LuaState结构体时发生错误: ${e.message}`);
}
}
function installLuaDecryptHook(module) {
try {
const functionOffset = GLOBAL_CONFIG.LUA_DECRYPT_FUNCTION.offset;
const functionAddress = module.base.add(functionOffset);
console.log(`[+] 安装Lua解密Hook: ${GLOBAL_CONFIG.LUA_DECRYPT_FUNCTION.name}`);
console.log(`[+] Hook地址: ${functionAddress} (偏移: 0x${functionOffset.toString(16)})`);
Interceptor.attach(functionAddress, {
onEnter: function(args) {
GLOBAL_STATE.hookStats.luaDecryptCalls++;
console.log(`\n[LUA] ==================== ${GLOBAL_CONFIG.LUA_DECRYPT_FUNCTION.name} 函数调用开始 ====================`);
console.log(`[LUA] 调用次数: #${GLOBAL_STATE.hookStats.luaDecryptCalls}`);
console.log(`[LUA] 调用时间: ${new Date().toISOString()}`);
console.log(`[LUA] 线程ID: ${Process.getCurrentThreadId()}`);
// __fastcall约定:ECX=第一个参数,EDX=第二个参数
const param1 = this.context.ecx; // LuaState指针
const param2 = this.context.edx; // 第二个参数
console.log(`[LUA] 参数1 (ECX - LuaState*): ${param1}`);
console.log(`[LUA] 参数2 (EDX): ${param2}`);
// 保存参数
this.param1 = param1;
this.param2 = param2;
this.startTime = Date.now();
// 解析LuaState结构体
if (!param1.isNull()) {
parseLuaState(param1, "输入参数", GLOBAL_STATE.hookStats.luaDecryptCalls);
}
// 输出寄存器状态(简化版)
if (GLOBAL_CONFIG.LOGGING.enableDetailedLuaLog) {
console.log(`\n[LUA] 关键寄存器状态:`);
console.log(`[LUA] EAX: ${this.context.eax}`);
console.log(`[LUA] ECX: ${this.context.ecx} (LuaState*)`);
console.log(`[LUA] EDX: ${this.context.edx} (参数2)`);
console.log(`[LUA] ESP: ${this.context.esp}`);
}
},
onLeave: function(retval) {
const endTime = Date.now();
const duration = endTime - this.startTime;
console.log(`\n[LUA] ==================== ${GLOBAL_CONFIG.LUA_DECRYPT_FUNCTION.name} 函数调用结束 ====================`);
console.log(`[LUA] 返回时间: ${new Date().toISOString()}`);
console.log(`[LUA] 执行耗时: ${duration}ms`);
console.log(`[LUA] 返回值 (EAX): ${retval}`);
// 再次检查LuaState结构体(可能已被修改)
if (!this.param1.isNull()) {
parseLuaState(this.param1, "返回时参数", GLOBAL_STATE.hookStats.luaDecryptCalls);
}
console.log(`[LUA] ==================== Hook结束 ====================\n`);
}
});
GLOBAL_STATE.luaHookInstalled = true;
console.log(`[+] Lua解密Hook安装成功!`);
return true;
} catch (error) {
console.log(`[!] Lua解密Hook安装失败: ${error.message}`);
return false;
}
}
// ==================== 状态监控和统计 ====================
function printHookStatus() {
const runtime = Date.now() - GLOBAL_STATE.hookStats.startTime;
const runtimeMinutes = Math.floor(runtime / 60000);
const runtimeSeconds = Math.floor((runtime % 60000) / 1000);
console.log(`\n[+] ========== Hook状态报告 ==========`);
console.log(`[+] 运行时间: ${runtimeMinutes}分${runtimeSeconds}秒`);
console.log(`[+] 字符串比较Hook: ${GLOBAL_STATE.stringHookInstalled ? '✓ 已安装' : '✗ 未安装'}`);
console.log(`[+] Lua解密Hook: ${GLOBAL_STATE.luaHookInstalled ? '✓ 已安装' : '✗ 未安装'}`);
console.log(`[+] 字符串比较调用次数: ${GLOBAL_STATE.hookStats.stringCompareCalls}`);
console.log(`[+] Lua解密调用次数: ${GLOBAL_STATE.hookStats.luaDecryptCalls}`);
console.log(`[+] =====================================\n`);
}
// 定期输出状态报告
setInterval(printHookStatus, 30000); // 每30秒输出一次状态
// ==================== 主执行逻辑 ====================
async function main() {
try {
console.log("[+] 等待目标模块加载...");
const module = await waitForModule();
if (!module) {
console.log("[!] 未找到目标模块,脚本退出");
return;
}
GLOBAL_STATE.targetModule = module;
console.log(`[+] 目标模块加载完成: ${module.name}`);
// 优先级1: 安装字符串比较Hook (绕过验证)
console.log("\n[+] ========== 第一阶段: 安装字符串比较Hook ==========");
const stringHookSuccess = installStringCompareHook(module);
if (stringHookSuccess) {
console.log("[+] ✓ 字符串比较Hook安装成功,验证绕过已激活");
} else {
console.log("[!] ✗ 字符串比较Hook安装失败,可能影响后续功能");
}
// 短暂延迟,确保字符串Hook生效
await new Promise(resolve => setTimeout(resolve, 1000));
// 优先级2: 安装Lua解密Hook (监控解密过程)
console.log("\n[+] ========== 第二阶段: 安装Lua解密Hook ==========");
const luaHookSuccess = installLuaDecryptHook(module);
if (luaHookSuccess) {
console.log("[+] ✓ Lua解密Hook安装成功,开始监控解密过程");
} else {
console.log("[!] ✗ Lua解密Hook安装失败");
}
// 最终状态报告
console.log("\n[+] ========================================");
console.log("[+] 综合Hook脚本安装完成!");
console.log(`[+] 字符串比较绕过: ${stringHookSuccess ? '✓ 激活' : '✗ 失败'}`);
console.log(`[+] Lua解密监控: ${luaHookSuccess ? '✓ 激活' : '✗ 失败'}`);
console.log("[+] 脚本正在运行,等待目标函数调用...");
console.log("[+] ========================================");
// 输出首次状态报告
setTimeout(printHookStatus, 5000);
} catch (error) {
console.log(`[!] 主执行流程出错: ${error.message}`);
console.log(`[!] 错误堆栈: ${error.stack}`);
}
}
// ==================== 异常处理和清理 ====================
// 进程异常处理
Process.setExceptionHandler(function(details) {
console.log(`[!] 进程异常捕获: ${JSON.stringify(details, null, 2)}`);
console.log(`[!] 异常发生时Hook状态:`);
printHookStatus();
return true; // 继续执行
});
// 脚本退出处理
function onScriptExit() {
console.log("\n[+] ========== 脚本退出清理 ==========");
printHookStatus();
console.log("[+] 综合Hook脚本已退出");
console.log("[+] ===================================");
}
// 注册退出处理
if (typeof Script !== 'undefined' && Script.setGlobalAccessHandler) {
Script.setGlobalAccessHandler({
enumerate: function() { return []; },
get: function(property) { return undefined; },
set: function(property, value) { return false; }
});
}
// ==================== 启动脚本 ====================
console.log("[+] 综合Hook脚本初始化完成,开始执行...");
main().catch(error => {
console.log(`[!] 脚本启动失败: ${error.message}`);
});
// 脚本加载完成提示
console.log("[+] 综合Hook脚本加载完成,等待目标模块和函数调用...");