Skip to content

Instantly share code, notes, and snippets.

@Pldare
Created August 10, 2021 01:46
Show Gist options
  • Save Pldare/ebf704c752a8d77ff9603d4adfe54083 to your computer and use it in GitHub Desktop.
Save Pldare/ebf704c752a8d77ff9603d4adfe54083 to your computer and use it in GitHub Desktop.
vroid web view vrm decrypt
using System;
using System.Security.Cryptography;
using System.IO;
using System.IO.Compression;
namespace Vroid
{
class Vroiddec
{
static void Main(string[] args)
{
using (FileStream fs = new FileStream(args[0],FileMode.Open)) {
using (BinaryReader bs = new BinaryReader(fs)) {
int buff_size=(int)(fs.Length)-48;
RijndaelManaged rDel=new RijndaelManaged();
rDel.IV=bs.ReadBytes(16);
rDel.Key=bs.ReadBytes(32);
rDel.Mode=CipherMode.CBC;
byte[] resultarray=rDel.CreateDecryptor().TransformFinalBlock(bs.ReadBytes(buff_size),0,buff_size);
using(MemoryStream ms =new MemoryStream(resultarray)){
using(GZipStream gzs=new GZipStream(ms,CompressionMode.Decompress)){
using(FileStream df=new FileStream(args[0]+".dec",FileMode.OpenOrCreate,FileAccess.Write))
{
int data;
while((data=gzs.ReadByte())!=-1)
{
df.WriteByte((byte)data);
}
}
}
}
}
}
log_msg("Done!");
}
static void log_msg(string msg)
{
Console.WriteLine(msg);
}
}
}
@darbdarb
Copy link

i was able to get the model to at least import by modifying the import_gltf2_with_indices method of the blender vrm importer with

        json_dict['extensionsRequired'].pop(0)
        for i in range(len(json_dict['textures'])):
            if 'extensions' in json_dict['textures'][i]:
                json_dict['textures'][i].pop('extensions')

(it's just a dumb hack to remove the extension requirement)

However, the meshes seem to not be quite right image

I will keep looking into this and update if I find more

nice nice keep us updated :)

@Pldare
Copy link
Author

Pldare commented Oct 28, 2023

hihihi Everyone
I’m glad that everyone pays attention to this code of mine
I share some json parsing methods of gltf models for everyone to facilitate extraction and error correction.
get block info

import json
import sys
with open(sys.argv[1],encoding='utf-8') as f:
    data = json.load(f)


for i in data.keys():
    a=data[i]
    #print(type(a))

accessors = data["accessors"]
buffer_views = data["bufferViews"]
type_num_dict = {"SCALAR": 1, "VEC2": 2, "VEC3": 3, "VEC4": 4, "MAT4": 16}

type_dict={
    5120: "BYTE",
    5121: "UNSIGNED_BYTE",
    5122: "SHORT",
    5123: "UNSIGNED_SHORT",
    5124: "INT",
    5125: "UNSIGNED_INT",
    5126: "FLOAT"
}

for accessor_index, accessor in enumerate(accessors):
    type_count = type_num_dict[accessor["type"]]
    #ct=accessor["componentType"]
    pos=buffer_views[accessor["bufferView"]]["byteOffset"]
    count=accessor["count"]
    ac=accessor["componentType"]
    print(accessor_index,count,type_dict[ac],type_count==1,pos)

get mesh info

print("mesh")
for n, mesh in enumerate(data["meshes"]):
    #print(n,mesh.keys())
    for j,primive in enumerate(mesh["primitives"]):
        #print(primive)
        if primive["mode"] != 4:
            raise "1"
        dbb=primive["indices"]
        print(dbb)
        
        va=primive["attributes"]
        print(va)

The id in meshinfo corresponds to the id in blockinfo
I will provide the complete model extraction code later

@darbdarb
Copy link

hihihi Everyone I’m glad that everyone pays attention to this code of mine I share some json parsing methods of gltf models for everyone to facilitate extraction and error correction. get block info

import json
import sys
with open(sys.argv[1],encoding='utf-8') as f:
    data = json.load(f)


for i in data.keys():
    a=data[i]
    #print(type(a))

accessors = data["accessors"]
buffer_views = data["bufferViews"]
type_num_dict = {"SCALAR": 1, "VEC2": 2, "VEC3": 3, "VEC4": 4, "MAT4": 16}

type_dict={
    5120: "BYTE",
    5121: "UNSIGNED_BYTE",
    5122: "SHORT",
    5123: "UNSIGNED_SHORT",
    5124: "INT",
    5125: "UNSIGNED_INT",
    5126: "FLOAT"
}

