GKLBB

当你经历了暴风雨,你也就成为了暴风雨

导航

应用安全 --- 安卓安全 之 恋人精灵高级版hook脚本

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

 

posted on 2025-10-21 11:11  GKLBB  阅读(2)  评论(0)    收藏  举报