Obfuscating C# Code

Semantic Designs can construct custom obfuscators for virtually any source language as a part of the corresponding Source Formatter. This page contains C# sample code, its obfuscated version, and the generated obfuscation map.

C# Sample Code before Obfuscation

(This is the same formatted code shown on the C# Formatter example page)


/*
 * BSD Licence:
 * Copyright (c) 2001, Lloyd Dupont (lloyd@galador.net)
 * 
 * All rights reserved.
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the  nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
using antlr;
using antlr.collections;
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Text;
public class GLType
  {
    public string typeName;

    public int stars;

    public GLType(string GLType)
      {
        typeName = GetCSType(GLType);
      }

    /** return the generation specific level of this type.
     *  roughly equal to the number of polymorphic type
     * it could have. <br>
     * <b>Note</b> a return type has a maximum of 3 level, anyway. */
    public int Level
      { get
          {
            if (stars == 0)
              return 1;
            if (stars == 1
                && typeName == "void")
              return 10;
            return 3;
          } }

    public bool IsVoid
      { get
          {
            return stars == 0
                   && typeName == "void";
          } }

    /** return the C# representation of a type for a given level.
     * @param isReturn tell wether or not it is a return argument as
     * the representation are different
     * @param codeFriendly to generate a compilable name for CFunction
     * name. */
    public string ToString(int level, bool isReturn, bool codeFriendly)
      {
        if (stars == 0)
          return typeName;
        if (level == 0) // C - name
          return typeName+StarString(codeFriendly ?
                                     'P' : '*');
        if (stars > 1
            || isReturn
            || level == 1)
          return "IntPtr";
        if (typeName == "void")
          switch (level)
          { case 2 :
              return "byte[]";
            case 3 :
              return "sbyte[]";
            case 4 :
              return "short[]";
            case 5 :
              return "ushort[]";
            case 6 :
              return "int[]";
            case 7 :
              return "uint[]";
            case 8 :
              return "float[]";
            case 9 :
              return "double[]"; }
        return typeName+"[]";
      }

    string StarString(char c)
      {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < stars; i++ )
          sb.Append(c);
        return sb.ToString();
      }

    public bool IsPointer
      { get
          {
            return stars > 0;
          } }

    public int Size
      { get
          {
            switch (typeName)
            { case "void" :
                return 0;
              case "byte" :
              case "sbyte" :
                return 1;
              case "short" :
              case "ushort" :
                return 2;
              case "int" :
              case "uint" :
                return 4;
              case "float" :
                return 4;
              case "double" :
                return 8;
              default :
                throw new ArgumentException("unknown base type"); }
          } }

    static Hashtable typeTable;

    public static string GetCSType(string GLType)
      {
        if (typeTable == null)
          typeTable = createTypeTable();
        string ret = (string)typeTable[GLType];
        if (ret == null)
          {
            Console.Error.WriteLine("warning: unknown type \""+GLType+"\" use as is.");
            typeTable[GLType] = GLType;
            ret = GLType;
          }
        return ret;
      }

    static Hashtable createTypeTable()
      {
        Hashtable ret = new Hashtable();
        ret["void"] = "void";
        ret["GLvoid"] = "void";
        ret["GLenum"] = "uint";
        ret["GLbyte"] = "byte";
        ret["GLshort"] = "short";
        ret["GLint"] = "int";
        ret["GLsizei"] = "int";
        ret["GLubyte"] = "byte";
        ret["GLuint"] = "uint";
        ret["GLfloat"] = "float";
        ret["GLushort"] = "ushort";
        ret["GLclampf"] = "float";
        ret["GLdouble"] = "double";
        ret["GLclampd"] = "double";
        ret["GLboolean"] = "byte";
        ret["GLbitfield"] = "uint";
        return ret;
      }

  }

C# Code after Obfuscation

The "line" below is the obfuscated C# code. Because all unnecessary line breaks have been removed, the obfuscated version is hard to view. Use the horizontal scroll bar on your browser to appreciate it properly.


