daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

xamarin-apps.md (17277B)


      1 ---
      2 title: "Xamarin Apps"
      3 section: "Mobile"
      4 sectionSlug: "mobile-pentesting"
      5 sourcePath: "src/mobile-pentesting/xamarin-apps.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/mobile-pentesting/xamarin-apps.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Xamarin Apps
     14 
     15 ## **Basic Information**
     16 
     17 Xamarin is an **open-source platform** designed for developers to **build apps for iOS, Android, and Windows** using the .NET and C# frameworks. This platform offers access to numerous tools and extensions to create modern applications efficiently.
     18 
     19 ### Xamarin's Architecture
     20 
     21 - For **Android**, Xamarin integrates with Android and Java namespaces through .NET bindings, operating within the Mono execution environment alongside the Android Runtime (ART). Managed Callable Wrappers (MCW) and Android Callable Wrappers (ACW) facilitate communication between Mono and ART, both of which are built on the Linux kernel.<sup>[[1]](#references)</sup>
     22 - For **iOS**, applications run under the Mono runtime, utilizing full Ahead of Time (AOT) compilation to convert C# .NET code into ARM assembly language. This process runs alongside the Objective-C Runtime on a UNIX-like kernel.<sup>[[1]](#references)</sup>
     23 
     24 ### .NET Runtime and Mono Framework
     25 
     26 The **.NET framework** includes assemblies, classes, and namespaces for application development, with the .NET Runtime managing code execution. It offers platform independence and backward compatibility. The **Mono Framework** is an open-source version of the .NET framework, initiated in 2005 to extend .NET to Linux, now supported by Microsoft and led by Xamarin.
     27 
     28 ### Reverse Engineering Xamarin Apps
     29 
     30 #### Decompilation of Xamarin Assemblies
     31 
     32 Decompilation transforms compiled code back into source code. In Windows, the Modules window in Visual Studio can identify modules for decompilation, allowing for direct access to third-party code and extraction of source code for analysis.<sup>[[1]](#references)</sup>
     33 
     34 #### JIT vs AOT Compilation
     35 
     36 - **Android / .NET for Android (Mono runtime)**: Debug builds commonly keep JIT enabled, while modern release builds usually ship **Mono AOT** images (`libaot-*.so`) plus managed assemblies inside an assembly store. You can often still decompile the C# assemblies, but hot paths may execute from AOT data/native stubs instead of being JITted on-device.<sup>[[13]](#references)</sup>
     37 - **iOS / Mac Catalyst**: Apple forbids JIT on physical devices, so release builds use **full AOT** by default. Some projects enable the **Mono interpreter** for selected assemblies to keep reflection-heavy code working, so not every release IPA is a pure "native only" target.<sup>[[13]](#references)[[14]](#references)</sup>
     38 - **NativeAOT / CoreCLR era**: Recent MAUI projects can also be published with **NativeAOT** and newer Android builds are no longer guaranteed to be Mono-only. If the package no longer ships the usual Mono artifacts, treat it as a primarily native reversing target first and only fall back to Mono-specific tooling after fingerprinting the runtime.<sup>[[13]](#references)</sup>
     39 
     40 #### Quick runtime fingerprinting
     41 
     42 Before spending time on Mono tooling, quickly identify what runtime/layout the app actually ships:
     43 
     44 ```bash
     45 unzip -l app.apk | grep -E "assemblies(/|\.blob)|libassemblies|libaot-|libmonosgen|libmonodroid|libxamarin-app|libcoreclr"
     46 ```
     47 
     48 - `assemblies/*.dll` or `assemblies.blob` → classic Xamarin / older MAUI packaging
     49 - `libassemblies.<abi>.blob.so` → MAUI 9+ assembly store hidden inside an ELF `payload` section
     50 - `libaot-*.so` → Mono AOT compiled methods are present; managed decompilation is still useful but patching only IL may not affect execution
     51 - `libmonosgen-2.0.so` / `libmonodroid.so` → Mono runtime is present, so `frida-mono-api`, Fridax, and Mono method interception are likely viable
     52 - Mostly native binaries and no usual Mono artifacts → expect NativeAOT/CoreCLR-style layouts and start from native libraries, exported platform bridges, and Frida native hooks because Mono-only scripts may fail outright. Reuse the generic Android ELF workflow from [Reversing Native Libraries](/hacktricks/mobile-pentesting/android-app-pentesting/reversing-native-libraries).
     53 
     54 ### Extracting dll Files from APK/IPA
     55 
     56 To access the assemblies in an APK/IPA, unzip the file and explore the assemblies directory. For Android, tools like [XamAsmUnZ](https://github.com/cihansol/XamAsmUnZ) and [xamarin-decompress](https://github.com/NickstaDB/xamarin-decompress) can uncompress dll files.<sup>[[1]](#references)</sup>
     57 
     58 ```bash
     59 python3 xamarin-decompress.py -o /path/to/decompressed/apk
     60 ```
     61 
     62 In cases where after decompiling the APK it's possible to see the unknown/assemblies/ folder with the `.dll` files inside it, it's possible to use [**dnSpy**](https://github.com/dnSpy/dnSpy) directly over the `.dlls` to analyze them. However, sometimes the `assemblies.blob` and `assemblies.manifest` files are inside the unknown/assemblies/ folder. The tool [pyxamstore](https://github.com/jakev/pyxamstore) can unpack the `assemblies.blob` file in Xamarin apps, allowing access to the .NET assemblies for further analysis:<sup>[[2]](#references)[[4]](#references)</sup>
     63 
     64 ```bash
     65 pyxamstore unpack -d /path/to/decompressed/apk/assemblies/
     66 # After patching DLLs, rebuild the store
     67 pyxamstore pack
     68 ```
     69 
     70 #### .NET MAUI 9 / .NET for Android assembly stores inside ELF `.so`
     71 
     72 Recent Android MAUI 9 builds no longer expose `assemblies.blob` directly. Instead, each ABI ships an ELF container such as `lib/arm64-v8a/libassemblies.arm64-v8a.blob.so`. This is a valid shared library with a custom `payload` section that contains the managed assembly store.<sup>[[9]](#references)</sup>
     73 
     74 Quick workflow:
     75 
     76 ```bash
     77 unzip app.apk -d app_unpacked
     78 llvm-readelf --section-headers app_unpacked/lib/arm64-v8a/libassemblies.arm64-v8a.blob.so
     79 llvm-objcopy --dump-section=payload=payload.bin \
     80   app_unpacked/lib/arm64-v8a/libassemblies.arm64-v8a.blob.so
     81 hexdump -c -n 4 payload.bin   # XABA
     82 ```
     83 
     84 If `llvm-readelf` shows a `payload` section, dump it and verify the extracted blob starts with `XABA` (`0x41424158`). That payload is the assembly store documented by .NET for Android, not a single DLL.<sup>[[9]](#references)</sup>
     85 
     86 Newer `.NET for Android` packages can wrap that same store in **two different ELF layouts**, which matters when deciding whether a Mono-only extractor will work unchanged:<sup>[[10]](#references)[[11]](#references)</sup>
     87 
     88 ```bash
     89 TARGET_SO=$(find app_unpacked/lib -name 'libassembly-store.so' -o -name 'libassemblies*.blob.so' | head -n 1)
     90 llvm-readelf --section-headers "$TARGET_SO" | grep payload
     91 llvm-readelf --dyn-symbols "$TARGET_SO" | grep _assembly_store
     92 ```
     93 
     94 - **MonoVM layout**: the `payload` section is **non-loadable** (no `A` flag / address `0`), usually found in `libassemblies.<abi>.blob.so`; the runtime locates it by scanning the APK ZIP and parsing section headers manually.
     95 - **CoreCLR layout**: the `payload` section is **loadable** (`SHF_ALLOC` / `PT_LOAD`) and `_assembly_store` is exported; the runtime resolves the store with `dlopen()` + `dlsym()` instead of ZIP scanning.
     96 - For manual parsers, current docs use **format version `3`** for MonoVM stores and **`4`** for CoreCLR stores.
     97 - In both cases, `llvm-objcopy --dump-section=payload=payload.bin ...` still gives you the raw `XABA` assembly store to unpack.
     98 
     99 > When repacking or re-signing, **do not run `strip`/`llvm-strip`** on `libassemblies.*.blob.so`: the custom `payload` section is non-standard and stripping can silently remove the managed assembly store.
    100 
    101 The store layout is useful when you need to carve assemblies manually or validate an extractor:<sup>[[9]](#references)[[10]](#references)</sup>
    102 
    103 - Header: `struct.unpack('<5I', ...)` for `magic`, `version`, `entry_count`, `index_entry_count`, `index_size`
    104 - Descriptors: `entry_count` records of `struct.unpack('<7I', ...)` with `data_offset` / `data_size` and optional debug/config offsets
    105 - Index: skip `index_size` bytes
    106 - Names: `uint32 length` + UTF-8 bytes
    107 - Data: seek to each `data_offset` and write `data_size` bytes as `<name>.dll`
    108 
    109 Some extracted entries still won't open directly in dnSpy/ILSpy/dotPeek because they are additionally wrapped with **XALZ**. In that case:<sup>[[9]](#references)</sup>
    110 
    111 - Check the first 4 bytes of each extracted file for `XALZ`
    112 - Read the uncompressed size from bytes `8:12` as little-endian `uint32`
    113 - Decompress bytes `12:` with `lz4.block.decompress(...)`
    114 
    115 Minimal decompression logic:
    116 
    117 ```python
    118 import struct
    119 import lz4.block
    120 
    121 def decompress_xalz(data):
    122     size = struct.unpack('<I', data[8:12])[0]
    123     return lz4.block.decompress(data[12:], uncompressed_size=size)
    124 ```
    125 
    126 If you don't want to parse the store manually, [pymauistore](https://github.com/mwalkowski/pymauistore/tree/main) automates ELF payload extraction, `XABA` store parsing, and `XALZ` decompression for MAUI 9 APKs.<sup>[[12]](#references)</sup>
    127 
    128 If a developer-provided **debug** APK seems to be missing assemblies entirely, remember that **Fast Deployment** can keep assemblies outside the APK and synchronize them from the host/device filesystem instead of packaging them in the archive.
    129 
    130 Some older Xamarin/MAUI builds store compressed assemblies using the **XALZ** format inside `/assemblies.blob` or `/resources/assemblies`. You can quickly decompress them with the [xamarout](https://pypi.org/project/xamarout/) library:<sup>[[5]](#references)[[7]](#references)</sup>
    131 
    132 ```python
    133 from xamarout import xalz
    134 import os
    135 for root, _, files in os.walk("."):
    136     for f in files:
    137         if open(os.path.join(root, f), 'rb').read(4) == b"XALZ":
    138             xa = xalz.XamarinCompressedAssembly(os.path.join(root, f))
    139             xa.write("decompressed/" + f)
    140 ```
    141 
    142 iOS dll files are readily accessible for decompilation, revealing significant portions of the application code, which often shares a common base across different platforms.
    143 
    144 > **AOT on iOS**: managed IL is compiled into native `*.aotdata.*` files. Patching the DLL alone will not change logic; you need to hook native stubs (e.g., with Frida) because the IL bodies are empty placeholders.<sup>[[8]](#references)</sup>
    145 
    146 ### Static Analysis
    147 
    148 Once the `.dll`s are obtained it's possible to analyze the .Net code statically using tools such as [**dnSpy**](https://github.com/dnSpy/dnSpy) or [**ILSpy**](https://github.com/icsharpcode/ILSpy) that will allow modifying the code of the app. This can be super useful to tamper the application to bypass protections for example.<sup>[[1]](#references)</sup>\
    149 Note that after modifying the app you will need to pack it back again and sign it again.
    150 
    151 > dnSpy is archived; maintained forks like **dnSpyEx** keep working with .NET 8/MAUI assemblies and preserve debug symbols when re-saving.
    152 
    153 ### Dynamic Analysis
    154 
    155 Dynamic analysis involves checking for SSL pinning and using tools like [Fridax](https://github.com/NorthwaveSecurity/fridax) for runtime modifications of the .NET binary in Xamarin apps. Frida scripts are available to bypass root detection or SSL pinning, enhancing analysis capabilities.<sup>[[1]](#references)</sup>
    156 
    157 Other interesting Frida scripts:
    158 
    159 - [**xamarin-antiroot**](https://codeshare.frida.re/@Gand3lf/xamarin-antiroot/)
    160 - [**xamarin-root-detect-bypass**](https://codeshare.frida.re/@nuschpl/xamarin-root-detect-bypass/)
    161 - [**Frida-xamarin-unpin**](https://github.com/GoSecure/frida-xamarin-unpin)<sup>[[6]](#references)</sup>
    162 
    163 Updated **Frida-xamarin-unpin** (Mono >=6) hooks `System.Net.Http.HttpClient.SendAsync` and swaps the handler to a permissive one, so it still works even when pinning is implemented in custom handlers. Run it after the app starts:<sup>[[6]](#references)</sup>
    164 
    165 ```bash
    166 frida -U -l dist/xamarin-unpin.js com.target.app --no-pause
    167 ```
    168 
    169 Operational caveats:
    170 
    171 - **Early spawn (`frida -f`) is unreliable** for Mono hooks if the runtime modules are not loaded yet; attach after launch or delay your hook until Mono is initialized.<sup>[[6]](#references)</sup>
    172 - **Full-AOT iOS / NativeAOT targets** are outside the sweet spot of `frida-mono-api` and `frida-xamarin-unpin`; in those cases switch to native hooks/stubs because there may be no convenient JITted Mono method body to intercept.
    173 - If the app enables the **Mono interpreter** for only a subset of assemblies, those interpreted assemblies remain much friendlier hook points than the AOT-compiled ones.
    174 
    175 Quick template to hook managed methods with the bundled `frida-mono-api`:
    176 
    177 ```javascript
    178 const mono = require('frida-mono-api');
    179 Mono.ensureInitialized();
    180 Mono.enumerateLoadedImages().forEach(i => console.log(i.name));
    181 const klass = Mono.classFromName("Namespace", "Class");
    182 const m = Mono.methodFromName(klass, "Method", 2);
    183 Mono.intercept(m, { onEnter(args){ console.log(args[1].toInt32()); } });
    184 ```
    185 
    186 #### High-value storage targets (`Xamarin.Essentials` / `Microsoft.Maui.Storage`)
    187 
    188 Once you can enumerate managed classes, storage wrappers are usually the fastest way to recover tokens, environment selectors, feature flags, and migration leftovers without fighting KeyStore/Keychain manually first.
    189 
    190 Quick triage after extracting assemblies:
    191 
    192 ```bash
    193 find extracted_dlls -name '*.dll' -print0 | \
    194   xargs -0 strings -a | grep -E \
    195   'LegacySecureStorage|Xamarin\.Essentials|Microsoft\.Maui\.Storage|microsoft\.maui\.essentials\.preferences|xamarinessentials'
    196 ```
    197 
    198 Useful hook/grep targets:<sup>[[15]](#references)</sup>
    199 
    200 - `Microsoft.Maui.Storage.SecureStorage.GetAsync/SetAsync/RemoveAll`
    201 - `Xamarin.Essentials.SecureStorage.GetAsync/SetAsync`
    202 - `Microsoft.Maui.Storage.Preferences.Get/Set/ContainsKey/Clear`
    203 
    204 Why these are valuable:
    205 
    206 - MAUI `SecureStorage` wraps encrypted key/value storage; on Android the backing store keeps the filename `[package].microsoft.maui.essentials.preferences`, and on iOS the Keychain service name is `[bundle-id].microsoft.maui.essentials.preferences`.<sup>[[15]](#references)</sup>
    207 - `Preferences` commonly stores non-secret but security-relevant values such as API base URLs, first-launch flags, environment selectors, and feature flags.
    208 - During Xamarin.Forms → MAUI migrations, Microsoft documents a `LegacySecureStorage` helper that reads old Xamarin.Essentials data. If you see that class/string in the assemblies, test whether old and new storage namespaces coexist and whether reinstall or upgrade paths leave reusable tokens behind.<sup>[[16]](#references)</sup>
    209 - On iOS, uninstalling the app does **not** automatically remove Keychain entries, so reinstall tests may inherit old `SecureStorage` data.<sup>[[15]](#references)</sup>
    210 
    211 ### Resigning
    212 
    213 The tool [Uber APK Signer](https://github.com/patrickfav/uber-apk-signer) simplifies signing multiple APKs with the same key, and can be used to resign an app after changes have been performed to it.<sup>[[3]](#references)</sup>
    214 
    215 ## References
    216 
    217 - [1] [Xamarin Reverse Engineering: A Guide for Penetration Testers](https://www.appknox.com/security/xamarin-reverse-engineering-a-guide-for-penetration-testers)
    218 - [2] [Unpacking Xamarin AssemblyStore Blobs](https://thecobraden.com/posts/unpacking_xamarin_assembly_stores/)
    219 - [3] [Introduction to the Exploitation of Xamarin Apps](https://medium.com/@justmobilesec/introduction-to-the-exploitation-of-xamarin-apps-fde4619a51bf)
    220 - [4] [pyxamstore - Python utility for parsing Xamarin AssemblyStore blob files](https://github.com/jakev/pyxamstore)
    221 - [5] [xamarout - PyPI](https://pypi.org/project/xamarout/)
    222 - [6] [GoSecure/frida-xamarin-unpin](https://github.com/GoSecure/frida-xamarin-unpin)
    223 - [7] [Xalz Unpacker for Xamarin.Android DLLs](https://gist.github.com/Diefunction/e26fce039efcab57aac342a4b2d48ff6)
    224 - [8] [Deobfuscating iOS dll file (I think arm64) - Reverse Engineering Stack Exchange](https://reverseengineering.stackexchange.com/questions/31716/deobfuscating-ios-dll-file-i-think-arm64)
    225 - [9] [Decompiling an Android Application Written in .NET MAUI 9 (Xamarin)](https://mwalkowski.com/post/decompiling-an-android-application-written-in-net-maui-9-xamarin/)
    226 - [10] [Assembly Stores - dotnet/android documentation](https://github.com/dotnet/android/blob/main/Documentation/project-docs/AssemblyStores.md)
    227 - [11] [Shared libraries in .NET for Android applications - dotnet/android documentation](https://github.com/dotnet/android/blob/main/Documentation/project-docs/ApkSharedLibraries.md)
    228 - [12] [pymauistore - Python utility for parsing MAUI AssemblyStore blob files](https://github.com/mwalkowski/pymauistore/tree/main)
    229 - [13] [Runtimes and compilation in .NET MAUI - Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/maui/deployment/runtimes-compilation?view=net-maui-10.0)
    230 - [14] [Mono interpreter on iOS and Mac Catalyst - .NET MAUI - Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/maui/macios/interpreter?view=net-maui-10.0)
    231 - [15] [Secure storage - .NET MAUI - Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/storage/secure-storage?view=net-maui-10.0)
    232 - [16] [Migrate from Xamarin.Essentials SecureStorage to .NET MAUI SecureStorage - Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/maui/migration/secure-storage?view=net-maui-10.0)