Is it possible to compress Xamarin assemblies into assemblies.blob after decompressing them using decompress-assemblies command line tool included in xamarin-android?
I got familiar with the structure of assemblies.blob file from https://github.com/xamarin/xamarin-android/blob/main/Documentation/project-docs/AssemblyStores.md and used the following to be able to modify a DLL inside that store file:
Decompress the file using decompress-assemblies
utility
Patch the DLL file needed
Read the descriptor index for the compressed DLL, to get that, use assembly-store-reader
, then write down the offset of the assembly that requires patching, open assemblies.blob in a hex editor, navigate to the offset of the DLL, descriptor index will be inside the uint after the compression magic uint (XALZ)
Compress the DLL file again using AssemblyCompression
class: and use the descriptor index as an input to AssemblyData
constructor
Add this method to AssemblyStoreReader
to recreate the assemblies.blob with the patched DLL:
internal void SaveStoreToStream(Stream output)
{
EnsureStoreDataAvailable();
// Load patched DLL.
MemoryStream dllStream;
using (var fs = File.Open("/location/of/patched.dll.lz4", FileMode.Open, FileAccess.Read))
{
dllStream = new MemoryStream();
fs.CopyTo(dllStream);
}
// Index of the assembly to patch in assemblies.blob file.
var patchedAssemblyIndex = 17;
uint offsetDiff = (uint)(Assemblies[patchedAssemblyIndex].DataSize - dllStream.Length);
using (var bw = new BinaryWriter(output))
{
bw.Write(ASSEMBLY_STORE_MAGIC);
bw.Write(ASSEMBLY_STORE_FORMAT_VERSION);
bw.Write(LocalEntryCount);
bw.Write(GlobalEntryCount);
bw.Write(StoreID);
foreach (AssemblyStoreAssembly assembly in Assemblies)
{
if (assembly.RuntimeIndex > patchedAssemblyIndex)
{
bw.Write(assembly.DataOffset - offsetDiff);
} else
{
bw.Write(assembly.DataOffset);
}
if (assembly.RuntimeIndex == patchedAssemblyIndex)
{
bw.Write((uint)dllStream.Length);
} else
{
bw.Write(assembly.DataSize);
}
bw.Write(assembly.DebugDataOffset);
bw.Write(assembly.DebugDataSize);
bw.Write(assembly.ConfigDataOffset);
bw.Write(assembly.ConfigDataSize);
}
foreach (AssemblyStoreHashEntry entry in GlobalIndex32)
{
bw.Write(entry.Hash);
bw.Write(entry.MappingIndex);
bw.Write(entry.LocalStoreIndex);
bw.Write(entry.StoreID);
}
foreach (AssemblyStoreHashEntry entry in GlobalIndex64)
{
bw.Write(entry.Hash);
bw.Write(entry.MappingIndex);
bw.Write(entry.LocalStoreIndex);
bw.Write(entry.StoreID);
}
foreach (AssemblyStoreAssembly assembly in Assemblies) {
if (assembly.RuntimeIndex == patchedAssemblyIndex)
{
bw.Write(dllStream.ToArray());
}
else
{
using (var stream = new MemoryStream())
{
assembly.ExtractImage(stream);
bw.Write(stream.ToArray());
}
}
}
}
}
Write the stream passed to the above method to a new file and that will be the assemblies.blob with the patched DLL
Use apktool to recreate the APK, align and sign, then it should be working with your patched DLL