using O111; using O111.l1000; using System; using System.Collections; using System.l1001; using System.l1010; using System.Text; public class l1011 { public string l1100; public int l1101; public l1011(string l1011) { l1100 = O1110(l1011); } public int O1111 { get { if (l1101 == 0) return 1; if (l1101 == 1 && l1100 == "v\u006F\u0069\u0064") return 012; return 3; } } public bool O10000 { get { return l1101 == 0 && l1100 == "\u0076oid"; } } public string O10001(int O10010, bool O10011, bool l10100) { if (l1101 == 0) return l1100; if (O10010 == 0) return l1100+l10101(l10100 ? '\u0050' : '\u002A'); if (l1101 > 1 || O10011 || O10010 == 1) return "\u0049n\u0074\u0050\u0074\u0072"; if (l1100 == "\u0076o\u0069d") switch (O10010) { case 2 : return "b\u0079\u0074\u0065[\u005D"; case 3 : return "sbyte\u005B]"; case 4 : return "\u0073\u0068\u006Fr\u0074[]"; case 5 : return "\u0075\u0073h\u006Fr\u0074\u005B]"; case 6 : return "\u0069\u006E\u0074[\u005D"; case 7 : return "\u0075int[]"; case 8 : return "\u0066\u006C\u006Fa\u0074[]"; case 011 : return "d\u006Fu\u0062\u006C\u0065\u005B]"; } return l1100+"\u005B]"; } string l10101(char O10110) { l10111 O11000 = new l10111(); for (int O11001 = 0; O11001 < l1101; O11001++ )O11000.l11010(O10110); return O11000.O10001(); } public bool O11011 { get { return l1101 > 0; } } public int O11100 { get { switch (l1100) { case "v\u006Fid" : return 0; case "b\u0079\u0074\u0065" : case "\u0073b\u0079te" : return 1; case "s\u0068o\u0072\u0074" : case "\u0075\u0073h\u006Fr\u0074" : return 2; case "i\u006Et" : case "u\u0069nt" : return 4; case "\u0066loat" : return 4; case "\u0064\u006F\u0075b\u006Ce" : return 8; default : throw new l11101("unkno\u0077\u006E \u0062\u0061se\u0020\u0074ype"); } } } static O11110 l11111; public static string O1110(string l1011) { if (l11111 == null)l11111 = O100000(); string l100001 = (string)l11111[l1011]; if (l100001 == null) { l100010.l100011.l100100("\u0077a\u0072\u006Ei\u006Eg:\u0020u\u006Ekno\u0077\u006E\u0020\u0074\u0079\u0070\u0065\u0020\u0022"+l1011+"\u0022\u0020use \u0061\u0073 \u0069\u0073."); l11111[l1011] = l1011; l100001 = l1011; } return l100001; } static O11110 O100000() { O11110 l100001 = new O11110(); l100001["v\u006Fid"] = "\u0076o\u0069\u0064"; l100001["\u0047L\u0076oid"] = "v\u006F\u0069\u0064"; l100001["G\u004Cenum"] = "u\u0069n\u0074"; l100001["G\u004Cby\u0074\u0065"] = "\u0062\u0079t\u0065"; l100001["\u0047\u004C\u0073h\u006F\u0072\u0074"] = "\u0073hort"; l100001["\u0047Lint"] = "\u0069\u006E\u0074"; l100001["\u0047Lsizei"] = "i\u006Et"; l100001["\u0047L\u0075\u0062yt\u0065"] = "b\u0079t\u0065"; l100001["\u0047\u004C\u0075\u0069n\u0074"] = "\u0075int"; l100001["G\u004Cfloat"] = "\u0066l\u006F\u0061t"; l100001["\u0047L\u0075short"] = "ushor\u0074"; l100001["G\u004Cclamp\u0066"] = "f\u006Coat"; l100001["\u0047Ldouble"] = "d\u006Fuble"; l100001["\u0047L\u0063lampd"] = "\u0064ouble"; l100001["G\u004Cbo\u006F\u006C\u0065\u0061n"] = "\u0062yte"; l100001["\u0047\u004C\u0062i\u0074\u0066iel\u0064"] = "\u0075int"; return l100001; } }

Because it is so hard to view, we've duplicated it below, adding line breaks roughly 80 characters apart. While a would-be reverse-engineer can do this too, it doesn't help him much. Notice that comments are gone, names have been scrambled, text strings obscured. Larger constants (none in this example) have their radix twiddled. The obfuscator uses special lists provided by the user to define names that should be preserved, ensuring that public interfaces and accesses to public libraries remain valid. If you obfuscate a set of C# source files simultaneously, only the public symbols they collectively offer will be visible in the compiled source files. An SD-supplied list covers standard C# system and library calls.


