Last active
August 20, 2025 02:50
-
-
Save barncastle/30e4261dbc956b1eb2404480f2112b4e to your computer and use it in GitHub Desktop.
Code for decrypting Pokémon HOME v2.0 Unity AssetBundles
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.IO; | |
| using System.Runtime.InteropServices; | |
| using System.Security.Cryptography; | |
| using System.Text; | |
| namespace ConsoleAppFramework | |
| { | |
| public static class ABA | |
| { | |
| private const string Key_0 = "lrZ6++Ln5tLnBsJk.ae6J8BLaLbMhVn@"; | |
| private const string IV_0 = "X6TU@VYU$HyqKy57PfwWg7.t7wk2oqtg"; | |
| private const int DEFAULT_KEYSIZE = 256; | |
| private const int DEFAULT_BLOCKSIZE = 256; | |
| public static FileHeader ExtractPackage(string abapPath, string outDirectory) | |
| { | |
| Directory.CreateDirectory(outDirectory); | |
| using (var fs = File.OpenRead(abapPath)) | |
| using (var br = new BinaryReader(fs)) | |
| { | |
| // read the header | |
| var header = new FileHeader() | |
| { | |
| Magic = br.ReadUInt32(), | |
| RootHeadOffset = br.ReadInt32(), | |
| NameHeadOffset = br.ReadInt32(), | |
| DataHeadOffset = br.ReadInt32() | |
| }; | |
| header.Bundles = new BundleInfo[header.DataCount]; | |
| // read each package | |
| for (var i = 0; i < header.DataCount; i++) | |
| { | |
| // seek to package info offset | |
| fs.Seek(i * 16 + header.RootHeadOffset, SeekOrigin.Begin); | |
| // read package info details | |
| var bundle = header.Bundles[i] = new BundleInfo() | |
| { | |
| NameOffset = br.ReadInt32(), | |
| NameSize = br.ReadInt32(), | |
| DataOffset = br.ReadInt32(), | |
| DataSize = br.ReadInt32(), | |
| }; | |
| // seek to name offset and read | |
| fs.Seek(header.NameHeadOffset + bundle.NameOffset, SeekOrigin.Begin); | |
| bundle.Name = Encoding.UTF8.GetString(br.ReadBytes(bundle.NameSize)); | |
| // seek to data offset and read | |
| fs.Seek(header.DataHeadOffset + bundle.DataOffset, SeekOrigin.Begin); | |
| bundle.Data = br.ReadBytes(bundle.DataSize); | |
| // decrypt package contents | |
| Decrypt(bundle, outDirectory); | |
| } | |
| return header; | |
| } | |
| } | |
| public static void Decrypt(BundleInfo package, string outPath) | |
| { | |
| var filename = Path.Combine(outPath, package.Name + ".unity3d"); | |
| using (var ms = new MemoryStream(package.Data)) | |
| DecryptImplementation(ms, filename); | |
| } | |
| public static void Decrypt(string abaPath, string outPath) | |
| { | |
| using (var fsInput = File.OpenRead(abaPath)) | |
| DecryptImplementation(fsInput, outPath); | |
| } | |
| private static void DecryptImplementation(Stream stream, string outPath) | |
| { | |
| using (var algo = new RijndaelManaged()) | |
| using (var strInput = stream) | |
| using (var strOutput = File.Create(outPath)) | |
| { | |
| // set up our params | |
| algo.Padding = PaddingMode.PKCS7; | |
| algo.Mode = CipherMode.CBC; | |
| algo.BlockSize = DEFAULT_BLOCKSIZE; | |
| algo.KeySize = DEFAULT_KEYSIZE; | |
| algo.Key = Encoding.UTF8.GetBytes(Key_0); | |
| algo.IV = Encoding.UTF8.GetBytes(IV_0); | |
| // calculate our buffer size | |
| var bufferLen = Math.Min(strInput.Length, 1024); | |
| var buffer = new byte[bufferLen]; | |
| // create a crypto stream and process bufferLen bytes | |
| using (var transform = new CryptoStream(strInput, algo.CreateDecryptor(), CryptoStreamMode.Read, true)) | |
| transform.Read(buffer, 0, buffer.Length); | |
| // write the decrypted bytes to our output | |
| strOutput.Write(buffer, 0, buffer.Length); | |
| // finally, copy the remaining unread bytes from the input to the output | |
| strInput.CopyTo(strOutput); | |
| } | |
| } | |
| [StructLayout(LayoutKind.Sequential)] | |
| public struct FileHeader | |
| { | |
| public uint Magic; // "ppir" aka "pupitar" | |
| public int RootHeadOffset; | |
| public int NameHeadOffset; | |
| public int DataHeadOffset; | |
| public BundleInfo[] Bundles; // [DataCount] | |
| public int RootBufferSize => NameHeadOffset - RootHeadOffset; | |
| public int NameBufferSize => DataHeadOffset - NameHeadOffset; | |
| public int DataCount => RootBufferSize >> 4; | |
| } | |
| [StructLayout(LayoutKind.Sequential)] | |
| public struct BundleInfo | |
| { | |
| public int NameOffset; // + header.NameHeadOffset | |
| public int NameSize; | |
| public int DataOffset; // + header.DataHeadOffset | |
| public int DataSize; | |
| public string Name; | |
| public byte[] Data; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
found this in ram