Remotesoft .NET Obfuscator User's Manual

Copyright 2011 Remotesoft Inc. All rights reserved.

Contents

  1. Overview
  2. Getting Started
  3. Installing Remotesoft Obfuscator
  4. Running Remotesoft Obfuscator
  5. Configuring Remotesoft Obfuscator
  6. Extending Remotesoft Obfuscator
  7. Handling of other files
  8. Working with the Graphical User Interfaces - Remotesoft .NET Explorer
  9. Creating patch files
  10. Working with .NET Compact Framework
  11. Advanced topics


1. Overview

Microsoft .NET format (MSIL code) contains almost all of the information, apart from comments, that is in original source (C#, Visual Basic.NET, Visual C++.NET, etc) files.

One must be aware that by using a decompiler, such as our Salamander .NET decompiler, a hostile competitor can easily reverse engineer your .NET binaries and steal your intellectual properties. This is obviously a major problem that need to be addressed.

To counter this threat, it is better to obfuscate your .NET executable files (so called assemblies) before distributing your product. The obfuscation process strips all unnecessary information from .NET assemblies. This includes local variable names, method parameter names, debug information and some metadata. Additionally, class, interface, field and method identifiers are renamed to render them meaningless.

The .NET runtime that runs your MSIL code, does not care at all about these changes. However, the decompiled version of these classes is extremely difficult to understand and therefore, frustrates and complicates any attempts to reverse engineer your code.

The changes that an obfuscator makes to your assemblies are not reversible - there is no automated way for a reverse engineer to recover the lost information about your code.

An additional benefit to obfuscation is a substantial reduction in the size of your code, due to the removal of unnecessary information and the replacement of large, human-readable identifiers with small machine generated names. This size reduction is beneficial as it leads to a faster startup for your programs.

Salamander .NET obfuscator is a tool to protect your intellectual properties. It transforms your .NET assemblies (.EXE or .DLL) in such a way that decompilation of the new format would result in source codes that are extremely difficult to understand, and virtually impossible to be recompiled.

Salamander .NET obfuscator is extremely easy to use through a command-line utility that simply takes .exe, .dll or XML config files as input and transform them into new format. Extensive testing of hundreds of assemblies have proven that our .NET obfuscator always produces equivalent and obfuscated code.

Uniqueness

Features

Screen Shot

The following images show the difference between the original (left panel) and the obfuscated assembly (right panel). Notice most of the identifiers are renamed to a single letter 'A'.

2. Getting Started

You can open one or more .NET assemblies, browse it, configure it, and then perform the obfuscation. This section shows you a tutorial for some of the common tasks.

Launch Remotesoft .NET Explorer

To launch the Explorer, goto Start -> Programs -> Remotesoft -> Remotesoft .NET Explorer

Open Files

From the File menu, choose Open, this starts a file dialog where you can select your file. You can also click the File Open icon in the toolbar to bring up the file dialog, or you can select a file from the most recent files in the File menu.

To open an obfuscator XML config file, from the File menu, choose Open XML Config, which will bring up a file dialog for you to choose an XML file.

Let's open our sample files, installed in c:\program Files\Remotesoft\Obfusactor\samples\MyApp. Please open the following 4 files one by one,

Browsing and Configuring

If the file is a valid .NET assembly, the left panel, will display a tree view that shows the class hierarchy of the assembly. If you do not see the explorer bar, go to the View menu, and select Explorer Bar. The explorer bar has three tabs: class view, metadata view, and PE Format, click the class view to browse the namespaces, classes and methods. Click the metadata tab to view the low level meta data of the assembly. The PE Format tab lists those data entries in an executable file, such as imported functions. If you open a regular Windows .exe or .dll file, which is not a .NET image, then only the PE Format tab works.

The following shows the explorer after the 4 files are opened. We are now going to do a simple config for the obfuscator, since MyModule.dll and MyPrivateLib.dll are only used internally, and thus we can mark them as private. Select those two nodes, and right click "Mark As Private".

Disassemble

Expand the class tree, and select a class, or any of its members, then do one of the following to disassemble the class,

  1. From the Actions menu, select Disassemble
  2. Right click to bring up the context menu, and select Disassemble
  3. Simply type F5 key

Obfuscate

Simply press the "Obfuscate" button to perform the obfuscation, you can read from the message on the rigt panel to find where the obfuscated files are stored, see below.

Name Lookup

Name look up is vey simple. You simply click a node of your interest, and the little arrow will tell you the obfuscated name of a symbol, or the original name of an obfuscated symol. For example, type UseSimpleLib in MyApp.exe becomes a.A after obfuscation, a.A::A() corresponds to original method,  UseSimpleLib::accessTest().

3. Installing Remotesoft .NET Obfuscator

The package contains two components that need to install, Remotesoft .NET Obfuscator and Remotesoft .NET Explorer.

Remotesoft .NET obfuscator is a console application that currently runs only on Windows platform, including Windows NT, Windows 2K and Windows XP. It requires Microsoft .NET Framework SDK. We recommend that you obtain the latest copy of the SDK from Microsoft.

The obfuscator is extremely easy to install. Double click the downloaded .msi file, input your serial number and the installation will proceed automatically.

Remotesoft .NET Explorer is a Windows Forms application, and it is also extremely easy to install. Double click the downloaded .msi file, and the installation will proceed automatically. 

4. Running Remotesoft .NET Obfuscator

Remotesoft .NET obfuscator is designed to be integrated into your build process, so that obfuscation can become a consistently applied and automatic part of your regular build and QA cycle. For this reason, it is a command line utility with simple options. For complicated situations, you can write a XML config file to control the obfuscation process.

A simple way to view obfuscation is as a phase in your build process where the interface to your .NET assemblies is specified and only that interface is left accessible to the outside world. This interface is the list of classes, interfaces, methods,  fields, properties and events that you provide through custom attribute programming, or in an XML config file. If you don't customize it, the obfuscator uses default settings.

Command line

The command for running the obfuscator is as follows,
obfuscator [-verbose] [-exe] [-dll] [-d outdir] [-out outfile] [-log] [-keepmd]
[-sign keyfile] [-delsig] [-delaysign] [-ildasm] [-enum] [-samesize] [-nosal]
[-keepparam] [-removeparam] [-ref assembly] [-keepdebug] [-nc cskeywords | vbkeywords | vckeywords | javakeywords | nondisplayable | nameconvention_xml_file]
[-dis] [-private file]* inputfile (inputfile)*
where:

inputfile is the filename for your original, unobfuscated .NET assembly (.exe or .dll), or the file name of your XML config file (.xml).

Options:

Verbose output.

Obfuscate the input assemblies as self-contained applications, and use the strongest obfuscation level. This is the default behavior for *.exe files.

Obfuscate the input assemblies as libraries, and do not obfuscate public members. This is the default behavior for *.dll files.

Specify which directory to put the generated codes.

Specify an output file, this is ignored if multiple input files are being obfuscated.

Generate a log file, .log, into the same output directory. This is used for the text log of this obfuscation run, the file is named as <inputExeOrDll>.log.

By default, some metadata, mainly properties and events, are removed. Use this option to keep those metadata in case the obfuscated code behaves differently. In some situations, e.g. XML serialization, this option has to be used to ensure the correct runtime behavior.

Sign input assemblies with the specified key file.

When your assembly has been already strong name signed, the obfuscated assembly will be re-signed automatically with the original keyfile. Use this option to specify a different key file. For more info, click here.

When your assembly has been already strong name signed, it won't work after obfuscation, choose this option to remove the signature, so you can test out the obfuscated assembly. Our standalone desktop version re-signs the obfuscated code automatically.

CAUTION: When this option is choosed, the resulting assembly will not be able to re-signed afterwards, therefore, this option should be mainly used for testing purpose in the case you are not the author of the assembly and you do not have access to the key file. For more info, click here.

When your assembly has been already strong name signed, it won't work after obfuscation, choose this option so you can re-sign the obfuscated code afterwards. After you download the obfuscated assembly, you can do one of the following,

  1. invoke sn -Vr <assembly> to continue to work with the obfuscated assembly.
  2. invoke sn -R <assembly> <keyfile> to re-sign the obfuscated assembly when the key file is finally available.

Prevent ILDASM-like tools from disassembling your assemblies after obfuscation. This option is only good for .exe files.

Obfuscate enum members. In default, enum members are left unchanged.

Obfuscation usually results in smaller file size, which may not work sometimes, especially for mixed images. Use this option to tell the obfuscator not to shrink the file size.

By default, the obfuscator emit code to prevent our salamander decompiler, use this option if you do not want to prevent the decompiler from decompiling the obfuscated code.

By default, all parameter names of methods are removed from EXE, and parameter names of all obfuscated methods are removed from DLL. use this option if you want to keep parameter names.

Remove all method parameter names. By default, parameter names of non-obfuscated methods are preserved.

Specify an external assembly for the obfuscator to resolve references. This option can be used multiple times to specify more than one references.

By default, debug info is stripped, use this option to keep debug info.

Specify a name convention to use for obfuscated symbols. If no -nc options is specified, the defaut will be used such that symbols are renamed to A, a, B, b, ..., but you can define your own scheme for renaming. There are five options:

  1. cskeywords     -  use csharp keywords for obfuscated symbol names.
  2. cvbkeywords    - useVB.NET keywords for obfuscated symbol names.
  3. cvckeywords    - use VC++.NET keywords for obfuscated symbol names.
  4. cjavakeywords  - use Java keywords for obfuscated symbol names.
  5. nondisplayable -   rename symbols so that they can't be displayed.
  6. nameconvention_xml_file - specify an xml file to rename symbols. This file uses <nameconvention> element to specify names. For more info, please read the description of <nameconvention> element in the XML config file section. The following is a simple XML file that can be used for name convention.
        <?xml version='1.0'?>
        <!DOCTYPE obfuscator SYSTEM "obfuscator.dtd">
        <obfuscator>  
        <nameconvention>
            <item>this</item>
            <item>is</item>
            <item>a</item>
            <item>sample</item>
            <item>name</item>	
            <item>convention</item>
        </nameconvention>  
        </obfuscator>
        

Disassemble the file, generate ilasm output.

Obfuscate the depedent assembly aggressively as an internal component. This option can be used multiple times to specify more than one dependency.

Graphical User Interfaces (GUI)

If you don't like the command line, you can choose the graphical user interfaces. Double click the dotexplorer.exe program, or start it through the start menu start\programs\Remotesoft Obfuscator\Remotesoft .NET Explorer. Please read the following section: Working with the Graphical User Interfaces.

Simple examples

Most of the time, the obfuscation will be accomplished by a simple invocation of the obfuscator with the default settings. The following are some examples,

SimpleTest.exe will be obfuscated using the strongest level (.exe) with almost all symbols renamed. The obfuscated image is saved to obfuscated\SimpleTest.exe, probably with a smaller file size.

SimpleTest.exe will be obfuscated as libraries (.dll) with public symbols unchanged. The obfuscated image is saved to obfuscated\SimpleTest.exe, probably with a smaller file size.

SimpleTest.exe will be obfuscated using the strongest level (.exe) with almost all symbols renamed. The obfuscated image is saved to obfuscated\SimpleTest.exe, probably with a smaller file size. The resulting file will crash the ildasm utility, and thus won't be disassembled by ildasm.

will perform a whole obfuscation, two files are specified as private and thus their public symbols will be fully obfuscated. These 4 files might depend on each other in a very complex fashion, but you do not care, the obfuscator will analyze the dependencies, and obfuscate cross references accordingly.

Take the XML config file as input, and perform obfuscation accordingly.

Log file contents

Due to inheritance constraints, some identifiers, mostly virtual methods, cannot be modified. For example, the public String ToString() method, an override of the System.Object class, cannot be renamed.

The log file records all of the symbols in an assembly, including the original names and the obfuscated names. You can look up the log file to find the original name from an obfuscated symbol name. We will provide a utility to help you.

Run-time problems

In theory, obfuscation can not guarantee code equivalency. The reflection APIs, namely, certain methods of the System.Type and the System.Reflection namespace, refer to classes, methods, fields, properties and events using a string name. If your assemblies use these reflection methods to refer to identifiers, your code may behave incorrectly after obfuscation because the string name may have been changed. Obfuscator adds a warning to the log file if the above reflection methods are used in the obfuscated code. If the reflection methods act only on classes, methods, fields, properties and events that are defined in external assemblies, there will be no problem. If, however, the reflection methods act on identifiers within the assembly, these identifiers must be specified in the XML config file so that obfuscation does not change them.

Object Serialization

When a type is declared as Serializable, its fields can not have duplicate names after obfuscation, otherwise, serialization will not work. Our obfuscator will automatically rename its fields so each of them will have a different obfuscated name. If it still does not work, you can use the keepfields attribute in the class element to specify that all of its fields should be excluded from obfuscation.

XML Serialization

XML serialization implicitly uses reflections, and thus the obfuscation needs to be configured. When a type is declared as Serializable, the type name needs to be exluded, and its fields can not have duplicate names after obfuscation, otherwise, serialization will not work, you can use the keepfields attribute in the class element to specify that all of its fields should be excluded from obfuscation. Please read more in the Serialization section of the Advanced Topics.

5. Configuring Remotesoft .NET Obfuscator

Although most of the time, a simple invocation of the obfuscator with the default settings will accomplish your obfuscation needs. However, there are situations that you may need to control the obfuscation process, for instance:

The obfuscator is designed to be highly configurable so you have full control of the obfuscation process. You can configure the obfuscator in two ways,

With custom attribute insertion, you insert certain attributes into your source code, when the obfuscator later recognizes these attributes, the obfuscation will proceed according to your specification, and all inserted code will be completely removed after obfuscation.

With XML config file, you specify your settings in an XML file, and use it as input of the obfuscator.

Custom Attribute Insertion

The attribute class, Remotesoft.ObfuscatorAttribute, is used to control the obfuscation behavior. You can apply this special attribute to an assembly, a type, a field, a method, a property, or an event, to define how the obfuscation should proceed.

The Remotesoft.ObfuscatorAttribute

namespace Remotesoft
{
/// This attribute is used to decorate a member in an assembly that is going to
/// be obfuscated. It controls how the obfuscation should be performed.
[AttributeUsage(AttributeTargets.All)]
public class ObfuscatorAttribute : Attribute
{
	public string MapTo;
	public bool Obfuscate;

	private bool rename;

	public ObfuscatorAttribute()
	{
		this.rename = true;
	}

	public ObfuscatorAttribute(bool rename)
	{
		this.rename = rename;
	}
}
}

Programming with this attribute is simple, you insert it into your source code where you want to configure the obfuscation. The following shows a simple example, the insertion of the attribute is shown in red. In the example, method bar(int data) will not be renamed, and field pData will be renamed to "ddd" after obfuscation.

class Foo
{ 
	[Remotesoft.ObfuscatorAttribute(false)]
	void bar(int data)
	{
		... 
	}
  
	int test(int data)
	{
		... 
	}	
    
	[Remotesoft.ObfuscatorAttribute(MapTo="ddd")]  
	int pData; 
}

To compile the source files, you need to use a reference to our library: Remotesoft.Obfuscator.dll, e.g.,
csc /r:Remotesoft.Obfuscator.dll ReflectionTest.cs

A Simple example

The following example uses reflection methods to retrieve a field, int which. Since the default obfuscation will rename all possible identifier names including the which field, the obfuscated code will behave differently from the original assembly. In order to prevent such error, you can tell the obfuscator not to rename the specific field using the attribute, as shown below in red. When this attrbiute is present, the obfuscated assembly behaves exactly as the original file.

// csc /r:Remotesoft.Obfuscator.dll ReflectionTest.cs

using System;
using System.Reflection;

class ReflectionTest
{
	[Remotesoft.ObfuscatorAttribute(false)]
	int which;

	[Remotesoft.ObfuscatorAttribute(MapTo="trash")]
	string name;
	
	ReflectionTest(int which)
	{
		this.which = which;
		name = "Christina Zhang";
	}

	public static void Main()
	{
		int data = 1234;

		ReflectionTest rt = new ReflectionTest(data);

		try {
			// use reflection to get the field value
			FieldInfo fi = typeof(ReflectionTest).GetField("which", BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);
			Console.WriteLine("You should see {0}, here: {1}", data, fi.GetValue(rt));
		} catch (Exception e) {
			Console.WriteLine(e.Message);
		}
		// display field directly
		Console.WriteLine("name = {0}", rt.name);
	}
}

The following image shows the obfuscated assembly, notice the which field is unchanged, and the name field is renamed to trash as specified by the ObfuscatorAttribute.

Attribute programming provides you a very simple way to configure the obfuscation during your programming circle. All of the inserted code will be completely removed from your final assembly after obfuscation.

XML Config File

Custom attribute insertion is simple, but it requires modification of your source code. If you do not want obfuscation to get involved into your source code, you may want to choose an XML config file to control the obfuscation process.

With XML config file, you have the most flexible way to define an obfuscation mechanism that meets your requirement.

The XML file resembles very much to the hierarchy of a .NET assembly, it contains similar elements, such as module, class, method, field, etc. In each level, you can specify how obfuscation should be done.

A Simple Example

<?xml version='1.0'?>
<!DOCTYPE obfuscator SYSTEM "obfuscator.dtd">
<obfuscator>
  <target>exe</target>
  <option name="ildasm" value="false" /> 
  <module>
    <file>SimpleTest.exe</file>
    <name>SimpleTest</name>
	<target>dll</target>
	<output>SimpleTestOB.exe</output>
    <namespace obfuscate="true">
	  <name>Test</name>
	  <mapto>TestOB</mapto>
	  <class rename="false">
	    <name>SimpleTest</name>
		<method rename="false">
          <name>foreachTest</name>
          <signature>void ()</signature>
		</method>
        <method rename="false">
          <name regex="true">.*</name>
          <attribute>public virtual</attribute>
        </method>
		<field rename="false">
          <name regex="true">.*</name>
          <attribute>private static</attribute>
		</field>
	  </class>
	  <class>
	    <name>SimpleException</name>
		<attribute>private</attribute>
		<field rename="false">
			<name regex="true">.*</name>
		</field>
	  </class>
     </namespace>
  </module>
</obfuscator>

DTD - the syntax

The following list is the DTD of the XML config file. In this section, we will explain all elements in details.

<!-- Remotesoft obfuscator XML configure file DTD. All rights reserved. March 2002 -->

<!ELEMENT obfuscator (var*, target?, option*, reference*, outdir?, nameconvention?, plugin?, module*)>

<!ELEMENT var EMPTY>
<!ATTLIST var name CDATA #REQUIRED>
<!ATTLIST var value CDATA #REQUIRED>

<!-- target should be exe or dll -->
<!ELEMENT target (#PCDATA)>

<!-- output directory -->
<!ELEMENT outdir (#PCDATA)>

<!ELEMENT option EMPTY>
<!ATTLIST option name CDATA #REQUIRED>
<!ATTLIST option value CDATA #REQUIRED>

<!-- specify an external assembly/module reference -->
<!ELEMENT reference (#PCDATA)>

<!-- name convention to define how obfuscation name is given, the default is:
A, a, B, b, C, c, ..., A1, a1, ..., A2, a2, ...
Here you can define as many names as you want, the obfuscator will fecth them one by one,
after all names are used, a number will be appended.
-->
<!ELEMENT nameconvention (item*)>
<!ELEMENT item (#PCDATA)>

<!ELEMENT plugin (classname, assemblyfile)>
<!ELEMENT classname (#PCDATA)>
<!ELEMENT assemblyfile (#PCDATA)>

<!ELEMENT module (file, (name | mapto)?, target?, output?, use?, (namespace | class | field | method | resource)*)>
<!ATTLIST module obfuscate (false | true) "true">
<!ATTLIST module rename (false | true) "false">
<!ATTLIST module export (false | true) "true">

<!ELEMENT file (#PCDATA)>

<!-- The following element is used to specify an obfuscated file for the module, 
	so the module will not be obfuscated, and other modules should perform 
	incremental obfuscation based on this obfuscated file.
-->
<!ELEMENT use (#PCDATA)>

<!-- output file path, relative to outdir, if absolute, then superseds outdir -->
<!ELEMENT output (#PCDATA)>

           
<!ELEMENT namespace (name, mapto?, class*)>
<!ATTLIST namespace obfuscate (false | true) "true">
<!ATTLIST namespace rename (false | true) "false">
<!ATTLIST namespace keeppublic (false | true) "false">


<!ELEMENT class (name, mapto?, attribute?, (field | method | property | event | class)*)>
<!ATTLIST class obfuscate (false | true) "true">
<!ATTLIST class rename (false | true) "true">
<!ATTLIST class keeppublic (false | true) "false">
<!ATTLIST class keepfields (false | true) "false">

<!ELEMENT field (name, mapto?, attribute?)>
<!ATTLIST field obfuscate (false | true) "true">
<!ATTLIST field rename (false | true) "true">

<!ELEMENT method (name, mapto?, attribute?, signature?)>
<!ATTLIST method obfuscate (false | true) "true">
<!ATTLIST method rename (false | true) "true">          

<!ELEMENT property (name, mapto?, attribute?)>
<!ATTLIST property obfuscate (false | true) "true">
<!ATTLIST property rename (false | true) "true">

<!ELEMENT event (name, mapto?, attribute?)>
<!ATTLIST event obfuscate (false | true) "true">
<!ATTLIST event rename (false | true) "true">

<!ELEMENT resource (name, mapto?)>
<!ATTLIST resource obfuscate (false | true) "true">
<!ATTLIST resource rename (false | true) "true">

<!ELEMENT attribute (#PCDATA)>

<!ELEMENT signature (#PCDATA)>

<!ELEMENT name (#PCDATA)>
<!ATTLIST name regex (false | true) "false">

<!ELEMENT mapto (#PCDATA)>
The <obfuscator> Element
<!ELEMENT obfuscator (var*, target?, option*, outdir?, nameconvention?, plugin?, module*)>

The XML config file starts with this root element, it may contain variable definitions (<var>), global options (<option>), obfuscation target (<target>), output directory (<outdir>), name convention (<nameconvention>), plugin definition (<plugin>), and one or more module definitions (<module>).

The <var> Element
<!ELEMENT var EMPTY>
<!ATTLIST var name CDATA #REQUIRED>
<!ATTLIST var value CDATA #REQUIRED>

This element defines variables to use in the XML config file. Each variable is defined using the required name-value attributes, e.g.,

<var name="workdir" value="d:\salamander\output" />

Other elements can refer to this variable using the following notation,

$(var)
e.g.,

<module>
  ...
  <output>$(workdir)\SimpleTestOB.exe</output>
  ...
</module>  

The value of the variable will replace the var reference when XML file is parsed, so the above output element will have a value of d:\salamander\output\SimpleTestOB.exe

The <target> Element - exe or dll
<!ELEMENT target (#PCDATA)>

The target element defines the obfuscation visibility, the value should be either exe or dll. When the target is exe, the obfuscator considers the assemblies to be self-contained, and obfuscates all possible names very aggressively, including public members, and removes all properties and events definitions. When the target is dll, the obfuscator considers the assemblies to be libraries, therefore public members are not obfuscated, and only non-public properties and events are removed. If you specify a value rather than exe or dll, an error will be thrown.

The <outdir> Element
<!ELEMENT outdir (#PCDATA)>

This element specifies the ouput directory to store the obfuscated assemblies. It works together with the <output> element. If no outdir is specified, the obfuscated files will be put into a sub directory, called obfuscated , in the same directory where the original assemblies reside.

The <option> Element
<!ELEMENT option EMPTY>
<!ATTLIST option name CDATA #REQUIRED>
<!ATTLIST option value CDATA #REQUIRED>

This element is used to specify the global options for the obfuscator, zero or more such elements may exist in the <obfuscator> element, a name-value pair is required to specify an option, e.g.,

<option name="ildasm" value="true" />

indicates that the obfuscator should emit code to prevent ILDASM from dissembling the obfuscated files. The global options specified in XML config file has low priority than those specified in the command line.

The <nameconvention> Element
<!ELEMENT nameconvention (item*)>
<!ELEMENT item (#PCDATA)>

This element defines how obfuscation names are given. By default, the obfuscator uses 52 letters, as shown in the following naming convention:

A,  a,  B,  b,  C,  c, ...,  Z, z
A1, a1, B1, b1, C1, c1, ..., Z1, z1
A2, a2, B2, b2, C2, c2, ..., Z2, z2
...

the obfuscator fetches them one by one, after all names are used, a number will be appended by the end. If you do not like the default, you can define a new naming convention here. Define as many names as you want, the obfuscator will fetch them one by one, after all are used, a number is appended.

For each name, make an <item> element, pay attention to the order as it will be the order for the name to be retrieved by the obfuscator, for example, the following name convention uses C# keywords for the obfuscated names,

<nameconvention>
  <item>abstract</item>
  <item>as</item>
  <item>base</item>
  <item>bool</item>
  <item>break</item>
  <item>byte</item>
  <item>case</item>
  <item>catch</item>
  <item>char</item>
  <item>checked</item>
  <item>class</item>
  <item>const</item>
  <item>continue</item>
  <item>decimal</item>
  <item>default</item>
  <item>delegate</item>
  <item>do</item>
  <item>double</item>
  <item>else</item>
  <item>enum</item>
  <item>event</item>
  <item>explicit</item>
  <item>extern</item>
  <item>false</item>
  <item>finally</item>
  <item>fixed</item>
  <item>float</item>
  <item>for</item>
  <item>foreach</item>
  <item>goto</item>
  <item>if</item>
  <item>implicit</item>
  <item>in</item>
  <item>int</item>
  <item>interface</item>
  <item>internal</item>
  <item>is</item>
  <item>lock</item>
  <item>long</item>
  <item>namespace</item>
  <item>new</item>
  <item>null</item>
  <item>object</item>
  <item>operator</item>
  <item>out</item>
  <item>override</item>
  <item>params</item>
  <item>private</item>
  <item>protected</item>
  <item>public</item>
  <item>readonly</item>
  <item>ref</item>
  <item>return</item>
  <item>sbyte</item>
  <item>sealed</item>
  <item>short</item>
  <item>sizeof</item>
  <item>stackalloc</item>
  <item>static</item>
  <item>string</item>
  <item>struct</item>
  <item>switch</item>
  <item>this</item>
  <item>throw</item>
  <item>true</item>
  <item>try</item>
  <item>typeof</item>
  <item>uint</item>
  <item>ulong</item>
  <item>unchecked</item>
  <item>unsafe</item>
  <item>ushort</item>
  <item>using</item>
  <item>virtual</item>
  <item>void</item>
  <item>while</item>
</nameconvention>

The following image shows an example using the the above name convention.

Unicode can be used to specify the name convention, if you want to use non-displayable characters as obfuscated names, you can do something as follows, ILDASM will have trouble displaying these names, however such non-displayable characters can be easily converted by a decompiler.

<nameconvention>
	<item>&#01;</item>
	<item>&#02;</item>
	<item>&#03;</item>
	<item>&#04;</item>
	<item>&#05;</item>
	<item>&#06;</item>
	<item>&#07;</item>
	<item>&#08;</item>
	<item>&#09;</item>
	<item>&#10;</item>
	<item>&#11;</item>
	<item>&#12;</item>
	<item>&#13;</item>
	<item>&#14;</item>
	<item>&#15;</item>
	<item>&#16;</item>
	<item>&#17;</item>
	<item>&#18;</item>
	<item>&#19;</item>
	<item>&#20;</item>
</nameconvention>

The following image shows an example using the above naming covention.

Name convention file is a special type of XML config file, which may not contain any other elements except for the root and the <nameconvention> element. Such an xml file can be used as input for the -nc option of the obfuscator command line. The following is a simple name convention xml file:

<?xml version='1.0'?> <!DOCTYPE obfuscator SYSTEM "obfuscator.dtd">
<obfuscator>  
    <nameconvention>
        <item>this</item>
        <item>is</item>
        <item>a</item>
        <item>sample</item>
        <item>name</item>	
        <item>convention</item>
    </nameconvention>  
</obfuscator>
The <plugin> Element
<!ELEMENT plugin (classname, assemblyfile)>
<!ELEMENT classname (#PCDATA)>
<!ELEMENT assemblyfile (#PCDATA)>

This element is used to extend the obfuscator. A classname and an assembly file need to be specified. The class must implement Remotesoft.IObfuscator interface. The obfuscator will invoke the methods defined in the interface during the obfuscation process. This way provides you with programmatic control of the obfuscation process. In your classes, you can access many internal data structures of the assembly.

This feature is not supported in the first version.

The <module> element
<!ELEMENT 
module (file, (name | mapto)?, target?, output?, use?, (namespace | class | field | method)*)>
<!ATTLIST module obfuscate (false | true) "true">
<!ATTLIST module rename (false | true) "false">
<!ATTLIST module export (false | true) "true">

The module element specifies how the given module should be obfuscated. The term module here also refers to an assembly, since the difference of an assembly and a module is minimal, an assembly has a manifest while a module does have a manifest. An assembly at least contains one module.

The hierarchy of the module element is the same as the logical structure of a module. It may contain global fields, methods, and classes. Classes are organized in different namespaces. The term class also refers to interfaces, enums and structs.

You can define the obfuscation rules at each granularity. The rules in smaller granularity always supersede the ones in upper level. When regular expressions are used, searches are performed in the same level of hierarchy.

the <module> element may not be present in the xml file in the case of a name convention file that only contains a <nameconvention> element. Such an xml file can be used with the -nc option of the obfuscator command line.

The export attribute

when this attribue is true, the module should be exported, and its public symbols should not be obfuscated; when this attribute is false, the module will be considered as an internal module, and even its public symbols will be obfuscated.

The obfuscate and rename attributes

These two attributes are common to the module element and its child nodes. The obfuscate attribute defines whether the associated element and its members need to be obfuscated, the default is true. When it is false, its associated element and that element's child elements will not be obfuscated, all obfuscation rules will be ignored. The rename attribute is different from the obfuscate attribute, it tells the obfuscator whether to rename the target element when that element is obfuscatable. A target element might have a true obfuscate value, but rename is false. For example, the following specifies that the class is to be obfuscated, but keeps the class name unchanged.

<class obfuscate=true rename=false>

When rename is false, you should not specify a <mapto> element.

The <file> Element
<!ELEMENT file (#PCDATA)>

This element specifies the input file name of the module for obfuscation. Obfuscator will load the module from this file.

The <output> Element
<!ELEMENT output (#PCDATA)>

This element specifies the output file name to save for the module after obfuscation, if the path is relative, then it will be created under the directory specified by the <outdir> element, if the path is absolute, then it superseds the <outdir> element. When this element does not exist, the same file name is used as that of the original module.

The <use> Element
<!ELEMENT use (#PCDATA)>

This element is used for incremental obfuscation. It specifies a previously generated obfuscated file for the module, so this module will not be obfuscated again, and other modules should perform incremental obfuscation based on this obfuscated file.

The <namespace> Element
           
<!ELEMENT namespace (name, mapto?, class*)>
<!ATTLIST namespace obfuscate (false | true) "true">
<!ATTLIST namespace rename (false | true) "false">
<!ATTLIST namespace keeppublic (false | true) "false">

A module may contain zero or more namespaces, a namespace contains at least one class. You can specify whether a namespace should be obfuscated, whether it should be renamed, and how it should be renamed.

When the keeppublic attribute is true, all public members within this namespace will be excluded from obfuscation, including all public types and their public members, recursively. This attribute is useful when you want to obfuscate everything except to keep a few namespaces.

The <class> Element
<!ELEMENT class (name, mapto?, attribute?, (field | method | property | event | class)*)>
<!ATTLIST class obfuscate (false | true) "true">
<!ATTLIST class rename (false | true) "true">
<!ATTLIST class keeppublic (false | true) "false">
<!ATTLIST class keepfields (false | true) "false">

A class belongs to a namespace, or directly to the module (the global namespace). The term class is used here in a general sense to refer to classes, interfaces, enums and structs. You can use this element to specify whether a class should be obfuscated, whether it should be renamed, and how it should be renamed.

Each class may contain zero or more fields, methods, properties, events and nested classes. By default, if the obfuscation target is exe, all classes and their members are renamed except those can not be changed, such as special method names (.ctor and .cctor), and virtual methods that are first declared in external assemblies. If the obfuscation target is dll, then all members that need to be exported to the outside will be kept unchanged, in addition to those can not be obfuscated.

When the keeppublic attribute is true, all public members within the current type will be excluded from obfuscation.

When the keepfields attribute is true, all fields within the current type will be excluded from obfuscation, including private fields. This attribute is useful, for example, hen you have trouble in dealing with object.

The <field> Element
<!ELEMENT field (name, mapto?, attribute?)>
<!ATTLIST field obfuscate (false | true) "true">
<!ATTLIST field rename (false | true) "true">

A field belongs to a class, or directly to the module (global fields). You can use this element to specify whether a field should be obfuscated, whether it should be renamed, and how it should be renamed. The signature subelement is optional.

The <method> Element
<!ELEMENT method (name, mapto?, attribute?, signature)>
<!ATTLIST method obfuscate (false | true) "true">
<!ATTLIST method rename (false | true) "true">          

A method belongs to a class, or directly to the module (global methods). You can use this element to specify whether a method should be obfuscated, whether it should be renamed, and how it should be renamed. The signature subelement is required since methods are usually overloaded.

By default, methods are renamed except those can not be changed, such as special method names (.ctor and .cctor), and virtual methods that are first declared in external assemblies. For libraries, public methods are kept unchanged. If you want to overwrite the default settings, you need to use this element to define your own obfuscation rules.

The <property> and <event> Element
<!ELEMENT property (name, mapto?, attributes)>
<!ATTLIST property obfuscate (false | true) "true">
<!ATTLIST property rename (false | true) "true">

<!ELEMENT event (name, mapto?, attribute?)>
<!ATTLIST event obfuscate (false | true) "true">
<!ATTLIST event rename (false | true) "true">

Properties and events belong to classes, they are usually used by tools such as compilers, the runtime does not need them. By default, if the obfuscation target is exe, all properties and events are completely removed from the assembly. If the obfuscation target is dll, then only public properties and events are kept unchanged, others are completely removed from the assembly.

The <resource> Element
<!ELEMENT resource (name, mapto?)>
<!ATTLIST resource obfuscate (false | true) "true">
<!ATTLIST resource rename (false | true) "true">

A resource is any nonexecutable data that is logically deployed with an application. A resource might be displayed in an application as error messages or as part of the user interface. Resources can contain data in a number of forms, including strings, images, and persisted objects. Each resource has a name in the MSIL code. This element is used to preserve a resource given its name.

The <attribute> Element
<!ELEMENT attribute (#PCDATA)>

This element allows the specification of modifiers, usually the accessibility of its target, to restrict the matching process. This element can be used with a class, a field, a method, a property or an event. Only those matching the specified attributes will be processed, e.g.,

<class>
  <name>SimpleTest</name>	
  <method rename="false">
    <name regex="true">.*</name>	
    <attribute>  
      public static
    </attribute>
    ...
  </method>
</class>  

The above sample code tells the obfuscator not to obfuscate all public and static methods in class SimpleTest.

The legal values of this element is shown below depending on its target. A list of values separated by a space, ' ', might be used for this element as well, in which case all of attributes should be matched.

attribute class field method property event
public yes yes yes yes yes
private yes yes yes yes yes
family yes yes yes yes yes
assembly yes yes yes yes yes
famandassem yes yes yes yes yes
famorassem yes yes yes yes yes
nested yes
interface yes
sealed yes
static yes yes yes yes
abstract yes yes yes yes yes
virtual yes yes yes
final yes yes yes

The <signature> Element
<!ELEMENT signature (#PCDATA)>

This element is used to specify a signature for field, method, property or event. You must specify a signature when you define <method> element, but signature for fields, properties and events definitions are optional.

Signature is defined using C# convention, e.g.,

<signature>
  String(int, String[])
</signature>  

Parameter names can be ignored, types will be automatically resolved from the class and module where this method is defined, therefore you can use simple type names.

The <name> Element
<!ELEMENT name (#PCDATA)>
<!ATTLIST name regex (false | true) "false">

This element defines a name for identifiers. When the regex attribute is true, matches will be searched in its enclosing element level, for example, if you define a <method> element in a <class> element with a regular expression method name, all methods in that class will be searched to match the regular expression, and the obfuscation rules will be performed to those methods.

The <mapto> Element
<!ELEMENT mapto (#PCDATA)>

This element specifies a name to be used for an identifier after obfuscation.

6. Extending Remotesoft Obfuscator

This feature provides you with the most powerful way to control the obfuscation process. You write your own code to define the renaming process.

This feature is not supported in the first release.

7. Handling of other files

Associated with your assembly or module files, there are other types of sattelite files need to be handled. Fortunately, our obfuscator deals with them automatically. What you need to do is make sure that these files can be accessed from the current directory.

Resource files

There is no special handling for resource files, our obfuscator handles them automatically in most situations. Sometimes, when a literal resource name is used, you will need to preserve certain namespaces or classes.

In this section, we'll show you how to trouble shoot resource problems through an example, WinCV.exe, distributed with Microsoft .NET Framework SDK.

First, let's simply use the default settig to obfuscate it, as shown below. Notice the resource name is automatically renamed to A.B.resources to reflect the namespace obfuscation (see the red circle). 

Second, test run the program. Simply click the "!" button to launch the program, we will see a JIT debug dialog box, select a debuuger, and the following error message appears. Obviously, the resource "Microsoft.Tools.SR" is mising, and thus the program can not run successfully. This indicates that the obfuscation has not performed correctly.

Using our decompiler, and we locate that the resource is refered through the following statement,

               resources = new ResourceManager("Microsoft.Tools.SR", base.GetType().Module.Assembly); 

Since the resource name,  Microsoft.Tools.SR, is refered as a literal string, however, the obfuscation we just performed automatically change the resource name to A.B, no wonder the program won't run after obfuscation. In this case, we have to preserve the resource name. Right click the "Microsoft.Tools.SR" class node, and select "Preserve" menu item, as shown below,

Now, perfrom the obfuscation, the resource name is preserved, and the program runs correctly as the original.

Signature files

Our obfuscator deals with strong named assemblies automatically, no extra work is required.

Usually, attaching a strong name signature to your code involves two steps:

(1) create the strong name key using the SN utility:   sn -k sample.snk

(2) compile your assembly with the key by adding a declaration to the assembly to indicate the location of the key file, as shown below,

C#:
[assembly: AssemblyKeyFile("sample.snk")]
class StrongNameTest
{
  ...
}

VB.NET:
<assembly: AssemblyKeyFile("sample.snk")>
Public Class StrongNameTest
  ...
End Class

Just make sure that your key file is in the correct path when the obfuscator is invoked, and our obfuscator will automatically re-sign the modified assembly with the same key file. If you do not have access to the key file, you can choose to delay sign the protected assembly, or choose to remove the signature, so you can continue to work with the obfuscated code.

8. Working with the Graphical User Interfaces - Remotesoft .NET Explorer

Remotesoft .NET Explorer is a generic object browser and disassembler with professional look and feel. It offers the same functionality as Microsoft ILDASM utility, plus low level viewing of metadata and PE format. Remotesoft .NET Explorer works together with our decompiler, obfuscator and protector, and acts as a console for easy navigation and powerful code editing and printing.  This tool can be used as a source code editor, and it has a powerful syntax coloring system that recognizes many popular source files, including IL, C#, C/C++, VB, ASP, JAVA, HTML, FORTRAN, PHP, etc.

Remotesoft .NET Explorer works together with our protector, and act as a console.

Browsing

Remotesoft .NET Explorer can be used alone as an assembly browsing and disassembling utility. In this regard, you can consider it to be something like Microsoft ILDASM, but with more functionality. It supports viewing of low level metadata, and most of all, it allows modification of the loaded assemblies.

Metadata Viewer

Remotesoft .NET Explorer has built-in support for viewing low level .NET metadata. The following is a screen shot that shows the string heap of the selected assembly. A hex dump of the file is display in the lower right pane.

PE Format Explorer

Remotesoft .NET Explorer has built-in support for viewing low level PE (Portable Executable) format. It recognizes both .NET images and native images that are compile from C/C++. The following is a screen shot shows the CLI header of the selected assembly. A hex dump of the file is displayed in the lower right pane..

Integrating With Protector

You can use the explorer to configure and perform the obfuscation interactively, then view the result immediately. If you are not satisfied with the result, you may change your config and redo the obfuscation. After you are done with config, simple click the "obfuscate" button to perform the obfuscation.

Configuring

Integrated with the obfuscator, Remotesoft .NET Explorer provides a console to facilitate the obfuscation process. You can use it to configure the obfuscation, then either performs the obfuscation immediately, or save the XML config file, and use the command line to perform the obfuscation.

Global Options

The global option page can be brought up by right click a node and select "Setting", or from menu "Actions" -> "Settings", or "Tools" -> "Options", then click Obfuscator tab.. The following shows the dialog box where global options can be specified to control the obfuscation process.

Each option is corresponding to a command line option as described in the Command line section, please refers to that section for more details. The global options are available to all assemblies that are currently loaded, and their values will be kept until they are modified next time.

Name Preservation

As we discussed earlier, certain symbols have to be excluded from obfuscation in some situations. Remotesoft .NET Explorer allows you to preserve any symbols very easily. Select any namespace, class, method or field node, right click and then select Preserve menu, the selected symbol will be preserved from obfuscation. A symbol with name preservation will be marked with red color. Use the same menu to remove a name preservation.

As shown below, the left panel shows how to preserve a class name, so that class will not be renamed after obfuscation. The right panel shows the results of the obfuscation, notice the selected class name is indeed kept unchanged.

Name Mapping

Use the MapTo menu, to create a name mapping for a symbol, so that symbol will be named to your specification after obfuscation.

As shown below, the left panel shows how to map a class name, so that class will be renamed as you specified after obfuscation. The right panel shows the results of the obfuscation, notice the selected class indeed have the obfuscated name as specified.

Keep Public Members

This option is used to preserve all public members of a namespace or a type. Right click a type or namespace node, and select "Advanced", and then click "Keep Public Members" from the popup menu. All public types under the selected namespace, or all public fields and methods under the selected type will have their names preserved.

Keep All Fields

In some situations, for example, when dealing with object serialization, you may want to preserve all fields of a class. Right click a type node, and select "Advanced", and then click "Keep All Fields" from the popup menu. All fields under that class will not be obfuscated.

Declare Private Modules

Select a top level module node, and use the Mark As Private to mark/unmark a private module. When a module is marked as private, it indicates that the selected module is an internal component, and thus its public symbols will be fully obfuscated. Cross references from other modules will be obfuscated accordingly.

Private modules are marked with a closed diamond icon.

Adding External Assembly References

The obfuscator requires the availability of all referenced assemblies in order to perform the best obfuscation. When some of them are not available, the obfuscator takes a conservative approach by not obfuscating those symbols when required external references are not available. Therefore, it is important to tell the obfuscator where the references are. You can select menu "Actions" -> "Add reference" to bring up a file dialog box where you can choose a file to add as a reference. You can add multiple references for the obfuscator.

The obfuscator resolves external assemblies according to the following order:
(1) The directory that holds the assembly to obfuscate,
(2) The references added by the user, as described as above, or use ref element in an XML config file,
(3) The .NET Framework directory.

Config File Generation

When you are satisfied with the config, you can use the Save XML Config menu to save the XML config file. This file has the format as described above.

A saved XML config file can be opened using the Explorer, and it can be also used as an input file for obfusactor command line.

9. Creating patch files

Incremental obfuscation

Incremental obfuscation allows you to obfuscate a new assembly in consistent with the previous obfuscation results, and therefore you can perform hot fixs by distributing a small patch to your customers.  This is extremely useful when your products consist of a few assemblies, and later you need to modify one of them or add a new assembly.

The incremental obfuscation will help, when your products consist of multiple assemblies, and later, you want to ship a new component to customers, but without redistributing those components your customers already have. Therefore, you want to obfuscate the new component based on the name maps generated from the last run.

Most obfuscators requires you to save a complete log file with all name mappings in order to perform an incremental obfuscation. These kind of log files are hard to maintain. Fortunately, our obfuscator does not rely on any logs, and it is extremely easy to perform incremental obfusaction. Instead of any log files, our obfuscator uses the original and obfuscated assemblies that you already have to perform incremental obfuscation. You simply need to specify an obfuscated file that has been previously generated for a module. 

An example

In this section, we give an example of doing incremental obfuscation. Use .NET Explorer to open UseSimpleLib.xml from the samples folder in Remotesoft Obfuscator installation directory.

UseSimpleLib.exe (compiled from UseSimpleLib.cs) refers to assembly SimpleLib.dll (compiled from SimpleTest.cs). The XML config file, UseSimpleLib.xml, performs the obfuscation with SimpleLib.dll as private component, and thus both UseSimpleLib.exe and SimpleLib.dll are obfuscated completely. Click the "Obfuscate" button, the obfusacted code will be saved to samples\obfusacted folder

Now, you are ready to ship the obfuscated images, UseSimpleLib.exe and SimpleLib.dll, to your customers.

After some period, you find that you need to add one more method into UseSimpleLib.exe, see the new source code in UseSimpleLib1.cs. One way is to perform the same obfusaction as before, and re-ship both images again. However, the best way would be to tell your customers to ony update the modified component, UseSimpleLib.exe. Now, you will have to perform an incremental obfusaction using the previous run.

The following are steps to perform an incremental obfuscation.

1. Right click a module node that you want to use an exisiting obfuscated file. In our example as shown below, right click "Set Incremental File..." on SimpleLib.dll

2. A file dialog will appear, and you can select the obfuscated file from previous obfusaction run, in our case, choose "samples\obfuscated\SimpleLib.dll". After the file is set, the tree node will be colored as pink.

3. Simply click the "Obfuscate" button to perform the obfuscation,

4. You can now use "Save XML Config..." from the "File" menu to save the configuration, which can be later on opened through "Open XML Config...", or used in a batch process.

Advanced issues

Name conversion utility

With this utility, you can easily look up the original/obfuscated symbol names.

1. Go to "Tools", "Name Utility" to open the utility

2. Input the original/obfuscated file names, you can also browse to selecy a file

3. Click the "OK" button, and the two modules will be loaded, click a node to see the orginal/obfuscated symbol names.

Working with .NET Compact Framework

This section explains how to obfuscate applications built with Micrsoft .NET Compact Framework. Detailed instructions are also presented here for developers who have not worked with the .NET Compact Framework.

Installation of .NET Compact Framework and Windows CE Emulator

Visual Studio .NET 2003 has strong and built-in support for developing smart device application with the Microsoft .NET Compact Framework, please skip this section if you are using Visual Studio .NET 2003. More information can be found at Microsoft web page.

If you do not have Visual Studio .NET 2003, you can follow this section to develop smart device applications with the freely available Microsoft .NET Framework and .NET Compact Framework, and an Windows CE Emulator. Follow the links below to download the frameworks and the emulator:

(1) Microsoft .NET Framework, http://www.microsoft.com/netframework

This is the standard .NET Framework 1.1, if your system already have it, please skip the installation.

(2) Microsoft Windows CE .NET 4.2 Emulation Edition

This is a big package, we are only interested in the .NET Compact Framework and the device emulator that are included with the package, After installation, one directory, usally C:\WINCE420\OTHERS\DOTNET, contains two folders, MANAGED and X86, that hold the managed assemblies and the native runtime for the .NET Compact Framework. An WinCE device emulator will be installed, which will be used as our testing device.

(3) Windows CE .NET 4.2 Device Emulator

Skip this if you have already installed (2), however if you do not want to install (2), you can follow the link to install only the device emulator.

(4) Microsoft .NET Compact Framework 1.0

This is the latest .NET Compact Framework, the installation may not be complete, buf some cab files (netcf.core.ppc3.SH3.cab, netcf.core.ppc3.MIPS.cab and netcf.core.ppc3.ARM.cab) will be left in C:\Program Files\Microsoft .NET Compact Framework. Open the netcf.core.ppc3.ARM.cab file with WinZip, and extract them to a directory, say, c:\netcf. The following table shows the file names, you need to rename those files as shown in the table. You can download this batch file, rename.bat, to rename the files.

Original File Name Rename to
00system.012 system.dll
00mscoree.001 mscoree.dll
Calend~1.006 calendar_1_0.nlp
cgacutil.003 cgacutil.exe
Charin~1.007 charinfo_1_0.nlp
Cultur~1.008 culture1_1_0.nlp
Cultur~1.009 culture2_1_0.nlp
Micros~2.020 microsoft.visualbasic.dll
Micros~3.021 microsoft.windowsce.forms.dll
Mscore~1.002 mscoree1_0.dll
mscorlib.011 mscorlib.dll
Ne8a18~1.000
Netcf_~1.999
Netcf1~1.004 netcf1_0license.txt
Netcfa~1.005 netcfagl1_0.dll
Region~1.010 region_1_0.nlp
Sy17b8~1.014 system.web.services.dll
Sy40c7~1.017 system.xml.dll
Sy5dd4~1.015 system.windows.forms.dll
Sy9b57~1.016 system.windows.forms.datagrid.dll
Syb769~1.018 system.net.irda.dll
Syd8c9~1.013 system.drawing.dll
System~2.019 system.data.dll

Create a seperate directory, say, c:\netcf\managed, and copy all of the managed assemblies into the new directory. Make sure that mscoree.dll is not copied. This directory will be the place where you compile your smart device applications, read the following section for more info.

Copy all of the native runtime DLLs from C:\WINCE420\OTHERS\DOTNET\X86 and all of the managed assemblies from c:\netcf\managed to c:\netcf, which will be the directory to execute the smart device applications. Notice the native DLLs renamed earlier do not work well, we have to use the files from the installation of Microsoft Windows CE .NET 4.2 Emulation Edition as described earlier in this section.

Developing smart device applications without Visual Studio .NET and without a device

Now that we have the .NET Compact Framework installed, let's develop a simple "Hello World" program for the smart device. Shown below is a very simple C# program, HelloCE.cs

// HelloCE.cs, .NET Compact Framework 1.0

using System; 
using System.Windows.Forms; 

public class HelloCE : Form { 
	private System.Windows.Forms.Button button1; 

	public static int Main(string[] args) {         
		Application.Run(new HelloCE()); 
		return 0; 
	}

	public HelloCE() {
		this.Text = "HelloCE";
		this.button1 = new System.Windows.Forms.Button(); 
		this.button1.Location = new System.Drawing.Point(30, 50); 
		this.button1.Size = new System.Drawing.Size(88, 88); 
		this.button1.Text = "Hello World"; 
		this.Controls.Add(button1); 
	}

	public virtual void test()
	{
	}		
}

Compiling

To compile .NET CF program, make sure the following command is running from the directory that contains all of managed assemblies of the .NET Compact Framework, and use /nostdlib option to tell the compiler that the standard .NET Framework class libraries should not be used.

C:\netcf\managed>csc /r:mscorlib.dll /r:system.windows.forms.d ll HelloCE.cs /nostdlib

If you run the compiler from different directory that does not have the managed assemblies, it will not work. The compiler will refer some assemblies, e.g., system.windows.forms.dll, from the standard .NET Framework directory.

Testing

An assembly, HelloCE.exe, will be created after the above compilation. You need to copy the file to c:\netcf directory that holds the runtime and managed assemblies. Now open the device emulator,

      Click Start > All Programs > Microsoft Windows CE .NET 4.2 > Device Emulator > Sample WebPad

Then go to menu Emulator > Folder Sharing, and the following dialog box will appear, and type c:\netcf into the text box, and click OK,

After the shared folder is set, you can now double click the My Computer icon in the emulator, and the shared folder appears as a storage card, click the Storage Card, you will see all of the files in c:\netcf, double click our smart application, HelloCE.exe, the program executes and "Hello World" displays in the screen.

Obfuscating smart device applications

Obfuscating applications for .NET Compact Framework is about the same as for the regualr .NET Framework except that we need to explicitly tell the obfuscator where to load the the compact version of the standard assemblies, such as mscorlib.dll, system.windows.forms.dll, system.xml.dll, etc. This can be achieved in two ways,

(1) Copy the assembly to be obfuscated to the same directory that holds the standard assemblies, then run the command line or use the .NET Explorer to perform the obfuscation. The obfuscator will load the dependent DLLs from the current directory, and the obfuscated assembly will be saved into obfuscated sub directory.

(2) If the assembly to be obfuscated is located in different directory than the .NET CF libraries, you need to specify the dependent DLLs using -ref option from the command line, or click Actions > Add reference from the .NET Explorer, see Adding External Reference section for more info.

To test the obfuscated assembly, copy it to the directory where the .NET Compact Framework runtime resides, and invoke the emulator as described above, or deploy to the real device to test.

When the obfuscator is invoked, make sure that the .NET Compact Framework runtime (mscoree.dll) is not in the path, undefined errors will occur if the compact version of mscoree.dll is used by the obfuscator. For example, we can not run the obfuscator from c:\netcf directory where .NET CF runtime is installed.

Advanced Topics

Suppose an obfuscator has no bugs, then the only place that might go wrong is reflections, which refers to a set of APIs that fetch information or invoking methods, fields, and other members from literal strings. Since obfuscator renames symbols while keep literal strings unchanged, members corresponding to those literal strings might have been given to different names after obfuscation, and thus can not be retrieved again by the same literal strings. All of runtime problems arise from reflections. In this case, the obfuscator must be configured so that symbol names that are involved with reflections are excluded from obfuscation.

Runtime problems and trouble shooting

Most of the time, the obfuscated assembly runs exactly same as the original. Howerver, there are cases where the obfuscated images behave differently, and thus you need to debug and find what is wrong. It is almost always true that the problem can be solved by configuring the obfuscator so some members are exluded from obfuscation. Our obfuscator does not reliy on ildasm-ilasm round trip, and all debug information is kept in tact, so you can simply replace the original with the obfuscated file, and step through your code as usual to identify the problem.

Peverify

Our obfuscator directly manipulates binary executables, and sometimes the metadata might get messed up. The first thing you can try is to use peverify on the obfuscated image,

    peverify yourfile

This will tell you whether the image is valid in terms of metadata and MSIL code, if you find errors, please use option,s such as,  -samesize, -keepmd, etc. If the problem still exists, please report a bug to support@remotesoft.com.

Debuging and Stack Tracing

Our obfuscator does not rely on ildasm-ilasm round trip, instead, it  directly manipulates the binary executable, and thus all debug information can be kept in tact,  for example, the line number info is still the same, so you can simply replace the original file with the obfuscated one, and step through your source code as usual to identify the problem. This is impossible to achieve with obfuscators that use ildasm-ilasm round trip, since the obfuscated assembly is re-generated from the MSIL code using the assembler tool, ilasm.exe, and the debug information generated by ilasm.exe, is totally different and no long relevant to your original source code, in other words, you can not debug your source code with the obfuscated assemblies.

To debug, you need to build a debug version of your assembly, and then perform obfusaction with -keepdebug option from the comnand line, or click the Do not remove debug info check box, see below.

Let's walk through an example to show how to identify and solve reflection related problems. The sample is located in samples\ReflectionTest directory, compile it with debug info as below:

    csc /debug ReflectionTest.cs

Execute it and the following results will be displayed on the screen,

You should see an exception here:
System.NullReferenceException: Object reference not set to an instance of an obj
ect.
   at ReflectionTest.Main() in C:\protector\samples\Reflection\ReflectionTest.cs
:line 28

You should see 1234, here: 1234
name = My Name

Now, open the .NET Explorer, and performs an obfuscation with debug info kept (see above). The obfuscated image will be saved as samples\Reflection\obfuscated\ReflectionTest.exe, copy the obfuscated file to replace the original file in samples\Reflection directory. The following results are displayed when executed:

You should see an exception here:
System.NullReferenceException: Object reference not set to an instance of an obj
ect.
   at A.A.A() in C:\protector\samples\Reflection\ReflectionTest.cs:line 28

System.NullReferenceException: Object reference not set to an instance of an obj
ect.
   at A.A.A() in C:\protector\samples\Reflection\ReflectionTest.cs:line 40
name = My Name

Compare the results before and after obfuscation, we notice two things,

(1) line number info (line 28), is exactly same for the first exception, which illustrates our obfuscator's unique feature that it keeps debug info intact.

(2) the obfuscated image generates different results, and the exceptions tells us that line 40 caused the problem, check our source code and we locate the following lines:

  #39: FieldInfo fi = typeof(ReflectionTest).GetField("which", BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);
  #40: Console.WriteLine("You should see {0}, here: {1}", data, fi.GetValue(rt));

Immediately, we identify the problem is due to reflection. Since the obfuscated image contains exactly same debug info, we can easily launch the CLR debugger to step through the code, see below (to launch the CLR debugger, doubclick DbgCLR.exe from GuiDebug directoy under .NET Framework installation).

We can notice that all debug related information are preserved, exactly same as the original assembly does, except for some class names and names of their members that have been modified by the obfuscator. This intact debug info is invaluable when there is a runtime problem arises from obfuscation. Most obfuscators do not have such functionality.

From the debugger, we know that fi is null that causes the runtime problem. Since fi is retrieved via reflections, we know that we have to preserve the which field in order to solve the problem. Please continue to read the next section on how to preserve the field.

The stack trace has some strange symbol names, such as A.A.A(), what are the original symbols? It is very easy to identify them, simply load the original and obfusacted images, please read the Name Conversion Utility section for more info.

Reflection

Download all samples here.

ReflectionTest is a simple sample that uses reflection to get the value of a field, which, since the field is renamed during the obfuscation, and it will be no longer retrieved by the same name "which". In order to make the code to work, we need to tell the obfuscator not to rename it.

FieldInfo fi = typeof(ReflectionTest).GetField("which", BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);

Follow the following steps,

(1) go to samples\Reflection directory, compile the sample, csc ReflectionTest.cs

(2) use the .NET Explorer to load the exe file, and select the ReflectionTest class, then click the which field, right click the node, and then select Preserve from the popup menu. For how to identify the symbol to preserve, please read the Debuging and Stack Tracing section above.

(3) click the Obfuscate button to perform the obfuscation.

Resources

A resource is any nonexecutable data that is logically deployed with an application. A resource might be displayed in an application as error messages or as part of the user interface. Resources can contain data in a number of forms, including strings, images, and persisted objects. Each resource has a distinct name within the assembly, and this name is used programmatically for reference to the resource.

Resources can be used in many different ways, but eventually it goes to the following methods of System.Resources.ResourceManager or Assembly,

 
(1) Creates a ResourceManager that looks up resources in satellite assemblies based 
on information from the specified Type. 

public ResourceManager(
   Type resourceSource
) 

Parameters
resourceSource A Type from which the ResourceManager derives all information for finding .resources files.

Remarks
The ResourceManager infer the assembly, the base name, and a namespace for .resources 
files from the Type. The ResourceManager assumes you will be using satellite 
assemblies and want to use the default ResourceSet class.

Example
Given a type such as MyCompany.MyProduct.MyType, the ResourceManager will
look for a .resources file (in the main assembly and satellite assemblies) named 
"MyCompany.MyProduct.MyType.[culture name.]resources" in the assembly that 
defines MyType.

(2) Initializes a new instance of the ResourceManager class that looks up resources 
contained in files derived from the specified root name using the given Assembly.

public ResourceManager(
   string baseName,
   Assembly assembly
)

Parameters
baseName  The root name of the resources. For example, the root name for the resource
          file named "MyResource.en-US.resources" is "MyResource". 
assembly  The main Assembly for the resources. 

Remarks
The individual resource files should be contained in satellite assemblies with the 
invariant culture's .resources file contained in the main assembly. A satellite 
assembly is assumed to contain resources for a single culture specified in that 
assembly's manifest, and are loaded as necessary.

(3) Loads the specified manifest resource, scoped by the namespace of the specified type, from this assembly.

public virtual Stream GetManifestResourceStream(
   Type type,
   string name
)

Parameters
type     The type whose namespace is used to scope the manifest resource name. 
name     The name of the manifest resource being requested. 
Return   A Stream representing this manifest resource.

Remarks
This method works on both public and private resources.

Example
If the full name of type is "MyNameSpace.MyClasses" and name is "Net", 
GetManifestResourceStream will search for a resource named MyNameSpace.Net.

From the above, we notice that a resource might be determined from the name of a type, or from a literal string. By default, our obfuscator renames the embeded resources when types are renamed, and thus you do not need to cofigure the obfuscator most of the time. Only when a literal string is used to refer to a resouce, as in (2) and (3) above, the obfuscator must be informed to exclude the resource from obfuscation. For example, the following code can be used to construct a resource, a literal string is used to identify the resource, and thus that resouce must be excluded from obfuscation.

     Assembly.GetManifestResourceStream("Test.Resources.Strings.txt"); 

ResourceTest Sample

Download all samples here.

This example uses a string to refer to a resouce, and thus the resource Test.myStrings.resources must be preserved.

  ResourceManager LocRM = new ResourceManager("Test.myStrings", typeof(ResourceTest).Assembly);

To make this sample to work, follow the following steps:

(1) compile ResourceTest.cs with the csc compiler,  and an assembly, ResourceTest.exe, will be created. Make sure the .NET Framework directory is in the path. Run the following batch file to build,

      build.bat

(2) open Remotesoft .NET Explorer, drag and drop ResourceTest.exe file to the left panel of Remotesoft .NET Explorer, and you should see a tree view structure of the assembly,

(3) preserve the Test.myStrings.resources  resource, click the Resources node, and select the Test.myStrings.resources node, right click, and select Preserve, and the node should be marked as red,

Notice the other resource, TestOK.myStrings.txt, is handled automatically by the obfuscator since that resource is refered based on a type, as shown below. The obfuscator synchronizes the transformation of the referenced type and the resource, and thus no configuration is required. 

Stream s = typeof(ResourceTest).Assembly.GetManifestResourceStream(typeof(TestOK.ResourceTestOK), "myStrings.txt");

App.exe.licenses Resource and License NAG Screen

If you are using a licensed components in your exe, a special resource named as app.exe.licenses is usually present. Most of the time, you will have to preserve this resource; otherwise, a NAG screen will show up.

Serialization

Serialization implicitly uses many reflection calls, and therefore obfuscation must be configured. This section presents several walkthroughs to guide you thorugh the process of configuring the obfuscator.

XML serializer is usually constructed with a type as input argument, for example, the following code snippet shows how a XMLSerializer is instantiated and an object of type AddressBook, addrbk, is serialized to a stram. The .NET runtime relies on reflection calls to save an XML file that uses fields and properties as the tag names. You must tell our obfuscator what types, fields and properties must be excluded from obfuscation, otherwise the serialization will not work. In the code show below, the type AddressBook , its fields and properties, Owner and Addresses , must be preserved. See the full examples below on how to preserve those symbols.

C#:
XmlSerializer serializer = new XmlSerializer(typeof(AddressBook));
...
serializer.Serialize(stream,
addrbk); VB.NET:

Dim
serializer As New XmlSerializer(GetType(AddressBook))
...
serializer.Serialize(stream, addrbk) 

Serialized XML Format: 
<AddressBook>
  <Owner>Huihong</Owner>
  <Addresses>...</Addresses>
</AddressBook>

From the serialization samples shown below, the following observation can be made,

  1. the name of the serializing type must be excluded from obfuscation,
  2. the fields of the serializing type must be excluded from obfuscation,
  3. the properties of the serializing type must be preserved.
  4. the type names, fields, properties of all base types of the serializing type must be excluded from obfuscation.

One exception exists when the serialized objects are consumed only by the same obfuscated code, then the type can be obfuscated without the constraints shown above.

Address Book Sample

Download all samples here.

This example uses XMLSerializer to serialize and deserialize instances of the AddressBook type, which contains two properties, Addresses and Owner. The Addresses property returns a list of the Address type.

To make this sample to work, follow the following steps:

(1) open the solution file, and build the project. The assembly, Address Book.exe, is saved into Address Book\bin directory,

(2) open Remotesoft .NET Explorer, drag and drop Address Book.exe file to the left panel of Remotesoft .NET Explorer, and you should see a tree view structure of the assembly,

(3) preserve the AddressBook type, right click the node, and click "Preserve", and the node should be marked as red,

(4) preserve the Address type and its fields, right click the node, click "Preserve", and then right click the node again, and select "Advanced", then click "Keep all fields" from the dropdown list, see the image below,

(5) preserve properties, right click and select "Settings", then choose the "Obfuscator" page, and check the "do not change metadata (keep properties and events)", see below

(6) save the config file, and perform obfuscation. Use menu "File" -> "Save XML Config..." to save the xml config file, and click the obfuscate button to perform obfuscation.

XMLSerializerTest Sample

Download all samples here.

This example first constructs an instance of  XmlTypeMapping, which in turn passes to XMLSerializer to serialize and deserialize instances of the Group, the GroupType type and the Car type. As we discussed in the AddressBook sample, all of  these types and their base types need to be excluded from obfuscation, and their fields and properties should be also excluded.

XmlTypeMapping myMapping = (new SoapReflectionImporter().ImportTypeMapping(typeof(Group))); 
XmlSerializer mySerializer = new XmlSerializer(myMapping);

To make this sample to work, follow the following steps:

(1) compile XMLSerializerTest.cs with the csc compiler,  and the assembly, XMLSerializerTest.exe, will be created. Make sure the .NET Framework directory is in the path.

csc XMLSerializerTest.cs

(2) open Remotesoft .NET Explorer, drag and drop XMLSerializerTest.exe file to the left panel of Remotesoft .NET Explorer, and you should see a tree view structure of the assembly,

(3) preserve the Grouptype and its fields, right click the node, select "Preserve", and the node should be marked as red. Then right click the node again, and select "Advanced", then select "Keep all fields" from the dropdown list.

(4) preserve the Cartype and its fields, right click the node, seclect "Preserve", and the node will be marked as red. Then right click the node again, and select "Advanced", then select "Keep all fields" from the dropdown list.

(5) preserve the base type of Car, the Vehicle type and its fields, right click the node, seclect "Preserve", and the node will be marked as red. Then right click the node again, and select "Advanced", then select "Keep all fields" from the dropdown list. 

(6) save the config file, and perform obfuscation. Use menu "File" -> "Save XML Config..." to save the xml config file, and click the obfuscate button to perform obfuscation.

Remoting

Remoting implicitly uses reflections, and thus may cause runtime problems after obfuscation. The same kind of care should be taken as that for serialization.

ASP.NET

Our obfuscator can be used to obfuscate code-behind DLLs for ASP.NET. Code-behind DLLs are no different from the regular DLLs, and the obfuscation procedures are exactly same.


Copyright 2011 Remotesoft Inc. All rights reserved.