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 ([email protected]) ** 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