Protecting the code

As Android app packages are just ZIP files that contain the assemblies and metadata, the assemblies can easily be decompiled. As a result, we need a way to make it very difficult to understand the decompiled code, or even prevent it from happening altogether.

Getting ready

In this recipe, we will make use of the Babel for .NET obfuscator (https://babelfor.net/products/obfuscator). This is a commercial product, but they do have a demo version that can be used for free. The demo version does have a few limitations, and the obfuscated apps only run for a period of time, however this does not prevent us from using it.

There aren't many Mac/Linux obfuscators for .NET, and Babel for .NET is no different. We will also need a Windows machine to use the obfuscator, but we can use the MSBuild task to integrate with the build process for Xamarin.Android. As the MSBuild task does not have any specific IDE requirements, it will work with Xamarin Studio or Visual Studio.

How to do it...

Once we have installed Babel for .NET, we add the MSBuild task to our project file:

  1. Open your solution in Xamarin Studio, and from the Solution panel, right-click on the project node and select Unload:
    How to do it...

    Unloading the project file

  2. Then, right-click on the unloaded project and navigate to Tools | Edit File:
    How to do it...

    Editing the project file by hand

  3. We now need to add the required <UsingTask> element into the project file as a child of the <Project> element:
    <Project>
      <UsingTask 
        TaskName="Babel"
        AssemblyName="Babel.Build, Version=8.0.0.0, Culture=neutral, PublicKeyToken=138d17b5bd621ab7" />
    </Project>
  4. Then, we add the <Babel> task element as a child of the <Target Name="AfterBuild"> element:
    <Target Name="AfterBuild">
      <Babel
        InputFile="$(TargetPath)"
        OutputFile="$(TargetPath)"
        VerboseLevel="5"
        ObfuscateTypes="true"
        ObfuscateEvents="true"
        ObfuscateMethods="true"
        ObfuscateProperties="true"
        ObfuscateFields="true"
        VirtualFunctions="true"
        FlattenNamespaces="false"
        UnicodeNormalization="false"
        SuppressIldasm="true"
        ControlFlowObfuscation=
          "goto=on;if=on;switch=on;case=on;call=on"
        ILIterations="5"/>
    </Target>
  5. To ensure that obfuscation only happens in the Release configuration, we can use the Condition attribute:
    <Babel Condition="'$(Configuration)'=='Release'" ... />
  6. Save the changes made to the project file and reload:
    How to do it...

    Reloading the project file

  7. Now, we can switch to the release build and rebuild the solution. Xamarin Studio will create the app using the obfuscated assembly during the build process.

Some types and members shouldn't be obfuscated. This can be because we are using reflection or serialization to access them. As obfuscation might rename a type to something that we cannot use, we need tell the obfuscator to ignore or skip certain types and members. We do this by performing the following steps:

  1. We first create an XML rules file that describes how and what to do. In our case, we name the file BabelRules.xml:
    <?xml version="1.0" encoding="UTF-8"?>
    <Rules
      xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
      version="2.0">
      <Rule name="Serializable types" exclude="true">
        <Target>Fields, StaticFields</Target>
        <Pattern><![CDATA[*]]></Pattern>
        <HasAttribute onEnclosingType="true">
          System.SerializableAttribute
        </HasAttribute>
        <Description>
          Skip fields of serializable types.
        </Description>
      </Rule>
    </Rules>
  2. Then, we add the RulesFiles attribute to the babel build task, setting it to be the name of the rules file:
    <Babel RulesFiles="BabelRules.xml" ... />

How it works...

Even though we add licensing checks to ensure that unauthorized users cannot access our app, they might be able to tamper with the package to bypass the license checks. It is not possible to avoid this completely, but we can make it much more difficult to do so.

There are several ways in which we can prevent or detect tampering, and one of them is to obfuscate the assemblies. Obfuscation removes unused code and renames types and members with semantically obscure names. It can also perform control flow obfuscation, which produces spaghetti logic that can be very difficult for a cracker to analyze. The result is a smaller sized app package file that is more difficult to reverse engineer. As almost all the app logic resides in the managed .NET assemblies, this is what we must obfuscate.

Tip

As Xamarin.Android assemblies are just normal .NET assemblies, most commercial .NET obfuscators can be used.

One such obfuscator that we can use, is babel for .NET. This obfuscator is great for Xamarin.Android projects because it provides a MSBuild task, which we use to easily integrate with the build process. There aren't many good obfuscators for non-Windows machines, and babel for .NET is one of the many that only run on Windows. But, it does work with both Xamarin Studio and Visual Studio as a result of its integration with MSBuild.

In order to integrate babel for .NET into our app's build, we do have to modify our project file manually in two places. The first is to add a reference to the build task that we want to include. This requires us to add the <UsingTask TaskName="Babel"> element as a child of the <Project> element. This new element requires us to provide either the GAC-registered assembly name or the file path to the Babel.Build assembly. We provide the GAC-registered assembly name using the AssemblyName attribute, or the file path to the assembly using the AssemblyFile attribute.

The next place we modify the project file is in the <Target Name="AfterBuild"> element. This target executes directly after the assembly is compiled, but before the app is packaged. In this element, we add a <Babel> child element and set the various obfuscator properties using attributes.

The two most important properties are set using the InputFile and OutputFile attributes. For both attributes, we must use the $(TargetPath) variable. This variable points to the final output file of the compiled project. The obfuscator will load the input file and then save the obfuscated file back in the same place. We need to do this in order to preserve the existing build process.

Note

Both the InputFile and OutputFile attribute values must be the $(TargetPath) variable.

We can also specify that the obfuscation only occurs when building with the release configuration. To do this, we set the Condition attribute to be the value "'$(Configuration)'=='Release'". This makes use of the project variables and will only include the babel task when the Configuration variable's value is Release.

The remainder of the attributes are used to set which assemblies are obfuscated and how it is done. Some attributes, such as UnicodeNormalization and FlattenNamespaces, do not work well with Xamarin.Android and must be set to false. For example, if we enable UnicodeNormalization, then the types will be renamed with Unicode characters, which is not valid for the generated Java types or file names.

Note

As Unicode characters are not valid for the Java packager, the value of the UnicodeNormalization attribute must be false.

There might be instances where the obfuscation process renames a type or member that is used in reflection or serialization. At run time, the app will crash as it will not be able to find that type or member. In this case, we tell the obfuscator to ignore these types or members using the rules file.

Once we have the rules file created, we specify that we want to use it by specifying the path to the file using the RulesFiles attribute. Rule files are just XML files that follow a specific structure.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset