标签 Frida 下的文章

目前只有プリンセスコネクト!Re:Dive 无法 dump,这玩意的 crackproof 不知道改了什么东西,会把完整的 PE 切成几百个分页,不过可以分析安卓版本的,壳子难度中等。
由于 Unity 的源代码得买,所以这里只能用反编译器 + pdb 来分析

LoadScriptingRuntime 这个函数加载了 GameAssembly.dll,相关的加载逻辑在 LoadIl2Cpp 里面,LoadIl2Cpp 的返回值是 GameAssembly.dll 的 handle

解法就很明显了,可以用 frida 拦截 LoadIl2Cpp 返回时候的动作,这时候 GameAssembly.dll 刚刚被 LoadLibraryW 加载上去,并且完成了一些初始化(crackproof 修复导入表,解密解压代码段等等),但是没有执行任何 il2cpp 部分的代码,dump 下来就能获得完全干净的 GameAssembly.dll 了。
dump 下来以后还需要简单的修复一下 PE 头,完整代码如下:

'use strict';

const UNITY_PLAYER = "UnityPlayer.dll";
const TARGET_RVA = ;
const GAMEASSEMBLY = "GameAssembly.dll";
const DUMP_PATH = "D:\\Reverse\\Frida_Hook\\GameAssembly_dump_fix.dll";
const CHUNK_SIZE = ;


function dumpModule(moduleName, outPath) {
    try {
        const m = Process.getModuleByName(moduleName);
        console.log("[*] Found module:", m.name, "Base:", m.base, "Size:", m.size);

        const size = m.size;
        const base = m.base;

        // raw→virtual const localCopy = fixPEHeader(base, size);
        if (localCopy === null) {
            console.error("[!] Fix PE Header failed");
            return;
        }

        const file = new File(outPath, "wb");
        console.log("[*] Output:", outPath);

        let offset = 0;
        while (offset < size) {
            const chunk = Math.min(CHUNK_SIZE, size - offset);
            const buf = localCopy.add(offset).readByteArray(chunk);
            file.write(buf);
            offset += chunk;
        }

        file.flush();
        file.close();
        console.log("[*] Dump finished:", outPath);

    } catch (e) {
        console.error("[!] Dump exception:", e);
    }
}

function hookAfterUnityPlayerLoaded(module) {
    if (module.name !== UNITY_PLAYER) return;
    console.log("[+] UnityPlayer.dll loaded @", module.base);

    const targetAddr = module.base.add(TARGET_RVA);
    console.log("[*] Hooking LoadDynamicLibrary @", targetAddr);

    Interceptor.attach(targetAddr, {
        onLeave(retval) {
            console.log("[*] LoadDynamicLibrary returned:", retval);

            try {
                const found = Process.findModuleByName(GAMEASSEMBLY);
                if (found) {
                    console.log("[*] GameAssembly.dll loaded -> dumping...");
                    dumpModule(GAMEASSEMBLY, DUMP_PATH);
                } else {
                    console.warn("[!] GameAssembly.dll not found yet");
                }
            } catch (e) {
                console.error("[!] Dump error:", e);
            }
        }
    });
}


function fixPEHeader(base, size) {
    try {
        const localBuf = Memory.alloc(size);
        Memory.copy(localBuf, base, size);

        const dos = localBuf.readPointer();
        const e_lfanew = localBuf.add().readU32();
        const nt = localBuf.add(e_lfanew);

        const numSections = nt.add().readU16();
        const optSize = nt.add().readU16();
        const firstSec = nt.add( + optSize);

        console.log("[*] Sections:", numSections, "First section @", firstSec);

        let secPtr = firstSec;
        for (let i = 0; i < numSections; i++) {
            const virtualAddress = secPtr.add(0xC).readU32();
            const virtualSize = secPtr.add().readU32();

            // 把 raw data 指向 virtual
            secPtr.add().writeU32(virtualAddress);       // PointerToRawData
            secPtr.add().writeU32(virtualSize);          // SizeOfRawData

            secPtr = secPtr.add(); // 下一节
        }

        return localBuf;

    } catch (e) {
        console.error("[!] fixPEHeader exception:", e);
        return null;
    }
}


setImmediate(() => {
    console.log("[*] Script started.");

    Process.attachModuleObserver({
        onAdded(module) {
            console.log("[*] Module loaded:", module.name);
            if (module.name === UNITY_PLAYER) {
                hookAfterUnityPlayerLoaded(module);
            }
        },
        onRemoved(module) { }
    });

    try {
        const existing = Process.getModuleByName(UNITY_PLAYER);
        if (existing) hookAfterUnityPlayerLoaded(existing);
    } catch (e) { }
});

由于 crackproof hook 了自身的 openprocess 并且进行的 handle 权限过滤,frida-server 是肯定不行了,但是 Windows 这玩意相当开放,有以下方法能把 frida-gadget.dll 塞进去:

  1. 劫持 version.dll
  2. 修改 UnityPlayer.dll 的导入表,把 frida-gadget.dll 导出表的任意函数塞进去。
  3. 搓一个 ring0 驱动,从内核用 APC 方法把 frida-gadget.dll 强行塞进去。

frida-gadget.dll 塞进去了以后还需要写一个配置文件,名称命名为 frida-gadget.config

