/**
* 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脚本加载完成,等待目标模块和函数调用...");

应用安全 --- 安卓安全 之 恋人精灵高级版hook脚本的更多相关文章

  1. yii2高级版账号密码问题

    yii2高级版默认后台没有密码,生成账号密码步骤: 1. CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` ...

  2. PHP框架Yii2.0安装(基础版、高级版)

    最近农成也是切入了yiiframework 2.0,就是安装yii2.0就花费了不少的时间,为此做了很多的弯路,特此写一篇博文来给后面学习的同学少走一点的弯路.写的不好的地方欢迎各位学习的同学们能够指 ...

  3. Win7家庭普通版、家庭高级版、专业版、旗舰版版本差别

    刚才我们发了一个大图片:<Windows7.Vista.XP 三大系统功能差异比较一览图>,现在,再发一张对比图片,简要的看看Windows7家庭普通版.家庭高级版.专业版.旗舰版这四个版 ...

  4. 【Win7激活工具2013版下载】适用于旗舰版、家庭高级版等所有版本32/64位 OEM激活

    虽然现在Win8已经发布了,但是身边总是还有一些朋友在用着Win7系统,而近期微软频繁的推送补丁包,导致之前的那些激活都失效了.找了网络上很多工具,之前的那些有的已经不能用了,激活不了,今天就推荐一些 ...

  5. 宣布正式发布 Biz Talk Services、Azure Active Directory 和 Traffic Manager, 同时发布 Azure Active Directory 高级版预览

    除经济优势之外,云计算还在可转化为竞争优势的应用程序开发方面提供了更大的灵活性.我们很高兴看到每天创建的新 Windows Azure 订阅超过 1000 个,更令人兴奋的是,有一半客户使用价值更高的 ...

  6. 自定义高级版python线程池

    基于简单版创建类对象过多,现自定义高级版python线程池,代码如下 #高级线程池 import queue import threading import time StopEvent = obje ...

  7. 希尔排序之C++实现(高级版)

    希尔排序之C++实现(高级版) 一.源代码:ShellSortHigh.cpp /*希尔排序基本思想: 先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组. 所有距离为d1的倍数的记录放在同 ...

  8. 直接插入排序(高级版)之C++实现

    直接插入排序(高级版)之C++实现 一.源代码:InsertSortHigh.cpp /*直接插入排序思想: 假设待排序的记录存放在数组R[1..n]中.初始时,R[1]自成1个有序区,无序区为R[2 ...

  9. 冒泡排序(高级版)之C++实现

    冒泡排序(高级版)之C++实现 一.源代码:BubbleSortHigh.cpp #include<iostream> using namespace std; /*定义输出一维数组的函数 ...

  10. Yii2.0 多语言设置(高级版配置方法) - 新的方法

    1.设置默认语言:在mail.php配置文件加上:'language'=>'zh_CN'; 2.多语言切换 (我这边是在site控制器里面操作的所以用的'/site/language') htm ...

随机推荐

  1. 关于pearcmd的探索与利用

    register_argc_argv配置 什么是register_argc_argv? 当register_argc_argv配置项打开(on)时,系统会自动将命令行输入的参数信息存入 $argc.$ ...

  2. Unity随机地图之多边形生态圈地图生成(含源码)

    Minecraft地图生成原理剖析 https://zhuanlan.zhihu.com/p/43566129 https://zhuanlan.zhihu.com/p/43579344 https: ...

  3. 技术:windows_bat_打开多个网页

    问题:对于经常打开多个网页的,点击一下打开多个网页 @echo start www.bing.com 默认的浏览器打开网址 start 语法 有关 Bat 中 start 命令的使用方法的详细说明.参 ...

  4. 一名ICer的博客开帖记录

    前言 看园子内容已有3-4年之久,虽然对于一名ICer来说,园子内容偏少.但是仍然"咸鱼IC"等一众大佬的优质好文,让我知道这是我未来学习技术的一个好地方.也在我心中埋下了一个未来 ...

  5. 配置ssh远程访问策略

    假如有两个域,一个是example.com(172.25.0.0/16),一个是my133t.org(172.24.0.0/16) 要求:从域group3.example.com能ssh远程访问两个虚 ...

  6. LongRunningTask-正确用法

    在上一篇文章<如何正确实现一个 BackgroundService>中有提到 LongRunning 来优化后台任务始终保持在同一个线程上. protected override Task ...

  7. 解析 RS485 总线:从技术内核到终端电阻的可靠性密码

    在工业自动化的复杂网络中,RS485 总线犹如一条隐形的 "神经脉络",连接着分散的传感器.控制器与执行器,支撑着能源管理.智能制造等场景的高效运转.这项诞生于上世纪 80 年代的 ...

  8. 【数据集】cubicasa5k数据集下载(稳定下载)-建筑图纸

    下载链接(需要注册,可以用Google 账号或者email):CubiCasa5k | Swin Transformer [MMDetection] (kaggle.com)

  9. git 重命名文件夹

    文件夹名称: game 想要修改名称为: gamesdk git mv game gamesdkgit commit -m 'rename dir game to gamesdk'git push o ...

  10. Windows应用开发-常用工具

    https://www.cnblogs.com/kybs0/p/18643823  .NET/WPF开发 Visual Studio 最新版本是VS2022,官网下载:Visual Studio 20 ...