v0.90.10
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -8,16 +10,20 @@ namespace AssetStudio
|
||||
private const int DataOffset = 0x2A;
|
||||
private const int KeySize = 0x1000;
|
||||
private const int SeedBlockSize = 0x800;
|
||||
private const int BufferSize = 0x10000;
|
||||
|
||||
public static XORStream Decrypt(FileReader reader, Blk blk)
|
||||
{
|
||||
reader.Endian = EndianType.LittleEndian;
|
||||
|
||||
var signature = reader.ReadStringToNull();
|
||||
Logger.Verbose($"Signature: {signature}");
|
||||
var count = reader.ReadInt32();
|
||||
Logger.Verbose($"Key size: {count}");
|
||||
var key = reader.ReadBytes(count);
|
||||
reader.Position += count;
|
||||
var seedSize = Math.Min(reader.ReadInt16(), blk.SBox.IsNullOrEmpty() ? SeedBlockSize : SeedBlockSize * 2);
|
||||
Logger.Verbose($"Seed size: 0x{seedSize:X8}");
|
||||
|
||||
if (!blk.SBox.IsNullOrEmpty() && blk.Type.IsGI())
|
||||
{
|
||||
@@ -47,6 +53,8 @@ namespace AssetStudio
|
||||
var keyHigh = BinaryPrimitives.ReadUInt64LittleEndian(key.AsSpan(8, 8));
|
||||
var seed = keyLow ^ keyHigh ^ keySeed ^ blk.InitSeed;
|
||||
|
||||
Logger.Verbose($"Seed: 0x{seed:X8}");
|
||||
|
||||
var mt64 = new MT19937_64(seed);
|
||||
var xorpad = new byte[KeySize];
|
||||
for (int i = 0; i < KeySize; i += 8)
|
||||
@@ -56,5 +64,48 @@ namespace AssetStudio
|
||||
|
||||
return new XORStream(reader.BaseStream, DataOffset, xorpad);
|
||||
}
|
||||
|
||||
public static IEnumerable<long> GetOffsets(this XORStream stream, string path)
|
||||
{
|
||||
if (AssetsHelper.TryGet(path, out var offsets))
|
||||
{
|
||||
foreach(var offset in offsets)
|
||||
{
|
||||
stream.Offset = offset;
|
||||
yield return offset;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using var reader = new FileReader(path, stream, true);
|
||||
var signature = reader.FileType switch
|
||||
{
|
||||
FileType.BundleFile => "UnityFS\x00",
|
||||
FileType.Mhy0File => "mhy0",
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
|
||||
Logger.Verbose($"Prased signature: {signature}");
|
||||
|
||||
var signatureBytes = Encoding.UTF8.GetBytes(signature);
|
||||
var buffer = BigArrayPool<byte>.Shared.Rent(BufferSize);
|
||||
while (stream.Remaining > 0)
|
||||
{
|
||||
var index = 0;
|
||||
var absOffset = stream.AbsolutePosition;
|
||||
var read = stream.Read(buffer);
|
||||
while (index < read)
|
||||
{
|
||||
index = buffer.AsSpan(0, read).Search(signatureBytes, index);
|
||||
if (index == -1) break;
|
||||
var offset = absOffset + index;
|
||||
stream.Offset = offset;
|
||||
yield return offset;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
BigArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ namespace AssetStudio
|
||||
|
||||
var encryptedBlockSize = Math.Min(0x10 * ((data.Length - 0x94) >> 7), BlockSize);
|
||||
|
||||
Logger.Verbose($"Encrypted block size: {encryptedBlockSize}");
|
||||
if (!mr0k.InitVector.IsNullOrEmpty())
|
||||
{
|
||||
for (int i = 0; i < mr0k.InitVector.Length; i++)
|
||||
@@ -47,6 +48,8 @@ namespace AssetStudio
|
||||
var seed2 = BinaryPrimitives.ReadUInt64LittleEndian(key3);
|
||||
var seed = seed2 ^ seed1 ^ (seed1 + (uint)data.Length - 20);
|
||||
|
||||
Logger.Verbose($"Seed: 0x{seed:X8}");
|
||||
|
||||
var encryptedBlock = data.Slice(0x94, encryptedBlockSize);
|
||||
var seedSpan = BitConverter.GetBytes(seed);
|
||||
for (var i = 0; i < encryptedBlockSize; i++)
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace AssetStudio
|
||||
private static readonly byte[] Signature = new byte[] { 0xEE, 0xDD };
|
||||
public static void Decrypt(Span<byte> bytes)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt block with NetEase encryption...");
|
||||
|
||||
var (encryptedOffset, encryptedSize) = ReadHeader(bytes);
|
||||
var encrypted = bytes.Slice(encryptedOffset, encryptedSize);
|
||||
var encryptedInts = MemoryMarshal.Cast<byte, int>(encrypted);
|
||||
@@ -77,7 +79,7 @@ namespace AssetStudio
|
||||
}
|
||||
private static (int, int) ReadHeader(Span<byte> bytes)
|
||||
{
|
||||
var index = bytes.Search(Signature, 0);
|
||||
var index = bytes.Search(Signature);
|
||||
if (index == -1 || index >= 0x40)
|
||||
{
|
||||
throw new Exception("Header not found !!");
|
||||
@@ -127,6 +129,7 @@ namespace AssetStudio
|
||||
throw new Exception("Unsupported version");
|
||||
}
|
||||
var versionString = version.ToString("X4");
|
||||
Logger.Verbose($"Bundle version: {versionString}");
|
||||
Encoding.UTF8.GetBytes(versionString, bytes);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace AssetStudio
|
||||
|
||||
public static void Decrypt(Span<byte> data, string path)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt block with OPFP encryption...");
|
||||
if (IsEncryptionBundle(path, out var key, out var version))
|
||||
{
|
||||
switch (version)
|
||||
@@ -41,30 +42,39 @@ namespace AssetStudio
|
||||
{
|
||||
if (V1_Prefixes.Any(prefix => relativePath.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
Logger.Verbose("Path matches with V1 prefixes, generatring key...");
|
||||
key = (byte)Path.GetFileName(relativePath).Length;
|
||||
version = 1;
|
||||
Logger.Verbose($"version: {version}, key: {key}");
|
||||
return true;
|
||||
}
|
||||
else if (V0_Prefixes.Any(prefix => relativePath.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
Logger.Verbose("Path matches with V2 prefixes, generatring key...");
|
||||
|
||||
key = (byte)relativePath.Length;
|
||||
version = 0;
|
||||
Logger.Verbose($"version: {version}, key: {key}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Logger.Verbose($"Unknown encryption type");
|
||||
key = 0x00;
|
||||
version = 0;
|
||||
return false;
|
||||
}
|
||||
private static bool IsFixedPath(string path, out string fixedPath)
|
||||
{
|
||||
Logger.Verbose($"Fixing path before checking...");
|
||||
var dirs = path.Split(Path.DirectorySeparatorChar);
|
||||
if (dirs.Contains(BaseFolder))
|
||||
{
|
||||
var idx = Array.IndexOf(dirs, BaseFolder);
|
||||
Logger.Verbose($"Seperator found at index {idx}");
|
||||
fixedPath = string.Join(Path.DirectorySeparatorChar, dirs[(idx+1)..]).Replace("\\", "/");
|
||||
return true;
|
||||
}
|
||||
Logger.Verbose($"Unknown path");
|
||||
fixedPath = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,11 @@ namespace AssetStudio
|
||||
DecryptKey(signatureKey, signatureBytes);
|
||||
|
||||
var str = Encoding.UTF8.GetString(signatureBytes);
|
||||
Logger.Verbose($"Decrypted signature is {str}");
|
||||
if (str != Signature)
|
||||
throw new Exception("Invalid Signature !!");
|
||||
{
|
||||
throw new Exception($"Invalid Signature, Expected {Signature} but found {str} instead");
|
||||
}
|
||||
|
||||
DecryptKey(infoKey, infoBytes);
|
||||
|
||||
@@ -41,19 +44,20 @@ namespace AssetStudio
|
||||
var idx = (i % 4 * 4) + (i / 4);
|
||||
Sub[idx] = subBytes[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static bool SetKey(Entry entry)
|
||||
{
|
||||
Logger.Verbose($"Initializing decryptor with key {entry.Key}");
|
||||
try
|
||||
{
|
||||
using (var aes = Aes.Create())
|
||||
{
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Key = Convert.FromHexString(entry.Key);
|
||||
using var aes = Aes.Create();
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Key = Convert.FromHexString(entry.Key);
|
||||
|
||||
Encryptor = aes.CreateEncryptor();
|
||||
}
|
||||
Encryptor = aes.CreateEncryptor();
|
||||
Logger.Verbose($"Decryptor initialized !!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user