for accessor_index, accessor in enumerate(accessors):
    type_count = type_num_dict[accessor["type"]]
    #ct=accessor["componentType"]
    pos=buffer_views[accessor["bufferView"]]["byteOffset"]
    count=accessor["count"]
    ac=accessor["componentType"]
    print(accessor_index,count,type_dict[ac],type_count==1,pos)

get mesh info

print("mesh")
for n, mesh in enumerate(data["meshes"]):
    #print(n,mesh.keys())
    for j,primive in enumerate(mesh["primitives"]):
        #print(primive)
        if primive["mode"] != 4:
            raise "1"
        dbb=primive["indices"]
        print(dbb)
        
        va=primive["attributes"]
        print(va)

The id in meshinfo corresponds to the id in blockinfo I will provide the complete model extraction code later

ur amazing <3, after 2 years i didnt expect u to comment but here u are and im here for it, keep up the good work ive got notifcations on for this post

@DerpyMario
Copy link

any updates?

@CetaceanNation
Copy link

VRoid is now using ZSTD in place of GZIP. I wrote a Python script which handles the new decompression along with the decryption https://github.com/CetaceanNation/misc-scripts/blob/main/vroid-hub-downloader/vroid-hub-downloader.py#L52. If @Pldare completes their extraction code it would be nice to incorporate or at least add to my workflow.

@OwariSync
Copy link

i was able to get the model to at least import by modifying the import_gltf2_with_indices method of the blender vrm importer with

        json_dict['extensionsRequired'].pop(0)
        for i in range(len(json_dict['textures'])):
            if 'extensions' in json_dict['textures'][i]:
                json_dict['textures'][i].pop('extensions')

(it's just a dumb hack to remove the extension requirement)

However, the meshes seem to not be quite right image

I will keep looking into this and update if I find more

Sorry for what might be an obvious question, but how can I implement this as well? Using Cetacean's new script, I was able to get a model, but I can't import it due to this extension requirement.

@seed93
Copy link

seed93 commented Jan 19, 2024

How to crack this extension?
Even skip this extension, the geometry is wrong as before. Any ideas?

@TenjoinWai
Copy link

Tried opening this on this model but the resulting VRM seems to have blanked out textures, is there something I'm missing or I'm using the incorrect endpoint?

I modernized the script a bit and documented a lot of things so everyone can understand how it works now (I used .NET 6.0 here).

using System.Security.Cryptography;
using System.IO.Compression;

namespace Vroid
{
    class VroidDecode
     {
         public static void Main(string[] args) 
         {

            // read the file from arguments, then get the binary representation
            var fs = new FileStream(args[0], FileMode.Open);
            var bs = new BinaryReader(fs);

            // VRoid Hub Encrypted VRMs usually have a the following data structure:
            // * The first 16 bytes is the Initialization Vector
            // * 32 bytes is the actual AES key
            // * The cipher is AES-CBC
            // * Then the rest of the VRM is Gzipped compressed for web transfer.
            // 
            // with this in mind, we now try to access those specific bits first.
            int bufferSize = ((int)fs.Length)-48;
            using Aes aes = Aes.Create();
            aes.IV = bs.ReadBytes(16);
            aes.Key = bs.ReadBytes(32);
            aes.Mode = CipherMode.CBC;

            // now everything is set, we now try to decrypt it!
            var decrypted = aes.CreateDecryptor().TransformFinalBlock(bs.ReadBytes(bufferSize), 0, bufferSize);

            // of course now we got the decrypted part, we still have to deal with the Gzip compression.
            var decryptedStream = new MemoryStream(decrypted);
            var decompressedStream = new GZipStream(decryptedStream, CompressionMode.Decompress);
            var finalStream = new FileStream(args[0] + ".dec", FileMode.OpenOrCreate, FileAccess.Write);

            int data;
            while ((data = decompressedStream.ReadByte()) != -1)
            {
                finalStream.WriteByte((byte)data);
            }

            Console.WriteLine($"Done! Saved as {args[0]}.dec");

        }
     }
}

I have downloaded a model from vroidhub in Feb 2021. all i got is a xs3 file. the model itself may be vrm or pmx format since it could display vmd motion online. and when i process the xs2 file with the cs code, things always go wrong. I wonder how to decypt this xs3 file correctly, could you give some suggestions? the files are here:https://github.com/TenjoinWai/symphony-miku/tree/main

@Mika-Ra
Copy link

Mika-Ra commented Mar 31, 2024

Just think this will be helpful?
https://inside.pixiv.blog/2024/02/05/120000

@TenjoinWai
Copy link

Just think this will be helpful? https://inside.pixiv.blog/2024/02/05/120000

now i get a decrypted .bin file. it seems that this .bin file contains a pmx model and its textures and meshes. but i cannnot read this file. hope somebody could give me some suggestions. https://github.com/TenjoinWai/symphony-miku

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment