Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Babel Obfuscator

0.00/5 (No votes)
23 Sep 2008 1  
Babel obfuscator for .NET Framework
Babel Obfuscator

Introduction

This article is an introduction to obfuscation with Babel, an obfuscator for .NET Framework that I wrote recently. At the moment it is a console based application.

Babel has the following features:

  • Obfuscate Namespace, Type, Method, Events, Properties and Field
  • Unicode normalization: Names are chosen in a set of Unicode not readable symbols
  • Support generic types and virtual function obfuscation
  • MSIL Control Flow Obfuscation
  • String Encryption

Babel is based on Microsoft Phoenix framework. Phoenix is a framework for building compilers and numerous tools for program analysis, optimization, and testing.

If you want use Babel you must first download and install Phoenix at: http://connect.microsoft.com/Phoenix/Downloads/DownloadDetails.aspx?DownloadID=12911

Background

Babel is an obfuscator that is build on top of Phoenix framework. I wrote this obfuscator because I need it for one of my early project. I try to use some readymade freeware obfuscators unsuccessfully. There are several professional obfuscators on the market but they are not cheap. I know that writing an obfuscator is not an easy task but two weeks ago I came across an article about Microsoft Phoenix SDK so I realized that should be possible to write this obfuscator. The task was not as easy as I thought at the beginning because the SDK at the moment is still pre release and the documentation is not complete. Moreover I found some strange behavior about this SDK that I was not able to solve yet.

Using Babel

This is a little introduction to command line arguments for Babel Obfuscator. From DOS window start Babel with option --help or -?

C:\> babel -?

Babel Obfuscator version 1.1.0.0 by Alberto Ferrazzoli
usage:
babel.exe [options] <assembly file>

options:
                    - Miscellaneous -
--help                    Display this usage message
--verbose <n>             Set output verbose level (Short form: -v <n>) (default: 1)
--statistics              Show obfuscation statistics
--[no]debug               Emit debugging information (default: disabled)

                    - Type Obfuscation -
--[no]types               Enable ([no]disable) types name obfuscation 
			(Short form: -[no]t)
--[no]events              Enable ([no]disable) events name obfuscation 
			(Short form: -[no]e)
--[no]methods             Enable ([no]disable) methods name obfuscation 
			(Short form: -[no]m)
--[no]properties          Enable ([no]disable) properties name obfuscation 
			(Short form: -[no]p)
--[no]fields              Enable ([no]disable) fields name obfuscation 
			(Short form: -[no]f)
--[no]virtual             Enable ([no]disable) internal virtual 
			function name obfuscation(default: disabled)
--[no]flatns              Flatten namespaces (Short form: -[no]n) (default: enabled)
--[no]unicode             Enable ([no]disable) Unicode normalization 
			(Short form: -[no]u) (default: enabled)

                    - MSIL Control Flow Obfuscation -
--[no]msil                Enable ([no]disable) MSIL control flow obfuscation 
			(default: enabled)
--iliterations <n>        Set the number of iteration to use in MSIL 
			control flow obfuscation algorithm (default: 2)

                    - String Encryption -
--[no]stringencrypt       Enable ([no]disable) string encryption (default: disabled)

                    - Output Files -
--output <file>           Specify output file name (default: .\BabelOut\<assembly file>)
--pdb <file>              Specify debug information file name 
			(default: output file name with .pdb extension)

                    - Input Files -
--keyfile <keyfile>       File containing key to sign the assembly 
			(Short form: --kf <keyfile>)
--keyname <keyname>       Key container name of key to sign assembly 
			(Short form: --kn <keyname>)
--rules <file>            XML file containing obfuscation rules

You can enter the option in the command line using option prefix -- or -, -- tells Babel you want use long option so that the option must be entered as it is shown in the command line help ex.: --help --keyfile etc. - is the short form for an option. The option in this case takes only one character and can be specified in bundle with other option ex.: -not -mpf means disable type obfuscation and enable obfuscation for methods properties and fields. Sometime for an option are available aliases. Aliases provides shortcut for an option: for example --keyfile option has an alias --kf. When Babel parse long options names use auto-abbreviation for resolve the option. So --statistics can be shortening by --stat. Some options are negatable. These options are like switch that can be turned on or off. All negatable options may have the prefix 'no' to turn off or disable the function specified.

