VirtualDesktop 是一项离线 LAN 串流恢复工程。当前主线是 1.34.22.0(66 处 IL 补丁 / 5 个程序集,含水印 C2acked by dwgx);
1.34.19.0 与 1.34.18.0 的切片保留为谱系真源。项目不修改网络栈、不更改 DNS、不伪造任何平台凭证,
而是在 .NET IL 字节码层实施精确修改:定位并中和应用内的反篡改校验指令,
使运行时回退至 JIT 执行补丁后的 IL,从而在无外部网络的环境下恢复局域网串流。
Windows 端工具 VDH 与 APK 分开发布;头显包分 EN / ZH 两份,里面不带 VDH。
APK 里藏着两层代码:运行时优先执行的 AOT 机器码,和可被改写的 IL 字节码。
AOT 在运行时覆盖托管 IL —— 即使你改了 IL,机器码也照旧执行,JADX 这类工具更是只能看到 Xamarin 桥接层。
我们的解法不是去逆机器码,而是移除全部 AOT(22.0 为 172 个 libaot-*.so),逼运行时回退到 JIT,让它执行我们补丁过的 IL。
本项目与常见做法存在本质区别:网络上流传的"汉化整合包"普遍采用 修改 hosts 劫持鉴权域名 + 安装原版 APK 的方式,改变的是整台头显的网络环境,应用本身未作任何修改。 本项目则相反:仅修改应用自身的字节码,不触碰任何网络配置。两种方式的对照如下:
改整机 DNS、把鉴权域名指到写死 IP、黑洞云端,装的是未补丁原版 APK。本质是绕过网络验证。
在 .NET IL 层做精确手术,把自毁指令 NOP 掉,不碰 DNS、不重定向任何域名、完全可逆、全开源。
没有任何现成工具能改这种 ELF → XABA assembly store → 每个程序集 XALZ/LZ4 压缩的 .NET PE 的多层封装。
所以我们自己写了三段式管线,逐字节解构再无损重组。
解析 ELF→定位 XABA 标记→按 28 字节描述符切片→LZ4 解压还原 .NET PE;
再解析 PE section、RVA→文件偏移,逐方法读 IL header(tiny/fat),打字节级补丁后以伪装名写回。
LZ4 high-compression(lvl 12) 重压每个补丁后 DLL,逐条比对原压缩大小防 OVERFLOW,
重建全部 XALZ 记录与描述符表,按体积增量修正 ELF 的 e_shoff 与 section sh_size。
剔除旧签名与全部 168 个 libaot-*.so,补丁 blob 以 ZIP_STORED 回填(mmap 加载要求),
zipalign 4 对齐,apksigner v3+v2 签名。18.0 产出 signed_v12_no_aot.apk;
22.0 对位脚本是 build_v22.py;19.0 是 build_v19.py / build_zh_v19.py。
上方 CFG 与导航条上的 44 处黄色刻度是 1.34.18.0 的教学切片(5 个程序集,含把 Xenko.OpenXR.dll::Kill() 打成 ret)。
当前主线 1.34.19.0 把同一套语义迁到新 blob:entry 181→185、索引全偏移、官方删掉那个空壳 Kill(),
token 0x0A0002F1 变成真 Process.Kill()。补丁数变成 56 / 4 个程序集。硬编码 Entry# 或 RVA 跨版本即崩。
| 程序集 | 1.34.18.0 | 1.34.19.0 |
|---|---|---|
| OpenTK-1.1 / EglContext | #43 | #42 |
| VirtualDesktop.Mobile | #52 | #51 |
| Xenko.OpenXR Kill() | #49 ret | 已删除 · 改 NOP 自毁 call |
| Xenko.VR ReleaseVR | #61 | #60 |
| VrApp / Path C 昵称 | #81 Shared | #82 Android |
按 module name 定位 4 个 PE,56 处带字节断言的 IL 补丁。码率上限 500→960。异步状态机改 MoveNext,不在 55 字节 wrapper 里硬塞。
Path C:运行时读 /sdcard/vd_account.txt,缺文件走烤入默认。一份签名 APK 发给不同人,改昵称不重签。
EN/ZH 安装脚本单一生成源。cmd 块语法只在真 Windows 上爆;bat_test/ + GitHub Actions 52 用例跑真 cmd,不再靠静态审查。
22.0 blob 仍是 185 entries,Mobile/OpenTK 序号没再漂,但 发现协议在 #53 VirtualDesktop.Net.dll。
Streamer 听的是 IPv6 DualMode [::]:38850,不是 0.0.0.0。Quest UdpClient(38850) 绑死端口会空列表。
水印 C2acked by dwgx / C2rcked by dwgx 按 18.0 的 48e/48f 写回电脑页警告栏。
Path C(/sdcard/vd_account.txt)在 22 上打坏 MemberRef,冻结包禁用。
头显包与 Windows 端 VDH 分开:APK zip 不带 VDH;VDH 走独立仓库做检查更新。
| 程序集 | 1.34.19.0 | 1.34.22.0 |
|---|---|---|
| OpenTK-1.1 | #42 | #42 |
| VirtualDesktop.Mobile | #51 | #51 · 544256B |
| VirtualDesktop.Net FindComputers | 未补 | #53 临时 UDP + ff02::1 |
| Xenko.OpenXR | Kill 已删 | #59 face-tracking → ret |
| Xenko.VR ReleaseVR | #60 → ret | #60 禁止 ret(每帧 CreateHmd) |
| VrApp PAT / 水印 | #82 Path C | #82 烤入 dwgx · 48e/48f |
66 处 IL。出货码率上限 500(不再烤 960)。Kill token 0x0A0002F7。#53 发现。假 PAT 只许 MoveNext ldflda+SetResult。加码率走 VDH Write IL。
AesStream 帧 + OculusQuest=1 + 账户名。本机探测 1344B 回复即 Streamer 局域网活着。
localize_v22.py + bundle 汉化 + NotoSC(Verdana32 保持 Offline)。ZH APK SHA 39C52DF5…。上限同样 500。
下面是 22.0 真机 LAN 通了之后的动作,不是计划。GitHub Pages 这份档案跟着仓库 docs/about.html 走。
| 日 | 动作 | 结果 / 不要再踩 |
|---|---|---|
| 08-28 | Quest 1.34.22.0 LAN 冻结包 | 真机电脑列表可用。EN SHA B0604A84…712A 上限 500。签名 56464486。HorizonOS 只点「打开应用」,点「恢复」会装回商店 10704。旧包 8DEEF4FF 曾烤 960,已作废。 |
| 08-28 | #53 FindComputersAsync | Android 绑死 UDP 38850 会空列表。改临时端口 + 强制 ff02::1。Streamer 听的是 DualMode [::]:38850。 |
| 08-28 | 水印 48e/48f | 电脑页 C2acked by dwgx / C2rcked by dwgx。发现路径仍跳过 Wi-Fi/Meta abort。 |
| 08-28 | pathc_v22.py | 禁跑。Computers tab:Memberref parent unknown: class: 6, index 1。昵称烤在 #US 0x1220 dwgx。 |
| 08-28 | 分发 README 乱码 | PowerShell here-string 把 UTF-8 中文编坏。现改为仓库里的 UTF-8 源文件拷进 zip。install_zh.bat 仍是 GBK + chcp 936。 |
| 08-28 | VDH 0.4.7 | C# net48 WinForms。仓库 dwgx/VirtualDesktopHelper tag v0.4.7。Write IL 改 APK 码率上限。LZ4 打进 exe。OTA 读 VERSION.txt + Release SHA-256。 |
| 08-28 | APK 与 VDH 拆包 | EN / ZH 头显 zip 不带 VDH。选文件夹安装时只认已知 SHA;不是我们的包会警告。 |
brtrue.s → br.s 即中和一个反篡改自毁陷阱补丁的核心原则:将触发自毁逻辑的调用替换为 NOP 指令使其失效,同时不生成任何伪造凭证。
下方为键盘组件反篡改校验的实际修改过程——原逻辑在签名校验失败时调用 Activity.Finish() 终止进程:
IL_0042 callvirt CanScroll // _pnCapsLock=null
IL_0047 brtrue.s IL_0060
IL_0049 callvirt Activity.Finish()
IL_004e throw / kill
IL_0042 nop nop nop // trap 中和
IL_0047 br.s IL_0060 // 无条件跳过
IL_0049 nop nop nop nop nop
IL_004e // 自毁已移除
下面这张表是 1.34.18.0 教学集:44 处补丁分布在 5 个程序集。1.34.19.0 的对应关系见上一节版本谱系(56 / 4, Kill 空壳已不存在)。
| 程序集(伪装名) | 作用域 | 补丁分布 |
|---|---|---|
| VirtualDesktop.Mobile → Xenko.dll | 主逻辑 / 游戏循环 / ControlPanel / 虚拟键盘 | 27 |
| VirtualDesktop.Mobile.Shared → ...TypeConverter.dll | 签名 / entitlement 自检 | 14 |
| VirtualDesktop.Net → OpenTK-1.1 | EGL 上下文 MakeCurrent(surfaceless) | 1 |
| Xenko.OpenXR → VD.Core | 进程管理 Kill() | 1 |
| Xenko.VR → Xenko.Native.dll | VR 硬件 OpenXRHMD.ReleaseVR | 1 |
反篡改 trap 绕过:_invalidHash 触发 NullRef trap,签名校验失败后调 Activity.Finish() 自毁。以控制流改写 + 字节填充双重中和。
渲染生命周期重连:构造 surfaceless EGL context(WindowInfo.Surface → IntPtr.Zero),放行 xrCreateSession,强制 _isPaused=false 维持 GameLoop 持续 Tick()。
菜单不可见根因定位:离线模式下 streamingSource=0 致 cylinder 尺寸归零。NOP 掉条件门,几何重建恢复,菜单重新渲染。
IL 字节级不变量:同步 Fat header 的 code_size、ret 后 NOP 填充、清空 SEH 异常表、精确改写 metadata token —— 任一错位即 InvalidProgramException。
通过对 PC Streamer(dnSpy 反编译)与 APK 侧交叉分析得出关键裁决:LAN 准入门是 账号字符串匹配 + AES 对称加密,而不是平台签名校验。所以把自毁 NOP 掉就够了,根本不必造假 token。
UDP 发现包只含 RSA 公钥串、1 字节 Platform、UTF-8 AccountID —— 不含任何 Meta entitlement,不含 UserProof。
它仅用于 vrdesktop.net/ComputerRegistry 的注册调用。离线 LAN 完全绕开该路径,因此补丁只需使应用在校验失败时不再触发自毁逻辑。
界面文本不在 APK 的 Android 资源里,而是打包进 Xenko/Stride 引擎的私有 bundle
(assets/data/db/bundles/default.bundle)。我们在补丁管线里多挂一步:把汉化后的两个 bundle
以 STORED 原样回填,出一个可直接安装的中文 APK —— 补丁与汉化一次成型,同一把签名。
补丁脚本、反编译参考、126 份实验报告复盘的完整决策链,以及这个站点的离线汉化构建流程,都在仓库里。