dwgx@blog:~$dwgx
> cd ../posts

SaoMoLa

// created

VRChat Avatar extraction and upload platform


VRChat Avatar/World 一键提取工具。从 VRChat 崩溃转储中恢复 AES-256 加密密钥,解密 Unity 6 DWR 缓存文件,导出 FBX/PNG/JSON。

快速开始

首次配置(一次性,需管理员)

# 配置 Windows 在 VRChat 崩溃时自动写完整内存转储
saomola dump-config --enable

提取流程

# 1. 正常启动 VRChat → 进公开房间 → 等 avatar 加载(30秒)
# 2. 管理员 cmd 杀掉 EAC:
taskkill /F /IM EasyAntiCheat_EOS.exe
# 3. 等 30-60 秒 VRChat 自行崩溃
# 4. 扫描崩溃转储提取密钥:
saomola dump-scan D:\Project\SaoMoLa\dumps\ --verify
# 5. 解密 + 提取所有缓存的 avatar:
saomola grab --all-cached -o ./output

提取结果

output/
├── Mesh/           ← FBX 模型(含骨骼、BlendShapes)
├── Texture2D/      ← PNG 贴图(diffuse、normal、emission)
├── Material/       ← JSON 材质参数
└── AnimationClip/  ← 动画文件

构建

D:\Software\dotnet\dotnet.exe build src\SaoMoLa\SaoMoLa.csproj
D:\Software\dotnet\dotnet.exe run --project src\SaoMoLa\SaoMoLa.csproj -- --help

全部命令

命令 功能
saomola dump-config --enable 配置 WER 自动写崩溃转储
saomola dump-scan <path> --verify 扫描 dump 文件找加密密钥
saomola dump-watch --auto-grab 监控 dump 目录,自动扫描+提取
saomola grab --all-cached 用已有密钥解密提取所有缓存
saomola grab <avatar_id> 提取指定 avatar
saomola grab --latest -n 10 提取最近 10 个缓存
saomola extract <file> 提取单个已解密 bundle
saomola watch 实时监控 VRChat 缓存目录
saomola keys 从运行中的 VRChat 读取密钥(需内核驱动)
saomola key-status 查看密钥缓存状态
saomola mcp-server 启动 MCP 服务器(AI 驱动)

工作原理

┌─────────────────────────────────────────────────────────────┐
│  VRChat 运行中(公开房间,avatar 已加载)                     │
└────────────────────────────┬────────────────────────────────┘
                             │ taskkill /F /IM EasyAntiCheat_EOS.exe

┌─────────────────────────────────────────────────────────────┐
│  VRChat 崩溃 → Windows WER 写完整进程 dump(~10GB)          │
│  EAC 已死 → WER 可以正常读取进程内存                         │
└────────────────────────────┬────────────────────────────────┘
                             │ saomola dump-scan

┌─────────────────────────────────────────────────────────────┐
│  扫描 dump 找 AES-256 密钥                                   │
│  算法:UTF-16 Base64 + UTF-8 JSON + 高熵 byte[] 扫描        │
│  验证:AES-GCM tag 认证(对缓存 bundle 试解密)             │
└────────────────────────────┬────────────────────────────────┘
                             │ saomola grab --all-cached

┌─────────────────────────────────────────────────────────────┐
│  AES-GCM 解密缓存 → Unity AssetBundle 解析 → 导出资产       │
│  Mesh → FBX | Texture → PNG | Material → JSON | Anim → YAML │
└─────────────────────────────────────────────────────────────┘

技术细节

加密方案(VRChat Unity 6 DWR)

  • Bundle 格式:UnityFS header(明文)+ AES-GCM 加密的数据块
  • 块结构:[12B IV][4B plaintext_length][ciphertext][16B auth_tag]
  • 每块 ~64KB
  • 密钥:服务器下发的 32 字节 AES-256 key(Base64 编码存在内存中)

密钥在内存中的形态

  • IL2CPP String 对象:44 字符 UTF-16 Base64
  • 位于 VRCPlayer 对象附近(DecryptPlayerEncryptionKey 方法 RVA: 0x180D4C7F0)
  • 堆扫描模式:连续 88 字节 UTF-16,每 char ∈ [A-Za-z0-9+/=],decode 后正好 32 字节

为什么 dump 方法有效

EAC 保护机制:

  • ✅ 阻止 ReadProcessMemory(usermode hook on NtReadVirtualMemory)
  • ✅ 阻止 MiniDumpWriteDump(ACCESS_DENIED)
  • ✅ 阻止 Task Manager dump(0 字节)
  • ✅ 检测 testsigning 模式(拒绝启动游戏)
  • ✅ Certificate pinning(阻止 MITM)
  • 无法阻止 WER crash dump(当 EAC 进程已被杀死)

项目结构

D:\Project\SaoMoLa\
├── README.md               ← 本文件
├── src\
│   ├── SaoMoLa\            ← 主项目
│   │   ├── SaoMoLa.csproj
│   │   ├── Program.cs      ← CLI 入口
│   │   ├── DumpKeyScanner.cs    ← dump 文件密钥扫描器
│   │   ├── BundleExtractor.cs   ← AssetBundle 解析+导出
│   │   ├── VrcCacheDecryptor.cs ← AES-GCM/CBC 解密
│   │   ├── GrabCommand.cs       ← grab 命令实现
│   │   ├── WerConfig.cs         ← WER 注册表配置
│   │   ├── KeyResolver.cs       ← 多路 key 获取调度
│   │   ├── Export\              ← FBX/PNG/JSON 导出器
│   │   └── ...
│   ├── AnimeStudio\         ← Unity AssetBundle 解析引擎
│   ├── AnimeStudio.Utility\ ← 模型转换工具
│   └── AnimeStudio.Libraries\ ← Native DLL
└── dumps\                   ← WER 写入的 crash dump

依赖

  • .NET 8 SDK
  • Windows 10/11(WER 功能)
  • VRChat(Steam 版)

已验证成果

  • 24 个 AES-GCM 加密 bundle 成功解密
  • 30 个 bundle 成功提取
  • 21,414 个文件,4.49 GB 总输出
  • 1,440 FBX + 4,631 PNG + 1,632 JSON + 13,711 动画