Xamarin Root and SSL Pinning Bypass
Introduction
Many Android applications implement root detection and SSL pinning mechanisms to prevent tampering, protect sensitive functionality, and secure network communications. While effective against casual users, these controls can hinder legitimate security research by restricting analysis on rooted test devices and blocking traffic inspection.
In this post, I describe how both root detection and SSL pinning were bypassed in a Xamarin-based Android application using static reverse engineering techniques. The approach focuses on de-compiling the application, extracting and modifying Xamarin assemblies, and rebuilding the app without relying on runtime hooking or dynamic instrumentation frameworks.
This work was conducted strictly for educational and authorized security testing purposes, with the goal of understanding and evaluating mobile application security controls.
Tools Used
The following tools were used throughout the analysis and bypass process:
- ADB (installed within Android SDK platform tools)
- Jadx-Gui
- APKTool
- pyxamstore
- dnSpy
- zipalign (installed within Android SDK platform tools)
- apksigner (installed within Android SDK platform tools)
Target Application Overview
The target application is a Xamarin-based Android app that implements managed root detection and SSL pinning logic within its .NET assemblies.
The primary challenge of this assessment was bypassing these security mechanisms, as the checks are enforced at the managed code level rather than through native libraries, making traditional smali-only patching ineffective
High-Level Attack Methodology
The overall approach followed in this research can be summarized in the following steps:
- Extracting the APK from a rooted device
- Reverse engineering application package
- Extracting Xamarin (.NET) assemblies
- Patching the managed root detection logic and SSL pinning Methods
- Rebuilding, aligning, and re-signing the application
Step-by-Step Technical Walk-through
Step 1: Extracting the APK from a rooted device
The first step is extracting the target APK from the rooted device to begin static analysis.
To do this, we first need to identify the application package name. In my case, this was done using the following command:
adb shell pm list packagesTo save time and filter the results, I used grep to search for the target package name:
adb shell pm list packages | grep -i exampleNote: On Windows systems, Select-String can be used instead of grep.
The output revealed the package name: like `com.example.com`
Next, I retrieved the APK path associated with this package using:
adb shell pm path com.example.comThis command returned the full path to the APK file onto the device.
Finally, I pulled the APK from the device to my local machine using:
adb pull <APK_PATH_FROM_PREVIOUS_COMMAND>At this point, the APK was successfully extracted and ready for further reverse engineering and analysis.
Step 2: Reverse Engineering the APK and extracting DLL files
At this stage, the extracted APK is reverse engineered to access its internal structure and resources.
First, APKTool was used to de-compile the application:
apktool d com.example.com.apkOnce the de-compilation process is completed, I navigated to the following directory within the extracted application folder:
app_folder/unknown/assemblies
This directory contains the Xamarin assemblies bundled with the application.
To avoid modifying the original extracted files, the assemblies folder was copied to a new working directory.
Next, the pyxamstore tool was used to unpack the Xamarin assemblies into readable .dll files:
pyxamstore unpack -d assemblies_folder_pathAfter running this command, two important artifacts were generated
- A new directory named out, containing the extracted .dll files
- An assemblies.json file, which will later be required when repacking the patched assemblies
At this point, the managed assemblies were successfully extracted and ready for static analysis and patching, which will be covered in the next step.
Beginners often try to patch the classes.dex file (Smali) first. but in Xamarin, the Java/Smali code is just an Android Callable Wrapper (ACW). It acts as a bridge. The “brain” of the app — where the security logic lives — is inside the .dll files within the assemblies.blob. Patching Smali is like trying to fix a car’s engine by painting the steering wheel.
Some high-security Xamarin apps enable a feature called “Bundle into Native Code.” * The Difference: Instead of finding .dll files in /assemblies, you might find a massive libmonodroid_bundle_app.so file in the lib/ folder.
Pro-Tip: If you encounter this, pyxamstore won’t work directly. You would first need to extract the DLLs from the .so file (which is essentially a compressed archive) before you can use dnSpy. Mentioning this edge case adds massive credibility to your report.
Step 3: Static Analysis and Patching the Root Detection Logic
With the Xamarin assemblies extracted, the next step was to analyze and patch the root detection logic.
Using dnSpy, I imported the suspected .dll files that potentially contained the root and debugger detection mechanisms. The analysis started by locating the MainActivity and following the application execution flow to identify where security checks were triggered.
During the analysis, multiple root-related methods were identified, including:
- isDeviceRooted
As an alternative approach, dnSpy’s search functionality was also used to quickly locate relevant detection logic by searching for common keywords such as:
rooted, hooked, frida, superapk, magisk, integrity.
Get Muhammedmedhat’s stories in your inbox
Join Medium for free to get updates from this writer.
This helped uncover additional root detection mechanisms scattered across the assemblies.
Once the relevant methods were identified, dnSpy’s Edit Class feature was used to modify the logic. The return values of the root and debugger detection methods were patched to always return false.
After saving the modified assemblies, the root detection logic was successfully neutralized, completing the patching phase.
Step 4: Patching the SSL Pinning Logic
After successfully bypassing the root detection logic, the next step was to disable the SSL pinning mechanism implemented at the managed code level.
Using the same static analysis approach in dnSpy, I searched through the extracted Xamarin assemblies for certificate validation–related methods. This was done by looking for common SSL and networking keywords such as:
HttpClientHandler, ServerCertificate, ValidationCallback, SslPolicyErrors
During this process, I identified the following method responsible for enforcing SSL certificate validation:
ServerCertificateCustomValidationCallback
This method is part of the HttpClientHandler implementation and is used to validate the server’s TLS certificate during HTTPS connections. The application relied on this callback to enforce SSL pinning by rejecting untrusted or intercepted certificates.
To bypass SSL pinning, the method was patched using dnSpy’s Edit Class feature. The validation logic was modified so that the callback always returns true, effectively instructing the application to trust all certificates, regardless of their validity or trust chain.
By forcing the certificate validation callback to always return true, SSL pinning was completely disabled. This allowed HTTPS traffic to be intercepted and analyzed without triggering certificate validation errors.
At this point, both root detection and SSL pinning mechanisms were successfully bypassed using purely static patching techniques, without relying on runtime hooking frameworks such as Frida or Xposed.
Step 5: Repacking the Patched Assemblies Using pyxamstore
After patching the required .dll files, the next step was to repack the modified assemblies back into Xamarin’s original format.
Using pyxamstore, and from within the directory containing the out folder and the assemblies.json file, the following command was executed:
pyxamstore packImportant: This command must be executed from the same directory that contains both the out folder and the assemblies.json file.
Upon successful execution, two new files were generated:
- assemblies.blob.new
- assemblies.manifest.new
These newly generated files were then used to replace the original files located in:
app_name/unknown/assembliesWith this step completed, the patched Xamarin assemblies were successfully repacked and integrated back into the application structure.
Step 6: Rebuilding, Aligning, and Signing the APK
With the patched assemblies in place, the final step was to rebuild the APK and prepare it for installation.
- Rebuilding the APK
The application was rebuilt using APKTool:
apktool b app_folderAfter a successful build, the rebuilt APK is located at:
app_folder/dist
- Aligning the APK
To optimize the rebuilt APK for proper memory alignment, the zipalign tool was used:
zipalign -v 4 the_extracted_apk out.apkThis produces a new aligned APK named out.apk
- Signing the APK
Before installation on a device, the APK must be signed using apksigner. A keystore is required for signing. If a keystore is not available, it can be generated using:
keytool -genkeypair -v -keystore keystore-name.jks -alias alias -keyalg RSA -keysize 2048 -validity 10000Once the key store is ready, the APK can be signed:
apksigner sign - ks-key-alias alias -ks ~/keystore-name.jks out.apkNote: apktool, zipalign, apksigner, and keytool are available via Android Studio or the Android SDK Platform Tools from the official Android website.
After completing these steps, the APK is fully rebuilt, patched, aligned, and signed, making it ready for installation on a device using
adb install out.apkProfessional apps often have a Secondary Integrity Check. Even if you bypass Root and SSL, the app might check its own signature at runtime.
If the app crashes immediately after your “Step 6,” it’s likely because the app detected it was re-signed with your debug key instead of the original developer’s key.
The Fix: You would need to find the logic that calls
PackageManager.getPackageInfo(..., GET_SIGNATURES)and patch that to return the original hash.
Step 7: IP-tables Traffic Routing to Burp Suite
In this final step, we redirect the device’s network traffic to Burp Suite by configuring iptables rules on the Android device. This allows all HTTP and HTTPS requests to be transparently intercepted without modifying proxy settings at the application level.
1. Flush Existing NAT Rules
Start by clearing up any existing NAT rules to avoid conflicts:
adb shell "iptables -t nat -F"2. Redirect Outbound Traffic to Burp Suite
Add DNAT rules to redirect all outbound HTTP and HTTPS traffic to Burp Suite:
adb shell "iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to-destination 172.16.35.138:8080"adb shell "iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination 172.16.35.138:8080"These rules ensure that any application traffic destined for ports 80 or 443 is transparently forwarded to Burp, regardless of the app’s internal networking logic.
3. Enable Masquerading (SNAT)
To ensure proper return routing and prevent connection issues, enable masquerading for both HTTP and HTTPS traffic:
adb shell "iptables -t nat -A POSTROUTING -p tcp --dport 80 -j MASQUERADE"adb shell "iptables -t nat -A POSTROUTING -p tcp --dport 443 -j MASQUERADE"Masquerading rewrites the source address, allowing responses from Burp Suite to be correctly routed back to the originating application.
In conclusion, this research demonstrates that security mechanisms such as root detection and SSL pinning in Xamarin-based applications can be effectively bypassed through static analysis when implemented solely at the managed code level. This highlights the importance of adopting defense-in-depth strategies, including native-level protections and server-side controls, to strengthen mobile application security against reverse engineering.