Options

--statistics

Show a short statistic on the operation performed by Babel. ex.:

H:\Projects\Babel>hexm.exe --stat
Processing hexm.exe ...
Analyzing...
Obfuscating...
Writing BabelOut\hexm.exe...

Statistics:
Type Obfuscation
Unicode normalization: True
Flatten namespaces: True
Virtual functions: False

Symbols statistic:
    96/[  107]        types: 89.72 %
     9/[   10]       events: 90.00 %
   737/[ 1169]      methods: 63.05 %
   177/[  232]   properties: 76.29 %
  1091/[ 1486]       fields: 73.42 %

  2110/[ 3004]      overall: 70.24 %

MSIL Control Flow Obfuscation
Number of iterations: 2
Number of obfuscated functions: 581
Average number of branch instruction per function: 5.535284

--[no]virtual

Enable or disable virtual method obfuscation. For default are disabled. Enabling virtual obfuscation result in a better obfuscate assembly, anyway sometimes this produce an invalid assembly that may fail during execution. You can try to enable this option and then run test on obfuscated assembly.

--keyfile <keyfile>

Resign an obfuscated assembly with key pair stored into a file. The obfuscation process change the content of the assembly, so if the assembly was strong named it must be resigned otherwise it fails to load during execution.

NB: this option work only if the assembly was previously signed and do not work for assembly without a strong name.

--keyname <keyname>

Resign an obfuscated assembly with key pair stored in a key container.

NB: this option work only if the assembly was previously signed and do not work for assembly without a strong name.

--rules <file>

XML file containing obfuscation rules. See Babel Rules section.

--[no]msil and --iliterations <n>

While --msil option enable and disable control flow obfuscation, --iliteration set the number of iteration to use in MSIL control flow obfuscation (default 2). MSIL control flow obfuscation consists in scrambling the instructions of a given function and inserting appropriate jumps so that the original flow is not changed. This stop some tools like Reflector to display the C# or VB.NET code list but not the IL that anyway is twisted and result difficult to read.

Reflector show MSIL control flow obfuscation

By increasing the number of iteration, more scrambling is made on IL and so more jump are inserted into the IL code of a function. The code result more difficult to read but slower because the increased number of jump inserted.

--[no]flatns

Flattening namespaces means remove the original namespace information for a given type and put the type into the global namespace. Ex:

   MyNamespace
     MyClass
       MyNestedClass     

After obfuscation became:

   -      -> Global namespace
     a    -> MyClass
       a  -> MyNestedClass     

With flatns disabled the original namespace names are maintained.

--[no]unicode

Enable or disable Unicode normalization. With Unicode normalization enabled all names of types methods fields and so on are chosen in a set of Unicode characters not readable.

Reflector show unicode obfuscation

--[no]stringencrypt

Enable or disable user string encryption.

To encrypt string Babel needs two internal methods declared into the target assembly: EncryptString and DecryptString. These two methods must have the following declaration:

       internal System.String EncryptString(System.String text) { ... }
       internal System.String DecryptString(System.String text) { ... }

They may be static as well as instance methods.

During string obfuscation, Babel searches these two methods into the target assembly. Then it searches all functions to find any user string pushed into the managed stack by the IL instruction:

       ldstr "my secret string"      

For each ldstr instruction do a call to EncryptString method passing as first argument the string to encrypt and replacing the ldstr operand with the EncryptString return value. To obtain the original string during execution Babel needs to add a call to DecryptString method just after ldstr instruction:

       ldstr "askldjaldjadd"
       call DecryptString

During execution the encrypted string pushed into the managed stack is decrypted on the fly by DecryptString method that return the original string by pushing its return value into the stack.

To enhance obfuscation and protection Babel removes the code of EncryptString method and change the name of DecryptString in something not readable.

Reflector show string obfuscation

