50 lines
1.8 KiB
Python
50 lines
1.8 KiB
Python
"""
|
|
Distill XABA/XALZ assemblies.blob into individual .NET assemblies.
|
|
Format ref (reverse-engineered): https://github.com/dotnet/android/blob/main/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStore.cs
|
|
"""
|
|
import struct, sys, pathlib, lz4.block
|
|
|
|
if len(sys.argv) != 3:
|
|
print("Usage: unpack_xaba.py <assemblies.blob> <out_dir>")
|
|
sys.exit(1)
|
|
|
|
blob_path = pathlib.Path(sys.argv[1])
|
|
out_dir = pathlib.Path(sys.argv[2])
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
data = blob_path.read_bytes()
|
|
assert data[:4] == b"XABA", f"not a XABA blob, got {data[:4]!r}"
|
|
|
|
# Scan for XALZ markers (each = one compressed assembly)
|
|
i = 0
|
|
extracted = 0
|
|
while True:
|
|
idx = data.find(b"XALZ", i)
|
|
if idx == -1:
|
|
break
|
|
# XALZ: 4 magic + 4 descriptor_index + 4 uncompressed_size + N lz4_data
|
|
desc_idx = struct.unpack_from("<I", data, idx + 4)[0]
|
|
uncompressed = struct.unpack_from("<I", data, idx + 8)[0]
|
|
payload_start = idx + 12
|
|
# Find next XALZ to bound payload (or EOF)
|
|
next_idx = data.find(b"XALZ", payload_start)
|
|
payload_end = next_idx if next_idx != -1 else len(data)
|
|
compressed = data[payload_start:payload_end]
|
|
try:
|
|
decompressed = lz4.block.decompress(compressed, uncompressed_size=uncompressed)
|
|
except Exception as e:
|
|
# Try without explicit size as fallback
|
|
try:
|
|
decompressed = lz4.block.decompress(compressed)
|
|
except Exception as e2:
|
|
print(f"[skip] desc={desc_idx} decompress failed: {e2}")
|
|
i = payload_start
|
|
continue
|
|
out_file = out_dir / f"{desc_idx:03d}.dll"
|
|
out_file.write_bytes(decompressed)
|
|
extracted += 1
|
|
print(f"[ok] desc={desc_idx:03d} size={len(decompressed)} -> {out_file.name}")
|
|
i = payload_start
|
|
|
|
print(f"\nExtracted {extracted} assemblies into {out_dir}")
|