using O111; using O111.l1000; using System; using System.Collections; using
System.l1001; using System.l1010; using System.Text; public class l1011 {
public string l1100; public int l1101; public l1011(string l1011) { l1100 =
O1110(l1011); } public int O1111 { get { if (l1101 == 0) return 1; if (l1101
== 1 && l1100 == "v\u006F\u0069\u0064") return 012; return 3; } } public bool
O10000 { get { return l1101 == 0 && l1100 == "\u0076oid"; } } public string
O10001(int O10010, bool O10011, bool l10100) { if (l1101 == 0) return l1100;
if (O10010 == 0) return l1100+l10101(l10100 ? '\u0050' : '\u002A'); if (l1101
> 1 || O10011 || O10010 == 1) return "\u0049n\u0074\u0050\u0074\u0072"; if (
l1100 == "\u0076o\u0069d") switch (O10010) { case 2 : return "b\u0079\u0074\
u0065[\u005D"; case 3 : return "sbyte\u005B]"; case 4 : return "\u0073\u0068\
u006Fr\u0074[]"; case 5 : return "\u0075\u0073h\u006Fr\u0074\u005B]"; case
6 : return "\u0069\u006E\u0074[\u005D"; case 7 : return "\u0075int[]"; case
8 : return "\u0066\u006C\u006Fa\u0074[]"; case 011 : return "d\u006Fu\u0062\
u006C\u0065\u005B]"; } return l1100+"\u005B]"; } string l10101(char O10110) {
l10111 O11000 = new l10111(); for (int O11001 = 0; O11001 < l1101;
O11001++ )O11000.l11010(O10110); return O11000.O10001(); } public bool O11011 {
get { return l1101 > 0; } } public int O11100 { get { switch (l1100) { case "
v\u006Fid" : return 0; case "b\u0079\u0074\u0065" : case "\u0073b\u0079te" :
return 1; case "s\u0068o\u0072\u0074" : case "\u0075\u0073h\u006Fr\u0074" :
return 2; case "i\u006Et" : case "u\u0069nt" : return 4; case "\u0066loat" :
return 4; case "\u0064\u006F\u0075b\u006Ce" : return 8; default : throw new
l11101("unkno\u0077\u006E \u0062\u0061se\u0020\u0074ype"); } } } static
O11110 l11111; public static string O1110(string l1011) { if (l11111 == null)
l11111 = O100000(); string l100001 = (string)l11111[l1011]; if (l100001 ==
null) { l100010.l100011.l100100("\u0077a\u0072\u006Ei\u006Eg:\u0020u\u006Ekno\
u0077\u006E\u0020\u0074\u0079\u0070\u0065\u0020\u0022"+l1011+"\u0022\
u0020use \u0061\u0073 \u0069\u0073."); l11111[l1011] = l1011; l100001 =
l1011; } return l100001; } static O11110 O100000() { O11110 l100001 = new
O11110(); l100001["v\u006Fid"] = "\u0076o\u0069\u0064"; l100001["\u0047L\
u0076oid"] = "v\u006F\u0069\u0064"; l100001["G\u004Cenum"] = "u\u0069n\
u0074"; l100001["G\u004Cby\u0074\u0065"] = "\u0062\u0079t\u0065"; l100001["\
u0047\u004C\u0073h\u006F\u0072\u0074"] = "\u0073hort"; l100001["\u0047Lint"]
= "\u0069\u006E\u0074"; l100001["\u0047Lsizei"] = "i\u006Et"; l100001["\
u0047L\u0075\u0062yt\u0065"] = "b\u0079t\u0065"; l100001["\u0047\u004C\u0075\
u0069n\u0074"] = "\u0075int"; l100001["G\u004Cfloat"] = "\u0066l\u006F\
u0061t"; l100001["\u0047L\u0075short"] = "ushor\u0074"; l100001["G\u004Cclamp\
u0066"] = "f\u006Coat"; l100001["\u0047Ldouble"] = "d\u006Fuble"; l100001["\
u0047L\u0063lampd"] = "\u0064ouble"; l100001["G\u004Cbo\u006F\u006C\u0065\
u0061n"] = "\u0062yte"; l100001["\u0047\u004C\u0062i\u0074\u0066iel\u0064"]
= "\u0075int"; return l100001; } }

Obfuscated Symbol Cross Reference

The obfuscator produces a cross reference mapping obfuscated symbols to the orginal symbols, so that obfuscated code in the field can still be decoded if necessary. In fact, by reversing this map, the obfuscator can unobfuscate the code (of course, it cannot restore the comments).


### Obfuscated Identifiers ###
Append -> l11010
ArgumentException -> l11101
Console -> l100010
Error -> l100011
GLType -> l1011
GetCSType -> O1110
Globalization -> l1001
Hashtable -> O11110
IO -> l1010
IsPointer -> O11011
IsVoid -> O10000
Level -> O1111
Size -> O11100
StarString -> l10101
StringBuilder -> l10111
ToString -> O10001
WriteLine -> l100100
antlr -> O111
c -> O10110
codeFriendly -> l10100
collections -> l1000
createTypeTable -> O100000
i -> O11001
isReturn -> O10011
level -> O10010
ret -> l100001
sb -> O11000
stars -> l1101
typeName -> l1100
typeTable -> l11111

### Preserved Identifiers ###
Collections -> Collections
Forms -> Forms
InteropServices -> InteropServices
Runtime -> Runtime
System -> System
Text -> Text
Windows -> Windows

For more information: info@semanticdesigns.com    Follow us at Twitter: @SemanticDesigns

C Sharp
Obfuscation Example