Skip to content

Instantly share code, notes, and snippets.

@barncastle
Last active August 20, 2025 02:50
Show Gist options
  • Save barncastle/30e4261dbc956b1eb2404480f2112b4e to your computer and use it in GitHub Desktop.
Save barncastle/30e4261dbc956b1eb2404480f2112b4e to your computer and use it in GitHub Desktop.
Code for decrypting Pokémon HOME v2.0 Unity AssetBundles
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;
}
}
}
@ChicoEevee
Copy link

image
found this in ram

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