VRChat 模型下载分析与攻击记录
事先声明
事先声明,这篇文章不会给出具体的步骤,主要还是留个记录,不是给打算利用我所使用的方法进行滥用的人所写的,看我的这篇文章说不定还会把你往错误的方向去引,所以请放下那些邪恶的点子。
前言
之前了解到VRC的一些外挂可以实现直接下载目前所在房间内的所有玩家或地图模型资源,甚至好友列表里还出了这么一号人,虽然不是很熟但是也仅仅是因为QQ或者VRChat的好友列表里有这么一个人还给我带来了点麻烦,真是圈子越小神人越神。
刚好最近因为各种原因刚好就有这么点时间,这么点兴趣,对于这方面的实现来了点兴趣,遂着手进行分析,
甚至可以下完就上传,似乎被称之为热更新。模型下载处理之后的操作其实应该都不是什么难事,应该也就是对模型的还原处理与再上传,直接复用官方通过VRC SDK的渠道上传就好了,难点就在于前半段怎么拿到模型与怎么处理模型文件。
安卓端分析
一开始考虑到了Windows端有反作弊,以及可能有额外的壳或者混淆,分析难度比较大,所以想找一个比较容易分析的样本,想到了安卓端也有VRChat,还有给头显用的版本,
通过对于安卓端VRChat的分析,对于模型加载加解密有了一定了解,VRChat下载的模型加密是可选的,服务端在进行文件发放时会根据情况进行版本选择与加密。如果你不在任何游戏实例下,你只能下载由你上传的模型,并且所下载的模型取决于你目前登录的客户端,比如我模拟安卓端的请求去登录与下载我的模型,得到的就是安卓端的模型。


文件后缀为vrca,不带加密,根据文件头可以发现是UnityFS,也就是AssetBundle,通过一些软件是可以解包它的,甚至可以直接在Unity里打开,只不过这个安卓端的模型文件似乎还不完整,导入到Unity打开模型文件似乎是异常的。
Windows端分析
后面单独复制了一份Windows端的去跑静态分析,VRChat的程序无壳无混淆,可以直接扔IDA去分析
加密模型结构
使用AES-256-GCM加密方式,非全文件加密
在github上也有相应的解密仓库可供参考,由于我不是密码学方面的专家,所以这里贴一部分AI分析的结果
文件大致结构如下
定义 A(x) = (x + 15) & ~15,H = A(h+20)(version >= 7;version 6 用 h+20),总大小为 F,信息区物理长度为 C:
若 flags & 0x80:
info_offset = F - C
data_offset = H
否则:
info_offset = H
data_offset = H + C
若 flags & 0x200:
data_offset = A(data_offset)两种物理布局如下;padding 长度由位置计算,不能恒定写成 16:
0x80 = 0: [Header][头对齐][压缩信息区][数据起点对齐][Block 0][Block 1]...
0x80 = 1: [Header][头 / 数据起点对齐][Block 0][Block 1]...[压缩信息区]按 header flags 的压缩方式先解开信息区。这里得到的是块描述和节点描述,还不是模型对象。
data_hash[16]
block_count : UInt32BE
重复 block_count 次:
uncompressed_size : UInt32BE
stored_size : UInt32BE
block_flags : UInt16BE
node_count : UInt32BE
重复 node_count 次:
logical_offset : UInt64BE
size : UInt64BE
node_flags : UInt32BE
path : NUL-terminated stringdata_hash 是原信息区的 16 字节记录
块物理起点为 data_offset + 前面所有块的 stored_size 之和。节点可能跨块,节点偏移不能直接用于原密文文件 seek;要先完成块解密和解压,再在逻辑流切片。
加密块的 GCM 帧
在所分析构建中,加密作用于各块压缩后的字节
[nonce 12][length LE32][ciphertext N][tag 16]
帧总长度 = N + 32
普通满帧 N = 65536 = 0x10000
满帧物理步长 = 65568 = 0x10020
最后一帧可以较短;帧按 UnityFS 块边界收尾长度怎么还原
设一个非空加密块的物理长度为 S,在上述满帧 / 尾帧布局合法时:
frame_count = ceil(S / 65568)
compressed_plain_size = S - 32 * frame_count每帧的密码学输入
K = Base64Decode(匹配的.encryptionKey) # 32 字节
IV = 包体 nonce # 12 字节
C = 包体 ciphertext # N 字节
T = 包体 tag # 16 字节
AAD = 空
P = AES-256-GCM-DecryptAndVerify(K, IV, C, T, AAD)
AES-GCM 同时完成内容解密和完整性认证。对应 96 bit nonce,GCM 初始化计数块 J0 = IV || 0x00000001;CTR 产生密钥流,GHASH 对密文和长度等计算认证值。本格式使用完整 128 bit tag
调用现成密码库:
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
compressed_frame = AESGCM(key32).decrypt(nonce12, ciphertext + tag16, None)解密后模型提取
通过提取可以直接得到一个unity项目,导入打开后可见完整模型资产,当然由于是编译之后的结果所以其实是不能直接上传的,还需要后续的处理,但其实也只是对模型一些原有环境的还原。

Linux中运行VRChat
出于一些需求我需要运行两个VRChat实例,再开一个windows性能开销太大,然后选择在Linux上去运行,你可能会好奇这东西怎么还能在Linux上运行,而且不仅是能运行,运行的还是Windows版的,就是转译的功劳了,通过Proton VRChat连带EAC都可以正常运行。
在此我必须吐槽一下这个B EAC了,EAC启动界面那个进度条你可能以为是在检查文件什么的,实则是tm在下载清单,但是国内这个网络环境懂得都懂,烂完了,下载贼tm慢,下一次甚至能干到半小时,配了代理之后几秒完事。
一开始我尝试了在VMWare中运行,即使VMWare已经设置了一些虚拟器启动选项来绕过虚拟机检测,甚至一开始还成功启动了一次,但是后面因为某种原因始终过不去虚拟机检测。

