Obtaining .NET Assemblies from Android Full AOT Compiled Applications
Recently on a mobile engagement, I came across an Android application built with Xamarin using full Ahead of Time (AOT) compilation. This technology allows a developer to create an application in C#, and the Xamarin platform compiles that code into platform-specific binaries without the use of the Mono Just-In-Time (JIT) compiler according to the documentation. While AOT is great for performance and application size, it poses a problem when one attempts to dynamically instrument and manipulate the application for security testing. Normally you can interact with and manipulate the Mono assemblies with Frida and frida-mono-api, but in this instance, the assembly, class, and method names were unknown and there wasn’t a straightforward way to obtain them. However, I did discover a method to obtain this information which I outline below.
Extraction Process
After extracting the contents of the APK (apktool d example.apk), I expected to see a number of “libaot-*.so” files under lib/$arch based on the documentation I had read. However, I only found the libraries listed below.
Not knowing where to start, I grepped these libraries for my application name which matched libmonodroid_bundle_app.so. Performing static analysis on libmonodroid_bundle_app.so in Ghidra indicated that there were assembly references exported by the library.
These exports appeared to be used by the mono_mkbundle_init function.
Following the referenced code, I found that mono_mkbundle_init eventually called the my_inflate function.
The source code for the my_inflate function in the Mono source code indicated that decompressed and registered Mono assemblies. This spawned the idea of searching the binary for compressed data. Using the binwalk tool, I discovered that libmonodroid_bundle_app.so contained a number of compressed sections and I extracted them using binwalk’s -e flag.
Low and behold, the compressed regions were the .NET assemblies.
Opening the extracted assemblies revealed the full assemblies referred to in the shared library exports. With the assemblies in hand, it was back to testing with the well-known methods of runtime manipulation via Frida and static analysis of the assemblies.
While there’s likely a more elegant way of extracting the assemblies, this quick and dirty method proved useful for gaining access to the assemblies without excessive reverse engineering effort or custom tool development.