{
  "interaction": {
    "type": "script",
    "path": "D:\\Reverse\\Frida_Hook\\crackproof\\1.js"
  }
}

这样 frida-gadget.dll 加载后就能自动执行脚本,手动连接执行肯定是来不及的,因为 GameAssembly.dll 加载时机非常早。

对于ウマ娘 プリティーダービー这种会检查目录下面有没有多余的 dll,可以把带 crackproof 但是不检查 dll 的启动器复制过去,然后就能随意改导入表了。(不带 crackproof 不检查 dll 的启动器貌似不行,疑似启动器上面的壳子有额外检测)


📌 转载信息
转载时间:
2026/1/12 17:05:44

特别说明

  • 我们为你提供从竞争白热化的移动互联网红海赛道,向 AI-native 应用全新蓝海领域转型的稀缺战略机遇。
  • 我们秉持结果导向,核心团队均由各领域资深专家领衔,从根源上杜绝 “外行指导内行” 的情况;管理上坚决摒弃微观管理(micro-manage),充分赋予团队自主决策与执行空间。
  • 团队成员中既有退役军人,也有伙伴;既有大专生,也有博士后。人员背景多元包容,招聘不设学历门槛,始终坚持唯才是举。

官网

职位描述

  • 主导 Android 端核心数据采集方案的设计与落地,负责主流及高难度 APP 的数据挖掘,包括复杂 UI 交互分析、数据交互逻辑还原、加密协议破解等核心工作,为 AI 大模型训练提供高质量数据支撑。
  • 深耕 Android 端逆向工程:负责 Android APP 的脱壳、加解密分析,精通 Smali/Java 代码还原,基于 Arm64 指令集进行汇编级分析,主导 Xposed/LSPosed 插件、Frida 脚本的设计与开发,实现对目标 APP 的 Hook 与数据拦截。
  • 主导 Android 端风控对抗体系搭建:针对 APP 端的设备指纹(IMEI/AndroidID/OAID)、Root 检测、行为验证、签名校验、进程注入检测等风控策略,设计并落地有效的对抗方案,保障采集任务的稳定运行。
  • 负责 Android 端自动化采集框架的设计与优化:基于 UiAutomator、Espresso 等框架封装高效的自动化采集工具,实现复杂场景下的 APP 自动操作、数据提取与异常重试,提升采集效率与稳定性。
  • 参与 Web 端中高难度数据采集任务:基于 Python 生态爬虫框架(Scrapy/Playwright)开发复杂动态渲染页面(SPA/Vue/React)的抓取逻辑,协助破解 Web 端 JS 混淆、参数签名等加密机制。
  • 深入分析 Web 及 Android 端网络协议(HTTP/HTTPS/WebSocket/gRPC),主导复杂协议的还原与模拟,协助构建分布式数据采集架构,参与采集任务的分布式调度与性能优化(并发控制、速率调节)。
  • 协同 AI 算法团队输出标准化数据格式,优化数据采集流程与质量校验机制;沉淀 Android 逆向、双端风控对抗等技术经验,形成技术文档与团队共享。

岗位要求

  • 3~5 年数据采集相关经验,其中至少 2 年以上 Android 端数据采集 / 逆向核心经验,具备高难度 Android APP 逆向(如加固脱壳、复杂加密协议还原)及风控对抗的实战落地案例。
  • 精通 Android Framework,深入理解 AccessibilityService 原理、UI 渲染机制、AMS/PMS 等系统服务工作流程;熟练掌握 Smali 指令、Arm64 指令集,能够独立完成 Android APP 的静态分析与动态调试。
  • 具备扎实的 Android 开发与逆向技能:能够独立开发 Xposed/LSPosed 插件、Frida 脚本;熟练使用 IDA Pro、Jadx、Apktool 等逆向工具;有 APP 加固(360 加固、爱加密等)脱壳经验者优先。
  • 精通 Android 端网络协议分析:能够使用 Charles/Fiddler/Wireshark 等工具完成复杂网络抓包,独立还原 HTTPS/WebSocket 等协议的加密交互逻辑;了解 Android 端网络请求框架(OkHttp/Retrofit)的工作原理。
  • 具备 Web 端数据采集基础:熟悉 Python 编程语言,熟练使用 Scrapy、Playwright 等爬虫框架及数据解析工具;具备 Web 端 JS 逆向、参数加密破解、基础反爬(IP 代理、浏览器指纹)对抗经验。
  • 了解分布式数据采集架构:熟悉 Redis(缓存 / 队列)、MongoDB 等中间件的使用,能够基于 Scrapy-Redis 等框架实现简单的分布式任务调度;具备大规模数据采集场景下的问题排查与性能优化能力。
  • 具备较强的独立攻坚能力、问题分析与解决能力,良好的沟通协作意识与技术沉淀意愿,能承受高难度任务压力,自驱力强。

工作地址

北京市海淀区清华同方科技广场 D 座 20 层 或 北京市朝阳区锐创国际中心 A 座 12 层

薪资

30 ~ 50 * 13 薪

联系方式

yinglu@pureblueai.com


📌 转载信息
原作者:
snakeninny
转载时间:
2026/1/10 19:18:18