Playful Xamarin. Researching and hacking a C# mobile app

Java or Kotlin are not the only languages you can use to create apps for Android. C# programmers can develop mobile apps using the Xamarin open-source platform. Today, I will explain how to research such apps and find ways to hack them.

Recently I stumbled upon an Android app that didn’t work as I expected it to. So, I had no choice but to examine its viscera!

I launched the latest version of GDA, opened the studied APK, and immediately noticed that it looks somewhat suspicious. Classes of all activities contain approximately the same boilerplate code looking something like this:

public class MainActivity extends BaseActivity
{
private ArrayList refList;
public static final String __md_methods;
static {
MainActivity.__md_methods = "n_onCreate:\(Landroid/os/Bundle;\)V:GetOnCreate_Landroid_os_Bundle_Handler
.....
_ILandroid_os_Bundle_Handler:Android.Locations.ILocationListenerInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n";
Runtime.register("Megaprogram.Activity.MainActivity, Megaprogram", MainActivity.class, MainActivity.__md_methods);
}
public void MainActivity(){
super();
if (this.getClass() == MainActivity.class) {
Object[] objArray = new Object[0];
TypeManager.Activate("Megaprogram.Activity.MainActivity, Megaprogram", "", this, objArray);
}
return;

This makes me thinking that something is wrong with this APK. Is it some kind of framework?.. If you change the .apk extension to .zip, then the assemblies folder immediately catches your eye: first, such folders are uncommon for ordinary mobile apps; and second, it contains numerous DLL libraries.

Names of many libraries contain the word Xamarin; so, it becomes clear where such uniformity comes from: the main program code is written in C# and is located in a DLL library; only boilerplate pieces of code designed for interaction between the Mono runtime and the Android Runtime (ART) virtual machine are written in Java.

Enough theory, let’s get to work!

Selecting tools

When I deal with .Net, I always use three tools.

  1. dotPeek by JetBrains. The program makes it possible to decompile and examine .dll and .exe files. In my opinion, dotPeek offers the most handy navigation through the decompiled code, which makes it the optimal tool for algorithm analysis.

    dotPeek main window
    dotPeek main window
  2. dnSpy is used to decompile, edit, compile, and debug .Net assemblies. It must be noted that not all of its functions always work (except for decompiling): much depends on the specific situation. In my case, the compilation function didn’t work: the program was unable to link together the Mono and Android namespaces.

    dnSpy main window
    dnSpy main window
  3. Simple Assembly Explorer is a rather old, but still efficient utility making it possible to decompile .dll and .exe into C# or CIL code (Common Intermediate Language is a ‘high-level assembler’ used by the .NET virtual machine – an intermediate language developed by Microsoft for the .NET Framework platform). Most importantly, this tool can compile CIL code, which enables you to easily make changes in the studied files.

    Simple Assembly Explorer main window
    Simple Assembly Explorer main window

Instead of the standard apktool, I am going to use 7z to unpack and pack the APK. A bit later, I will explain why.

Unpacking the APK

Initially, I tried to use the well-known apktool to unpack the APK, but encountered problems with subsequent packing: too bad, apktool ‘doesn’t know’ such a file type as .dll and doesn’t consider it a standard file type for an APK. Only files whose names are present in the array below are considered standard ones:

private final static String[] APK_STANDARD_ALL_FILENAMES = new String[] {
"classes.dex", "AndroidManifest.xml", "resources.arsc", "res", "lib", "libs", "assets", "META-INF" };.

When an APK is assembled, all unknown files are compressed (compression type: DEFLATED), while Xamarin expects its DLLs to be uncompressed (STORED) and cannot read them properly. My first idea was to fix and rebuild apktool, but then I decided to take an easier way: unpack and pack files without compression using a standard archiver. After all, I don’t need to decode the manifest or get the Smali code.

Unpacking:

7z.exe x program.apk -oprogram_apk

After unpacking, in addition to typical APK files, I see the assemblies directory with a bunch of dll files inside. Out of them, the library whose name matches the name of the app is of special interest. Let’s dissect it.

Patching .Net

Analysis of the DLL and identification of the place to be modified is beyond the scope of this article (frankly speaking, this topic requires a separate book); so, I will focus only on technical aspects. As said above, dnSpy refused to compile the modified library, and I had to use Simple Assembly Explorer (SAE) for this purpose.

Let’s say I need a given function to always return true:

protected bool IsOnline()
{
ConnectivityManager connectivityManager = (ConnectivityManager)this.GetSystemService#0x0a0001d5("connectivity");
if (connectivityManager == null)
{
return false;
}
NetworkInfo activeNetworkInfo = connectivityManager.get_ActiveNetworkInfo#0x0a0002ad();
return activeNetworkInfo != null && activeNetworkInfo.get_IsConnected#0x0a0002ae();
}

Since modifications can only be made in IL code, I switch to the Details tab in the SAE window and see the following picture:

0 L_0000: ldarg.0
1 L_0001: ldstr "connectivity"
2 L_0006: callvirt Java.Lang.Object Android.Content.Context::GetSystemService(System.String)
3 L_000b: castclass Android.Net.ConnectivityManager
4 L_0010: stloc.0
5 L_0011: ldloc.0
6 L_0012: brtrue.s 9 -> ldloc.0
7 L_0014: ldc.i4.0
8 L_0015: ret
9 L_0016: ldloc.0
10 L_0017: callvirt Android.Net.NetworkInfo Android.Net.ConnectivityManager::get_ActiveNetworkInfo()
11 L_001c: stloc.1
12 L_001d: ldloc.1
13 L_001e: brfalse.s 17 -> ldc.i4.0
14 L_0020: ldloc.1
15 L_0021: callvirt System.Boolean Android.Net.NetworkInfo::get_IsConnected()
16 L_0026: ret
17 L_0027: ldc.i4.0
18 L_0028: ret

I have two options:

  1. Take a fundamental approach: learn the CIL language and write the required code myself; or 
  2. Write the required function in C# and compile it to CIL, thus, getting the required code automatically.

I chose the second option since it’s much easier and faster. Furthermore, I found a very useful site sharplab.io where you can convert code from C# to CIL.

On the left tab, I enter:

bool function() {
return true;
}

On the right tab, I see among other things:

IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ldloc.0
IL_0006: ret

I insert the obtained code into the library using Simple Assembly Explorer and save the modified DLL. Time to assemble a new APK (provided that I did everything correctly).

Rebuilding the APK

As said above, I am going to use 7z in the uncompressed mode for building. The APK built this way will be larger than the original one, but in this particular case, its size doesn’t matter:

7z.exe a -tzip -mx0 -r0 program_patched_unalign_unsigned.apk .\program_apk\*.*

FYI: -tzip is the archive format, -mx0 means no compression, and -r0 means recursive traversal of all subdirectories.

Prior to the building, I delete the META-INF directory containing the old signature. It’s not needed anymore because I have to sign the APK myself. Then I have to create a signing certificate and copy it to the keystore. If you already have a certificate, you can skip this step. I create a certificate using the keytool utility from the JDK:

"c:\Android\Android Studio\jre\bin\keytool.exe" -genkey -v -keystore keys.keystore -alias key -keyalg RSA -keysize 2048 -validity 10000

As usual, it asks the standard questions:

Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: x
What is the name of your organizational unit?
[Unknown]: x
What is the name of your organization?
[Unknown]: x
What is the name of your City or Locality?
[Unknown]: x
What is the name of your State or Province?
[Unknown]: x
What is the two-letter country code for this unit?
[Unknown]: x
Is CN=x, OU=x, O=x, L=x, ST=x, C=x correct?
[no]: yes

Generating 2 048 bit RSA key pair and self-signed certificate (SHA256withRSA) wi
th a validity of 10 000 days
for: CN=x, OU=x, O=x, L=x, ST=x, C=x
[Storing keys.keystore]

After that, I proceed to signing:

jarsigner.exe -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore keys.keystore -signedjar program_pathed_unalign.apk program_pathed_unalign_unsigned.apk key

The newly-created file, program_pathed_unalign.apk, is almost ready for installation. ‘Almost’ because it must be first aligned using the zipalign program from Android SDK Build-Tools. This procedure ensures that all uncompressed files in the archive are aligned relative to the file beginning. This, in turn, makes it possible to access files directly, without copying the data to RAM, which reduces the app’s memory usage.

Aligning:

zipalign.exe -v 4 program_pathed_unalign.apk program_pathed.apk

That’s it! Now I can safely install the program on a phone or emulator and start testing it.

Conclusions

As you can see, the analysis of Xamarin assemblies is no more difficult in comparison with native Android OS apps; you just need to take into account some APK building aspects.

www

Below are some useful links that will help you to deal with similar apps:


One Response to “Playful Xamarin. Researching and hacking a C# mobile app”

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>