Skip to content

Commit 42245a3

Browse files
authored
Fix UnityCN Decryption + brute-force function (#168)
* fix UnityCN decryption * ArchiveStorageManager - add brute-force function and better error description. * Update __init__.py
1 parent ba57286 commit 42245a3

File tree

3 files changed

+49
-11
lines changed

3 files changed

+49
-11
lines changed

UnityPy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.9.24"
1+
__version__ = "1.9.25"
22

33
from .environment import Environment
44
from .helpers.ArchiveStorageManager import set_assetbundle_decrypt_key

UnityPy/files/BundleFile.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# TODO: implement encryption for saving files
22
from collections import namedtuple
33
import re
4-
from typing import Tuple
4+
from typing import Tuple, Union
55

66
from . import File
77
from ..enums import ArchiveFlags, ArchiveFlagsOld, CompressionFlags
@@ -106,6 +106,9 @@ def read_fs(self, reader: EndianBinaryReader):
106106
else:
107107
self.dataflags = ArchiveFlags(self.dataflags)
108108

109+
if self.dataflags & self.dataflags.UsesAssetBundleEncryption:
110+
self.decryptor = ArchiveStorageManager.ArchiveStorageDecryptor(reader)
111+
109112
if self.version >= 7:
110113
reader.align_stream(16)
111114

@@ -117,8 +120,6 @@ def read_fs(self, reader: EndianBinaryReader):
117120
blocksInfoBytes = reader.read_bytes(compressedSize)
118121
reader.Position = start
119122
else: # 0x40 kArchiveBlocksAndDirectoryInfoCombined
120-
if self.dataflags & self.dataflags.UsesAssetBundleEncryption:
121-
self.decryptor = ArchiveStorageManager.ArchiveStorageDecryptor(reader)
122123
blocksInfoBytes = reader.read_bytes(compressedSize)
123124

124125
blocksInfoBytes = self.decompress_data(
@@ -385,7 +386,11 @@ def save_fs(self, writer: EndianBinaryWriter, data_flag: int, block_info_flag: i
385386
writer.Position = writer_end_pos
386387

387388
def decompress_data(
388-
self, compressed_data: bytes, uncompressed_size: int, flags: int, index: int = 0
389+
self,
390+
compressed_data: bytes,
391+
uncompressed_size: int,
392+
flags: Union[int, ArchiveFlags, ArchiveFlagsOld],
393+
index: int = 0,
389394
) -> bytes:
390395
"""
391396
Parameters
@@ -401,7 +406,7 @@ def decompress_data(
401406
-------
402407
bytes
403408
The decompressed data."""
404-
comp_flag = flags & ArchiveFlags.CompressionTypeMask
409+
comp_flag = CompressionFlags(flags & ArchiveFlags.CompressionTypeMask)
405410

406411
if comp_flag == CompressionFlags.LZMA: # LZMA
407412
return CompressionHelper.decompress_lzma(compressed_data)

UnityPy/helpers/ArchiveStorageManager.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# based on: https://github.com/Razmoth/PGRStudio/blob/master/AssetStudio/PGR/PGR.cs
2+
import re
23
from typing import Tuple, Union
34

45
from ..streams import EndianBinaryReader
@@ -20,7 +21,8 @@ def set_assetbundle_decrypt_key(key: Union[bytes, str]):
2021

2122
def read_vector(reader: EndianBinaryReader) -> Tuple[bytes, bytes]:
2223
data = reader.read_bytes(0x10)
23-
key = reader.read_string_to_null().encode("utf-8", "surrogateescape")
24+
key = reader.read_bytes(0x10)
25+
reader.Position += 1
2426

2527
return data, key
2628

@@ -32,6 +34,28 @@ def decrypt_key(key: bytes, data: bytes, keybytes: bytes):
3234
return bytes(x ^ y for x, y in zip(data, key))
3335

3436

37+
def brute_force_key(
38+
fp: str,
39+
key_sig: bytes,
40+
data_sig: bytes,
41+
pattern: re.Pattern = re.compile(rb"(?=(\w{16}))"),
42+
verbose: bool = False,
43+
):
44+
with open(fp, "rb") as f:
45+
data = f.read()
46+
47+
matches = pattern.findall(data)
48+
for i, key in enumerate(matches):
49+
if verbose:
50+
print(f"Trying {i + 1}/{len(matches)} - {key}")
51+
signature = decrypt_key(key_sig, data_sig, key)
52+
if signature == UNITY3D_SIGNATURE:
53+
if verbose:
54+
print(f"Found key: {key}")
55+
return key
56+
return None
57+
58+
3559
def to_uint4_array(source: bytes, offset: int = 0):
3660
buffer = bytearray(len(source) * 2)
3761
for j in range(len(source)):
@@ -46,16 +70,25 @@ class ArchiveStorageDecryptor:
4670
substitute: bytes = bytes(0x10)
4771

4872
def __init__(self, reader: EndianBinaryReader) -> None:
49-
if DECRYPT_KEY is None:
50-
raise LookupError(
51-
"The BundleFile is encrypted, but no key was provided!\nYou can set the key via UnityPy.set_assetbundle_decrypt_key(key)"
52-
)
5373
self.unknown_1 = reader.read_u_int()
5474

5575
# read vector data/key vectors
5676
self.data, self.key = read_vector(reader)
5777
self.data_sig, self.key_sig = read_vector(reader)
5878

79+
if DECRYPT_KEY is None:
80+
raise LookupError(
81+
"\n".join(
82+
[
83+
"The BundleFile is encrypted, but no key was provided!",
84+
"You can set the key via UnityPy.set_assetbundle_decrypt_key(key).",
85+
"To try brute-forcing the key, use UnityPy.helpers.ArchiveStorageManager.brute_force_key(fp, key_sig, data_sig)",
86+
f"with key_sig = {self.key_sig}, data_sig = {self.data_sig},"
87+
"and fp being the path to global-metadata.dat or a memory dump.",
88+
]
89+
)
90+
)
91+
5992
signature = decrypt_key(self.key_sig, self.data_sig, DECRYPT_KEY)
6093
if signature != UNITY3D_SIGNATURE:
6194
raise Exception(f"Invalid signature {signature} != {UNITY3D_SIGNATURE}")

0 commit comments

Comments
 (0)