然后让Codex忙活了大半天,在vmware和wsl发现都没办法绕过虚拟机检测,结果在wsl ubuntu里装了个QEMU,跑了个Ubuntu,做了硬件信息伪装,结果还真就过了检测,这也算是嵌套虚拟机了,但是对于流量还得在WSL再做一层转发到我们的宿主机Windows中。当然具体的步骤这里不给出,如果要学习的话这些其实都是必须涉足到的,对于那些想要直接利用这个干坏事的还是再次说明这篇文章帮不了你多少。

VirtualBox排障
尝试切换虚拟机软件,尝试使用了VirtualBox,但是不知道为什么一直无法创建和启动虚拟机

考虑到可能是安全软件在干扰,为了排障我中间甚至都卸载了我电脑上的所有安全软件,依然在报这个错误,无奈交给AI尝试找到原因,结果原来是完美的诅咒,通过下面的命令停用就能让Virtualbox正常启动
sc.exe stop MessageTransfer

其实为了节省带宽,模型在本地都是有缓存的,但是均为加密,只有在有实例并且正在使用的情况下服务器才会下发解密密钥来在内存中将模型解密加载,一般模型都以vrca后缀下发,文件头为UnityFS,本质上还是Unity AssetBundle。

EAC绕过
经过一些奇技淫巧之后实现了EAC的绕过,成功在没有steam,没有小蓝熊的情况下启动本机的VRChat并加入受保护实例房间。此时的VRChat工作在不受任何保护的环境下,内存读取与注入,文件修改等也就变得轻而易举了。
内存读取
最终根据各种分析成果再让AI做一个自动脚本出来,成果如图,我的宿主机电脑上VRChat正在以无任何外置程序保护的情况下运行,没有steam,没有小蓝熊,EAC被完全绕过,对VRChat实现了任意的内存读取,而再进一步对程序进行修补还能实现对网络流量的解密分析,让抓包软件可以正常工作。
而我目前这个是基于内存提取获得的当前房间下玩家地图信息,后面再尝试对其资源进行修补,如果能够实现对其网络请求的解密,可以不需要读内存,只要把缓存一清让VRChat进行网络资源请求,读到数据包中的密钥即可对资源进行解密。


抓包分析
此前曾尝试在不修改VRChat的情况下进行抓包,并安装的有系统证书,但是VRChat使用白名单证书,就算安装第三方证书也会被拒绝使用,所以一般情况下是获取不到明文流量的。
通过修改程序让VRChat成功使用了Fiddler的证书,可以正常解密VRChat的流量,其中将一些比较有趣的事情分享一下
每次请求VRC服务器响应中的x-powered-by都会带一句随机的短语



甚至还有招聘启事

查看模型文件的流量可以看到使用的是AmazonS3存储桶

后面基于对网络包解析的方法来让AI生成自动化工具,成果大致如下

玩家进出的消息在客户端是通过ws传递的,这里可能还有点问题导致ws流量捕获不成功,只能捕获到模型资产下载的HTTPS流量,但是房间内玩家模型状态是不需要非得通过游戏实际客户端进行获取的,完全可以通过其它端,网页端进行请求获取的,辅助完成信息的监控补全。
模型下载分析
通过流量分析大致得到资源的请求路径

以一个模型请求为模板

在请求模型信息之前游戏中应已经通过websocket获得的模型的基础内容,这时会请求
https://api.vrchat.cloud/api/1/analysis/{文件ID}/{variant}/security
该请求中会带有filetoken文件授权令牌,会话cookie以及twoFactorAuth,响应则包含模型的详细信息以及解密密钥
但这里有额外的限制,一般情况是需要用户在实例中并且得到了用户模型的id与token才能进行下载,前面分析安卓端所下载的模型是由账号自己所上传的模型,即使尝试下载用户目前正在使用的模型也会失败。另外authtoken不能混用,不能使用一个以web身份登录的token去请求安卓端的模型,其它端应该也同理。




预览图的请求则简易很多,只要有id即可
https://api.vrchat.cloud/api/1/image/{fileID}/{variant}/256
虽然请求头还带有MacAddress等等,但是依然可以直接浏览器去请求该地址,然后会将你302到AWS CDN取得文件,直接访问AWS CDN的连接是拿不了的。

但模型的获取就会有额外的检验了,不能像预览图那样直接请求,在取得链接时必须带上你的凭证信息,但是如果拿取得的链接不带凭证信息直接去下载,是可以下载的,所以关键的地方就是那条获取下载链接的请求。


结语
目前VRChat使用的EAC是免费版本,反作弊强度弱,可以被较为轻易的绕过。虽然一整个国庆大概我有一半的时间耗在了这个上面,但是其中还是有大部分时间是浪费在EAC的虚拟环境检测和虚拟机配置上面了,实际在AI的加持下我其实应该两天之内就能实现这样的效果。
VRC不想花钱所使用的小蓝熊太弱以至于从来没有过类似经验的我单单是靠AI都能实现EAC的绕过并实现修改VRC来进行模型的下载解密,这很难评。因为官方的Windows程序本体其实是没什么问题的,该加密的都加密了,UDP WS流量也是加密的,证书也是安全的,在无法修改程序的情况下抓包软件也是抓不到明文的,但问题在于反作弊一旦被绕过其它都是白搭,但是话又说回来,如果提升反作弊的强度反而会出现的情况是游戏可能无法在linux或者其它环境稍微差点的平台上运行,所以这也算是一把双刃剑,需要看官方怎么选择。