This kind of approach to string obfuscation is unique because is up to you the choice of encryption algorithm. You can implement the simplest encryption algorithm, for example xoring string bytes, or one more complicated involving asymmetric encryption. An example of EncryptString that xor string bytes could be the following one:

       internal static string EncryptString(string text)
       {
           int len = text.Length;
           char[] chars = text.ToCharArray();
           char[] res = new char[len];
 
           int seed = 0x4312;
           while (--len >= 0)
           {
               res[len] = (char)((int)chars[len] ^ seed++);
           }
 
           return new String(res);
       }

Since we use xor, the DecryptString method is identical.

Babel Rules

This new release of babel introduce rule files. Rule files are XML files that contains information to guide the process of obfuscation. The rule file consist of a list of rules defined by XML element <Rule></Rule>. This list is enclosed by the element <Rules></Rules> that is also the root document element.

<Rules version="1.0">
   <Rule>
   </Rule>
   <Rule>
   </Rule>
   ...
</Rules>

The Rules element attribute version is used to define the current schema version and must be set to "1.0".

The defined rules are processed during type obfuscation: each rule into the file is checked against the assembly symbols (classes, methods, properties, fields). If a symbol match all the rule properties, the symbol obfuscation action is taken according to the boolean attribute obfuscate value:

<Rule name="my rule" obfuscate="false">
	...
</Rule>

The Rule element has another attribute, name, that is used to name the rule. The name and obfuscate attributes are mandatory as specified by the XSD schema.

Any Rule element has five other child elements:

Access, Target, Pattern, HasAttribute, Description.

The first three elements are mandatory, whereas the last two are optional.

Access: Specify the symbol visibility for this rule.

Admitted values are: All or any combination of Public, Protected, Internal, Private.

Target: Specify which kind of symbol should be checked.

Admitted values are: All or any combination of Classes, Delegates, Structures, Interfaces, Enums, Events, Methods, Properties, Fields, StaticFields

Pattern: any wildcard expression or regular expression, the boolean attribute isRegEx states if the pattern is a regular expression. If a regular expression is used, remember to enclose the regular expression definition string in a CDATA section, ex:

<Pattern isRegEx="true"><![CDATA[^Properties.*]]></Pattern>

The optional elements are:

  • HasAttribute: specify a list of fully qualified type attributes. If the target has at least one of these attribute the rule match. This element may have the boolean attribute onEnclosingType. If this attribute is set to "true" the attribute presence is checked on the target symbol enclosing type. Otherwise if it is set to "false" the attribute presence is checked on the target symbol.
  • Description: any useful rule description

The following example show the content of SafeRules.xml file distributed with Babel 1.1.0.0 package:

<Rules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           version="1.0">
  <Rule name="Properties types" obfuscate="false">
    <Access>All</Access>
    <Target>All</Target>
    <Pattern isRegEx="false">*Properties*</Pattern>
    <Description>Do not obfuscate all Properties namespace types.</Description>
  </Rule>
  <Rule name="Serializable types" obfuscate="false">
    <Access>All</Access>
    <Target>Fields, StaticFields</Target>
    <Pattern><![CDATA[*]]></Pattern>
    <HasAttribute onEnclosingType="true">System.SerializableAttribute</HasAttribute>
    <Description>Do not obfuscate fields of Serializable types.</Description>
  </Rule>
</Rules>

The first rule tells babel to do not obfuscate any symbol inside the Properties namespace or whatever symbol contains Properties in his fully qualified name. This rule is required by .NET winform applications that put resources access into the Properties namespace.

The second rule tells babel to do not obfuscate fields of types that are declared as serializable. The Serializable attribute presence is checked on the field enclosing type because the XML attribute onEnclosingType is set to true.

