Unity3D逆向分析可视化:搭建WebUI辅助工具
发布时间:2026/10/1 17:04:41来源:尧图网络
1. 为什么要给 Unity3D 逆向分析套一个 WebUI日常做 Unity3D 游戏分析时很多朋友还是习惯开好几个命令行窗口一边用 AssetStudio 导资源一边用 Il2CppDumper 砸脚本再开着调试器盯内存。偶尔还要切换到 hex 工具或者写一堆临时脚本去过滤日志。这套流程不是不能用但效率很低尤其是当你需要同时面对几十个 AssetBundle、大量 GameObject 或者本地化文本时纯命令行操作很容易让人迷失。相比之下一个设计良好的 WebUI 可以把静态资源扫描、脚本逻辑分析、动态内存读取展示到一个页面按场景、资源类型、游戏对象层级组织数据。你不需要记一堆命令参数不需要反复在多个工具之间切换只需要在浏览器里点击、搜索、过滤就能快速定位问题。本文要做的就是基于 Unity3D 引擎游戏分析场景搭建一个美观、可扩展的 WebUI 分析工具。重点覆盖三块内容Unity3D 游戏静态分析解析资源文件、AssetBundle、GameObject 等动态分析通过 Frida 等工具读取运行中的游戏数据再回传到 WebUI工程落地后端接口、前端页面、实时通信的完整实现思路。如果你是刚开始接触 Unity3D 游戏分析可以通过本文理解常见的分析对象和流程如果你已经会使用单一命令行工具则可以参考本文的综合化思路把散落的工具整合成一套可视化工作流。2. Unity3D 游戏分析的核心概念在写代码之前需要先搞清楚 Unity3D 游戏里到底有哪些东西值得分析。很多初学者一上来就盯着内存数据结果发现无从下手原因在于没有先理解引擎的数据组织方式。2.1 游戏资源与 AssetBundleUnity3D 的资源体系比较庞大。一个发布后的游戏包中常见目录包括Assets/编辑器中的原始资源Library/Unity 导入资源后的缓存AssetBundle打包后的资源集合通常有.ab、.unity3d、.bundle等后缀StreamingAssets随包发布的外部文件可能是配置表、加载动画、音频等。分析 Unity3D 游戏时我们最关心的是资源元数据和引用关系。例如某个场景里加载了哪些贴图、某个 Prefab 的 Transform 层级是怎样的、某个 UI 界面绑定了哪些脚本。这些信息在打包后会以SerializedFile的形式保存在资源中需要专门的解析器来读取。常见的分析目标包括贴图与纹理找出立绘、图标、背景图音频与视频提取语音、BGM、过场动画字体与文本定位 UI 文案、本地化资源Prefab还原界面结构、按钮层级场景理解场景加载顺序和资源依赖。2.2 脚本后端Mono 与 IL2CPPUnity3D 脚本有两种常见后端Mono托管代码被编译成 IL中间语言以Assembly-CSharp.dll形式存在可以直接用 .NET 反编译工具分析IL2CPP把 IL 转换成 C再编译进 native 库中如libil2cpp.so无法直接反编译出原始 C# 源码需要配合 Il2CppDumper 还原基本结构和函数地址。分析 IL2CPP 游戏时普通的字符串搜索和静态反编译会很吃力。此时需要先识别 Unity 版本再根据元数据文件global-metadata.dat导出命名空间、类名、方法名。有了这些符号信息后续分析脚本逻辑、定位关键函数就会顺畅很多。本文的 WebUI 在设计上会把“脚本后端类型”作为一个重要的项目元数据展示出来方便分析人员判断该走静态反编译路线还是走动态 Hook 路线。2.3 静态分析、动态分析与内存操作静态分析不运行游戏直接解析包体文件、DLL、资源文件。优点是安全可控缺点是无法观察运行时数据。动态分析让游戏跑起来在运行过程中注入脚本或附加调试器。优点是能看到实时状态适合定位加密字符串、解密逻辑、内存数值变化缺点是环境搭建复杂且必须获得授权。内存操作在游戏进程中读取、修改数值。通常配合动态分析使用但这也是最容易被滥用的一种能力。在成熟团队中一般会先用静态分析理清资源结构再用动态分析验证关键逻辑最后把两者结果统一汇总到一个平台。WebUI 正好是承载这套流程的合适载体。3. 环境准备与项目结构3.1 工具链选型这次演示我选择的是一个前后端分离的轻量方案后端Python FastAPI负责文件解析、接口调度、WebSocket 通信前端原生 HTML JavaScript Bootstrap 5降低上手成本不引入太重的前端工程化资源解析使用 Python 的UnityPy库它可以读取 Unity 序列化文件、AssetBundle并导出资源动态分析通过 Frida 连接测试设备或模拟器将数据回传到 FastAPI 服务项目状态存储直接使用 JSON 文件方便调试。这种选型的好处是依赖少、可复现性强。如果后期需要团队协作可以再把前端拆成 Vue/React 项目后端继续复用 FastAPI。3.2 前置环境建议环境如下具体版本需要根据你的实际项目调整操作系统Windows 10/11 或 Ubuntu 20.04Python 3.9推荐 3.10 或 3.11Node.js当前仅用于前端资源预览插件不强制要求测试设备Android 模拟器或真机需要开启调试授权目标一个用于测试的 Unity3D 游戏 APK/IPA 包体或者一个本地工程目录。3.3 项目目录设计建议按下面结构组织项目unity3d-webui-analyzer/ ├── backend/ │ ├── app.py # FastAPI 入口 │ ├── unity_parser.py # Unity 资源解析模块 │ ├── frida_bridge.py # Frida 通信模块 │ ├── static/ │ │ └── index.html # 前端页面 │ └── data/ │ └── analysis_cache/ # 分析结果缓存 ├── scripts/ │ ├── hook_unity.py # Frida 注入脚本 │ └── dump_assets.py # 命令行资源导出 └── requirements.txt这里把前端页面放在backend/static下FastAPI 可以直接挂载省去跨域配置适合单机分析场景。后续如果需要多人访问再加入反向代理即可。4. 搭建 WebUI 后端4.1 用 FastAPI 暴露资产解析接口先创建requirements.txtfastapi uvicorn unitypy python-multipart frida websockets然后创建backend/app.py先实现一个最简单的健康检查接口和静态文件挂载# 文件路径backend/app.py from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.responses import JSONResponse app FastAPI(titleUnity3D WebUI Analyzer) # 挂载前端页面 app.mount(/static, StaticFiles(directorystatic), namestatic) app.get(/) def index(): return JSONResponse({message: Unity3D WebUI Analyzer is running.}) app.get(/api/health) def health(): return JSONResponse({status: ok})运行命令cd backend pip install -r requirements.txt uvicorn app:app --host 127.0.0.1 --port 8000启动后访问http://127.0.0.1:8000/api/health可以看到返回 JSON。这一步的作用是确认环境和服务框架可用。4.2 解析 Unity 资源文件接下来实现核心的unity_parser.py。这里使用 UnityPy 读取资源文件提取基本元数据。# 文件路径backend/unity_parser.py import json import UnityPy class UnityParser: def __init__(self, path): self.path path self.env UnityPy.load(path) def list_assets(self, filter_typeNone): 列出资源文件中的对象 result [] for obj in self.env.objects: if filter_type and obj.type.name ! filter_type: continue item { name: obj.read().m_Name if hasattr(obj.read(), m_Name) else , type: obj.type.name, path_id: obj.path_id, container: obj.container.name if obj.container else , } result.append(item) return result def export_textures(self, output_dir): 导出所有贴图 import os os.makedirs(output_dir, exist_okTrue) exported [] for obj in self.env.objects: if obj.type.name Texture2D: data obj.read() image data.image if image: path os.path.join(output_dir, f{data.m_Name}.png) with open(path, wb) as f: f.write(image.tobytes()) exported.append(path) return exported在app.py中添加接口from unity_parser import UnityParser app.post(/api/parse) async def parse_file(file_path: str): parser UnityParser(file_path) assets parser.list_assets() return {file: file_path, assets: assets[:100], total: len(assets)}实际使用中你可能会先上传 APK/IPA或者直接传入已解包的资源目录。因为 UnityPy 对 Unity 版本兼容性问题比较多建议先用小文件测试。4.3 提供 WebSocket 动态数据通道动态分析通常需要实时推送比如 Frida 脚本发现某个对象的属性变化、某个函数被调用。用 WebSocket 比轮询更合适。在app.py中增加 WebSocket 端点from fastapi import WebSocket, WebSocketDisconnect class ConnectionManager: def __init__(self): self.active_connections [] async def connect(self, websocket: WebSocket): await websocket.accept() self.active_connections.append(websocket) def disconnect(self, websocket: WebSocket): self.active_connections.remove(websocket) async def send_message(self, message: str): for connection in self.active_connections: await connection.send_text(message) manager ConnectionManager() app.websocket(/ws) async def websocket_endpoint(websocket: WebSocket): await manager.connect(websocket) try: while True: data await websocket.receive_text() # 这里可以把消息发给 Frida 端或者接收 Frida 回传的数据 await manager.send_message(fecho: {data}) except WebSocketDisconnect: manager.disconnect(websocket)在前端页面浏览器可以通过new WebSocket(ws://127.0.0.1:8000/ws)连接这个通道。后续 Frida 端也可以作为 WebSocket 客户端把数据发送过来。5. 实现前端分析面板5.1 页面布局与交互使用 Bootstrap 5 搭一个简单的分析面板包含四个区域顶部导航标题、连接状态、目标进程信息左侧侧边栏资源分类、场景列表、脚本对象列表中间内容区当前选中对象的属性详情底部日志区实时显示 WebSocket 返回的日志和 Frida 输出。创建backend/static/index.html先搭建基础结构!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleUnity3D WebUI Analyzer/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.0/dist/css/bootstrap.min.css relstylesheet style body { background-color: #f5f7fa; } .sidebar { height: 100vh; overflow-y: auto; background: #fff; border-right: 1px solid #dee2e6; } .log-box { background: #1e1e1e; color: #d4d4d4; font-family: monospace; height: 200px; overflow-y: auto; } /style /head body nav classnavbar navbar-dark bg-dark span classnavbar-brand ms-3Unity3D WebUI Analyzer/span span idconnStatus classbadge bg-secondary me-3未连接/span /nav div classcontainer-fluid div classrow div classcol-md-3 sidebar p-3 h6资源列表/h6 div idassetList classlist-group list-group-flush/div /div div classcol-md-6 p-3 h6对象详情/h6 div iddetailPanel classborder rounded p-3 bg-white请选择资源/div /div div classcol-md-3 p-3 h6实时日志/h6 div idlogBox classlog-box rounded p-2/div /div /div /div script // 后续逻辑 /script /body /html这是一版干净的基础布局颜色以白灰为主信息够清晰。如果你希望更“赛博朋克”一点可以换成深色主题和霓虹色系不过建议优先保证内容可读性。5.2 展示资源列表与预览在页面加载后通过调用后端接口获取资源列表。这里放一个简单的示例async function loadAssets() { const resp await fetch(/api/parse, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({file_path: /path/to/your/file}) }); const data await resp.json(); const listEl document.getElementById(assetList); listEl.innerHTML ; data.assets.forEach(item { const btn document.createElement(button); btn.className list-group-item list-group-item-action py-2; btn.textContent ${item.type} - ${item.name || 未命名}; btn.onclick () showDetail(item); listEl.appendChild(btn); }); } function showDetail(item) { document.getElementById(detailPanel).innerHTML pstrong类型/strong${item.type}/p pstrong名称/strong${item.name}/p pstrongPathID/strong${item.path_id}/p pstrong所属容器/strong${item.container}/p ; }这里没有把文件路径写死实际项目中应该通过文件选择器上传或者扫描整个目录后自动填充。5.3 实时展示 Frida 返回的游戏对象层级动态数据的展示更讲究实时性。我们可以让 Frida 端把游戏对象层级序列化成 JSON通过 WebSocket 发送到后端后端再用manager.send_message推到前端。前端监听日志let socket new WebSocket(ws://127.0.0.1:8000/ws); socket.onopen function() { document.getElementById(connStatus).textContent WebSocket 已连接; document.getElementById(connStatus).classList.remove(bg-secondary); document.getElementById(connStatus).classList.add(bg-success); }; socket.onmessage function(event) { const logBox document.getElementById(logBox); const line document.createElement(div); line.textContent event.data; logBox.appendChild(line); logBox.scrollTop logBox.scrollHeight; };Frida 端返回的数据可以是{time: 1699999999, type: scene, data: MainScene: Canvas Button Text}前端的展示逻辑可以根据type分类渲染。比如type object就渲染成对象树type log就渲染成控制台文本。6. 完整实战从加载游戏包到查看 GameObject6.1 准备测试环境与授权先强调一次所有分析步骤只能用于你自己拥有的应用、已获得授权的测试应用或者学习研究环境。不要对未知应用、商业游戏做未授权分析。准备一个用于测试的 Unity3D 游戏包体版本建议选择 Unity 2020 或 2021 左右的经典结构。如果是 APK可以先解压提取assets/bin/Data目录也可以直接拿一个含 AssetBundle 的本地文件测试。6.2 后端启动在backend目录下更新app.py把解析接口调整为真正的文件扫描逻辑。这里给出一个可用的版本# 文件路径backend/app.py import os import json from fastapi import FastAPI, File, UploadFile, Request from fastapi.staticfiles import StaticFiles from fastapi.responses import JSONResponse from fastapi import WebSocket, WebSocketDisconnect from unity_parser import UnityParser app FastAPI() app.mount(/static, StaticFiles(directorystatic), namestatic) DATA_DIR data/uploads os.makedirs(DATA_DIR, exist_okTrue) app.post(/api/upload) async def upload_file(file: UploadFile File(...)): 上传 Unity 资源文件返回解析结果 file_path os.path.join(DATA_DIR, file.filename) with open(file_path, wb) as fp: fp.write(await file.read()) parser UnityParser(file_path) assets parser.list_assets() return { file: file.filename, total: len(assets), assets: assets[:200], }接下来启动服务uvicorn app:app --host 0.0.0.0 --port 80006.3 前端打开浏览器访问http://127.0.0.1:8000/static/index.html页面会显示资源列表区域。上传一个包含 Texture2D 和 GameObject 的资源文件点击解析可以看到资源列表被渲染出来。如果你使用的是 UnityPy 无法直接读取的文件可以先使用工具转成可读格式或者在解析前统一到同一种 Unity 版本再导出。6.4 运行结果预期效果左侧列表出现资源条目类型包括Texture2D、GameObject、MonoBehaviour等点击其中一个条目右侧显示名称、类型、PathID、容器等基础属性底部日志区可以收到动态数据。若想进一步验证动态分析可以连接一台测试设备运行scripts/hook_unity.py。例如下面的 Frida 脚本片段遍历 GameObject 的 Transform 层级# 文件路径scripts/hook_unity.py import frida import sys import json import websocket # 这里的代码仅用于授权测试环境 def on_message(message, data): if message[type] send: payload message[payload] ws websocket.create_connection(ws://127.0.0.1:8000/ws) ws.send(json.dumps(payload)) ws.close() elif message[type] error: print(message[stack]) session frida.get_usb_device().attach(com.example.testgame) script_code Java.perform(function () { send({type: log, data: ready}); }); script session.create_script(script_code) script.on(message, on_message) script.load()这段代码只是为了演示链路实际 Frida 脚本需要根据 Unity 版本和导出符号调整。例如要遍历 Transform需要从UnityEngine.Transform入手先拿到场景根对象再递归读取子节点。在 IL2CPP 场景下还要依赖 Il2CppDumper 导出的偏移量。当 Frida 脚本发送数据后WebUI 底部日志会实时显示{type: log, data: ready}就说明全链路已经跑通。7. 常见问题与排查思路问题现象常见原因解决思路上传文件后接口报错Not a valid Unity file文件被加密、压缩或不是标准 Unity 资源格式先用 AssetStudio 尝试打开若失败检查包体是否被加固或二次加密UnityPy 读取时报TypeError目标文件使用了较新的 Unity 版本或包含自定义类型升级 UnityPy或者改用 C# 工具 AssetStudio前端页面无法连接 WebSocket前后端端口不同或浏览器安全策略限制确保前端由 FastAPI 的/static路由提供或配置 CORSFrida 连接设备失败设备未开启调试、USB 调试授权未确认执行adb devices确认设备状态重新允许 USB 调试动态分析看不到目标函数游戏是 IL2CPP 后端Frida 内置的 Java API 无法直接匹配先用 Il2CppDumper 导出 C# 结构再按基址偏移方式定位函数页面加载大量资源时卡顿一次解析的文件过多前端渲染压力大后端分页返回前端使用虚拟列表日志区数据过多导致浏览器内存上涨没有对日志数量做上限前端限制最多显示 500~1000 条日志超出后清空最早记录排查时建议按“网络链路页面 → 后端 → 文件解析/Frida → 页面”的顺序逐步确认。先确认接口返回是否正常再确认 WebSocket 连接是否打开最后才去看具体解析和注入逻辑。8. 最佳实践与工程建议8.1 安全与合规边界涉及 Unity3D 游戏分析尤其是动态分析和内存操作时必须严格遵守授权边界。建议在工具启动页和说明文档中明确标注仅用于分析自有应用仅用于授权测试环境不做任何商业化破解、盗版分发或外挂制作不存储和传播从目标应用中提取的敏感数据。在代码层面可以将授权校验设计为一道开关。例如后端启动时要求输入项目名和授权标签只有匹配才允许调用动态分析接口。这不能阻止所有滥用但至少能形成审计记录。8.2 资源缓存与增量解析Unity3D 游戏的资源文件通常很大一次全量解析既慢又占内存。更好的做法是按文件 Hash 建立缓存如果文件没有变化直接读取上一次 JSON 结果解析时只扫描目标类型比如只需要看 UI 就只解析Canvas、Sprite、Font图片预览单独生成缩略图避免直接加载原始大图。下面是一个缓存读取的简单思路import hashlib import os def file_md5(path, chunk_size8192): h hashlib.md5() with open(path, rb) as f: while True: chunk f.read(chunk_size) if not chunk: break h.update(chunk) return h.hexdigest() def get_cache_path(file_path): md5 file_md5(file_path) return os.path.join(data/analysis_cache, f{md5}.json)缓存命中时直接返回 JSON可以大幅缩短分析时间。8.3 日志规范日志至少要包含时间、来源、级别、数据摘要。不要一股脑地把原始二进制数据打进日志。建议统一使用 JSON 格式{time: 2025-01-01 12:00:00, source: frida, level: info, message: GameObject found: Canvas}在 WebUI 前端过滤栏中可以按来源和级别筛选避免日志滚动过快。8.4 动态分析脚本管理Frida 脚本通常需要在不同 Unity 版本、不同 Android 架构上反复调整。建议按 Unity 版本分支保存脚本将关键偏移量集中到一个 YAML 文件脚本运行时自动校验 Unity 版本和包名不匹配则拒绝执行钩子函数只读不改除非明确进入“内存修改实验”模式。8.5 前后端解耦虽然本文演示时简化了前端但工程上建议把前端拆成独立项目。这样后端可以专注处理解析任务和动态数据转发前端可以独立迭代界面交互多人协作时也更顺畅。如果担心部署麻烦也可以用 Docker 把前后端分别打成镜像通过 nginx 统一入口。9. 总结与下一步学习方向到这里你已经完成了一个基于 WebUI 的 Unity3D 游戏分析工具雏形后端能解析 Unity 资源文件前端能展示资源列表Frida 数据能通过 WebSocket 实时回传到浏览器。整体设计兼顾了静态分析和动态分析两条路径后续完全可以根据实际分析需求去扩展。下一步建议从三个方向继续深入静态分析方面学习 AssetStudio 源码理解SerializedFile的二进制结构掌握手动解析自定义类型的能力动态分析方面研究 Unity 游戏运行时对象管理机制熟悉GameObject、Transform、Component在内存中的关系工程化方向为 WebUI 增加项目管理、批量分析、报告导出、多设备并发连接等功能把它变成一个团队工具。你掌握的不只是某一条命令或某一个库而是一套从数据出发的分析思路。真正遇到复杂游戏时静态、动态、可视化三者结合才能快速定位到你想找的对象。建议先拿一个自己用 Unity 打包的小工程做实验从解析自己的项目开始逐步积累经验再尝试分析更复杂的授权测试包。
网站建设高端定制企业官网