Using this file for all windows form applications is a good starting point. Follow the XSD schema that babel use to validate the input XML rule file.

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Rules" type="RulesType" />
  <xs:complexType name="RuleType">
    <xs:all>
      <xs:element minOccurs="1" name="Access" type="xs:string" />
      <xs:element minOccurs="1" name="Target" type="xs:string" />
      <xs:element minOccurs="1" name="Pattern" type="PatternType" />
      <xs:element minOccurs="0" name="HasAttribute" type="HasAttributeType" />
      <xs:element minOccurs="0" name="Description" type="xs:string" />
    </xs:all>
    <xs:attribute name="name" type="xs:string" use="required" />
    <xs:attribute name="obfuscate" type="xs:boolean" use="required" />
  </xs:complexType>
  <xs:complexType name="PatternType">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute default="false" name="isRegEx" type="xs:boolean" use="optional" />
        <xs:attribute default="false" name="ignoreCase" type="xs:boolean" />
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:complexType name="HasAttributeType">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute default="false" name="onEnclosingType" 
			type="xs:boolean" use="optional" />
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:complexType name="RulesType">
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
      <xs:element name="Rule">
        <xs:complexType>
          <xs:complexContent mixed="false">
            <xs:extension base="RuleType" />
          </xs:complexContent>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute fixed="1.0" name="version" type="xs:string" use="required" />
  </xs:complexType>
</xs:schema>

Debugging Tips

Sometimes happens that an obfuscated assembly cannot load and the nasty debug dialog pop ups. With obfuscated assembly call stack is unreadable and it is difficult to understand where the problem is. it is also possible that the program starts without any problem but its behavior is not as usual or/and the produced results are wrong, or even better during the execution a crash may occur.

No panic, there are several things you can do to understand the problem. First be sure that the original assembly was not signed and if it so take care to use the same key for the option --keyfile or --keyname.

Then try to generate debug info for the obfuscated target using --debug command line option. Starting the obfuscate target with pdb file loaded should help in find the origin of the problem.

You can also try to relax the obfuscation disabling virtual function obfuscation. If --virtual option is disabled and the assembly do not load try to disable option --flatns. Flatting namespaces can introduce some problem with resource managers especially if they load at run-time the resource using full qualified names.

As a last resort, try to disable progressively Type, Methods, Events, Properties and Fields name obfuscation. At this point the stack should be readable and I hope the problem will be clear.

Command Line Examples

Follow some examples of Babel command line use:

C:\>babel.exe mylib.dll --nomsil -noun --kf milib.snk

Start Babel on mylib.dll assembly with no MSIL control flow obfuscation and with Unicode normalization and flat namespaces both disabled. The obfuscated mylib.dll is put in BabelOut directory.

C:\>babel.exe mylib.dll -v 3 --output mylib_babel.dll

Start Babel on mylib.dll with verbose output message level 3 and save the obfuscated library as mylib_babel.dll. All the remaining options take default values.

C:\>babel.exe mylib.dll --novirtual --noflatns -notme

Start Babel on mylib.dll assembly with both virtual function obfuscation and flat namespace options disabled. Also type, method and event names obfuscation are disabled. The obfuscated mylib.dll is put in BabelOut directory.

Open Points

Babel of course is not perfect. There is a lot to do, this is a list of open points that maybe introduced in a future release:

  • Resource encryption

    It will be great if this obfuscator could compress/encrypt also managed resources.

  • Mixed code (managed + native)

    I have not tested Babel with mixed (managed + native) code assemblies. Anyway Phoenix library state some additional requirements for native code input binaries, so I expect problem with this kind of assemblies.

  • An user interface

    I think this is not completely necessary but it won't do any harm.

I have not included the capability of merge assemblies. I know this is a nice feature, but if you need it you can use IlMerge tool. Just merge your assemblies with IlMerge then use Babel to obfuscate the resulting assembly.

History

Because Babel is a protection tool I decided to not distribute the source code. You can use Babel for free also on commercial products. Beware that because the nature of free software, Babel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. The project is hosted at Google Code.

Babel current release version is 1.1.0.0

Updates can be downloaded at http://code.google.com/p/babelobfuscator.

Babel v. 1.1.0.0

Features:

  • Obfuscate Namespace, Type, Method, Events, Properties and Field using Unicode symbols or standard characters
  • Support generic types and virtual functions
  • MSIL Control Flow Obfuscation
  • String Encryption
  • Obfuscation controlled by XML rule file

Babel v. 1.0.0.0

Features:

  • Obfuscate Namespace, Type, Method, Events, Properties and Field using Unicode symbols or standard characters
  • MSIL Control Flow Obfuscation
  • String Encryption

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here