Welcome to BARMAGY Sign in | Join | Help

Don’t rely on obfuscation

Managed code unlike native code have been known to be easily decompiled to it’s source code easing its reverse engineering thus giving the need to what we call obfuscation to change the managed code after compiling it in a way that makes decompilers obsolete and makes decompiling it useless as the decompilation will generate garbage code that can’t be understood or compiled again after modifying it. Obfuscation is mostly done with renaming the names of classes, methods and variables into random names rendering it unreadable when it’s decompiled and in the case of some obfuscators the output obfuscated application when decompiled generate a code that gives build errors when being compiled again. But although obfuscation sometimes proves to be efficient, it has major weakness and limitations that makes relying on it is not a good decision.

For the sake of demonstration in this article I’m going to use C# .net as my managed code and preemptive dotfuscator that comes as a community edition with Microsoft Visual Studio will be my obfuscation tool.

Say that we have this application that checks if the user is authenticated or not before doing an action

  private void btnSubmit_Click(object sender, EventArgs e)

        {

            //we authenticate the user here using the method Authenticate()

            if (Authenticate())

            {

                //if the user credential is valid then...

                MessageBox.Show("access granted");

                this.Run();

               

            }

            else

            {

                //else we kick him/her out

                MessageBox.Show("invalid credentials");

                this.Close();

            }

        }

So when we obfuscate this code and try to decompile it we get (I use Lutz reflector to do the decompile)

private void a(object A_0, EventArgs A_1)

{

    if (this.c())

    {

        MessageBox.Show("access granted");

        this.b();

    }

    else

    {

        MessageBox.Show("invalid credentials");

        base.Close();

    }

}

 

As it’s obvious most of the code have been renamed but the messages strings are untouched also the .net framework used classes and methods like MessageBox class and Show() method still not renamed which is a big problem, compiling the resulting code from decompiling obfuscated code might result build time errors because the obfuscated code might have the same names for methods and classes but this isn’t the same for IL (intermediate language) so if we simply used ildasm to disassemble the exe assembly for this application we will get this

 

C:\Program Files\Microsoft Visual Studio 8\VC>ildasm C:\Dotfuscated\password.exe /out=c:\password.il

 

.method private hidebysig instance void

          a(object A_0,

            class [mscorlib]System.EventArgs A_1) cil managed

  {

    // Code size       44 (0x2c)

    .maxstack  8

    IL_0000:  ldarg.0

    IL_0001:  call       instance bool a::c()

    IL_0006:  brfalse.s  IL_001a

 

    IL_0008:  ldstr      "access granted"

    IL_000d:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)

    IL_0012:  pop

    IL_0013:  ldarg.0

    IL_0014:  call       instance void a::b()

    IL_0019:  ret

 

    IL_001a:  ldstr      "invalid credentials"

    IL_001f:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)

    IL_0024:  pop

    IL_0025:  ldarg.0

    IL_0026:  call       instance void [System.Windows.Forms]System.Windows.Forms.Form::Close()

    IL_002b:  ret

  } // end of method a::a

 

As we can see again that our messages string is written in plain text also the used .net framework namespaces and here comes our message box again System.Windows.Forms.MessageBox::Show(string)

So what the problem in this? The problem that the old cracking techniques that were used with win32 applications can still be applied to .net assemblies very easily, so if I’m an experienced cracker I would disassemble this application into IL and search for the “invalid credentials” string that shows in my face every time I write in an invalid password and look up a few lines till I find the branching statement at line IL_0006 and easily I would change from brfalse to brtrue and build the application using ilasm

 

C:\Program Files\Microsoft Visual Studio 8\VC>ilasm c:\password.il /out=c:\password.exe

 

So next time I run the new built application when I supply an invalid user name and password I will get the welcome message saying “access granted” instead of being kicked out. And as it’s very obvious the same can be applied for cracking license keys and similar stuff.

But that was because my current obfuscation tool didn’t obfuscate the messages strings, right? So what if we obfuscate every available string in my application too, I will be doing this using the evaluation version of dotfuscator which have a feature called “string encryption” which personally I don’t consider encryption rather than ecoding or obfuscation because you can’t encrypt things and supply the encryption algorithm and key with it. So here is the disassemebled code after the string obfuscation

 

.method private hidebysig instance void

          eval_a(object A_0,

                 class [mscorlib]System.EventArgs A_1) cil managed

  {

    // Code size       71 (0x47)

    .maxstack  9

    .locals init (int32 V_0)

    IL_0000:  ldc.i4     0x3

    IL_0005:  stloc      V_0

    IL_0009:  ldarg.0

    IL_000a:  call       instance bool eval_a::eval_c()

    IL_000f:  brfalse.s  IL_002c

 

    IL_0011:  ldstr      bytearray (F0 90 F2 90 F4 96 F6 92 F8 8A FA 88 FC DD FE 98

                                    00 73 02 62 04 6B 06 73 08 6C 0A 6F )             // .s.b.k.s.l.o

    IL_0016:  ldloc      V_0

    IL_001a:  call       string a$PST06000001(string,

                                              int32)

    IL_001f:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)

    IL_0024:  pop

    IL_0025:  ldarg.0

    IL_0026:  call       instance void eval_a::b()

    IL_002b:  ret

 

    IL_002c:  ldstr      bytearray (F0 98 F2 9D F4 83 F6 96 F8 95 FA 92 FC 99 FE DF

                                    00 62 02 71 04 60 06 63 08 6C 0A 65 0C 79 0E 66   // .b.q.`.c.l.e.y.f

                                    10 70 12 7F 14 66 )                               // .p...f

    IL_0031:  ldloc      V_0

    IL_0035:  call       string a$PST06000001(string,

                                              int32)

    IL_003a:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)

    IL_003f:  pop

    IL_0040:  ldarg.0

    IL_0041:  call       instance void [System.Windows.Forms]System.Windows.Forms.Form::Close()

    IL_0046:  ret

  } // end of method eval_a::eval_a

 

Note: the “eval” prefix is because I’m using an evaluation version of the dotfuscator

 

As we can see all of the strings have been obfuscated, but still all the .net framework used classes and methods names still in plain text and readable to anyone and that is because you can obfuscate anything but the .net framework namespaces, classes and methods because if you obfuscated there names how you are going to call them on your user machine?

so again if I’m an experienced cracker and I know what I’m looking for I will be looking for the most rare .net framework methods and classes that have been called within this application, for example the MessageBox is a very good example also the Form::Close() is another good option so I would search for them in the new IL and again look up for a few lines searching for the branching statement till I find it at line IL_000f and again I will change it from brfalse to brtrue and build again my application using ilasm and when I run it I will get another access granted message and as you can see it took me only 5 minutes

 

but that because the application flow was so clear and it wasn’t obfuscated, right? So what if we obfuscate the application flow too using the “Control Flow obfuscation” feature in dotfuscator? The output IL is going to look like this

 

.method private hidebysig instance void

          eval_a(object A_0,

                 class [mscorlib]System.EventArgs A_1) cil managed

  {

    // Code size       81 (0x51)

    .maxstack  2

    .locals init (int32 V_0)

    IL_0000:  ldc.i4     0xa

    IL_0005:  stloc      V_0

    IL_0009:  ldarg.0

    IL_000a:  call       instance bool eval_a::eval_c()

    IL_000f:  brfalse.s  IL_0036

 

    IL_0011:  ldc.i4.1

    IL_0012:  br.s       IL_0017

 

    IL_0014:  ldc.i4.0

    IL_0015:  br.s       IL_0017

 

    IL_0017:  brfalse.s  IL_0019

 

    IL_0019:  br.s       IL_001b

 

    IL_001b:  ldstr      bytearray (57 39 59 39 5B 3F 5D 3B 5F 13 61 11 63 44 65 01   // W9Y9[?];_.a.cDe.

                                    67 1A 69 0B 6B 02 6D 1A 6F 15 71 16 )             // g.i.k.m.o.q.

    IL_0020:  ldloc      V_0

    IL_0024:  call       string a$PST06000001(string,

                                              int32)

    IL_0029:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)

    IL_002e:  pop

    IL_002f:  ldarg.0

    IL_0030:  call       instance void eval_a::b()

    IL_0035:  ret

 

    IL_0036:  ldstr      bytearray (57 31 59 34 5B 2A 5D 3F 5F 0C 61 0B 63 00 65 46   // W1Y4[*]?_.a.c.eF

                                    67 0B 69 18 6B 09 6D 0A 6F 15 71 1C 73 00 75 1F   // g.i.k.m.o.q.s.u.

                                    77 19 79 16 7B 0F )                               // w.y.{.

    IL_003b:  ldloc      V_0

    IL_003f:  call       string a$PST06000001(string,

                                              int32)

    IL_0044:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)

    IL_0049:  pop

    IL_004a:  ldarg.0

    IL_004b:  call       instance void [System.Windows.Forms]System.Windows.Forms.Form::Close()

    IL_0050:  ret

  } // end of method eval_a::eval_a

 

So as we so now there lots of branches but with the bare eye inspection all of them are pointing to other branches that also is pointing to another one till they reach the real branch also with bare eye inspection none of them have condition they just branch so it would be very easy to spot the real branch that we are seeking at line IL_000f and do the same again by changing the condition from false to true and build the application and again we will get the previous result.

 

But that because the code isn’t complex enough, what if we made the code a little bit more complex and used the previous way to obfuscate it?

So a code that looks like this

 

private void btnSubmit_Click(object sender, EventArgs e)

        {

 

            if (CheckConnection())

            {

                if (CheckDB())

                {

                    //we authenticate the user here using the method Authenticate()

                    if (Authenticate())

                    {

                        //if the user credential is valid then...

                        MessageBox.Show("access granted");

                        this.Run();

 

                    }

                    else

                    {

                        //else we kick him out

                        MessageBox.Show("invalid credentials");

                        this.Close();

                    }

                }

            }

        }

 

Would look like this after disassembling it

 

.method private hidebysig instance void

          eval_a(object A_0,

                 class [mscorlib]System.EventArgs A_1) cil managed

  {

    // Code size       81 (0x51)

    .maxstack  2

    .locals init (int32 V_0)

    IL_0000:  ldc.i4     0xa

    IL_0005:  stloc      V_0

    IL_0009:  ldarg.0

    IL_000a:  call       instance bool eval_a::eval_c()

    IL_000f:  brtrue.s  IL_0036

 

    IL_0011:  ldc.i4.1

    IL_0012:  br.s       IL_0017

 

    IL_0014:  ldc.i4.0

    IL_0015:  br.s       IL_0017

 

    IL_0017:  brfalse.s  IL_0019

 

    IL_0019:  br.s       IL_001b

 

    IL_001b:  ldstr      bytearray (57 39 59 39 5B 3F 5D 3B 5F 13 61 11 63 44 65 01   // W9Y9[?];_.a.cDe.

                                    67 1A 69 0B 6B 02 6D 1A 6F 15 71 16 )             // g.i.k.m.o.q.

    IL_0020:  ldloc      V_0

    IL_0024:  call       string a$PST06000001(string,

                                              int32)

    IL_0029:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)

    IL_002e:  pop

    IL_002f:  ldarg.0

    IL_0030:  call       instance void eval_a::b()

    IL_0035:  ret

 

    IL_0036:  ldstr      bytearray (57 31 59 34 5B 2A 5D 3F 5F 0C 61 0B 63 00 65 46   // W1Y4[*]?_.a.c.eF

                                    67 0B 69 18 6B 09 6D 0A 6F 15 71 1C 73 00 75 1F   // g.i.k.m.o.q.s.u.

                                    77 19 79 16 7B 0F )                               // w.y.{.

    IL_003b:  ldloc      V_0

    IL_003f:  call       string a$PST06000001(string,

                                              int32)

    IL_0044:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)

    IL_0049:  pop

    IL_004a:  ldarg.0

    IL_004b:  call       instance void [System.Windows.Forms]System.Windows.Forms.Form::Close()

    IL_0050:  ret

  } // end of method eval_a::eval_a

 

This time it’s harder to crack it but with bare eye inspection we can see there are only 2 conditioned branches at lines IL_000f and IL_0017 before where I found the Form::Close() method, so I can try my luck with them or I would just change the 1st one before where I found the Form::Close() method and the MessageBox::Show(string) method and build the application again and again I get another access granted message.

 

So what is the conclusion?

Well, obfuscation is a good way to protect our intellectual properties and it’s better than just leaving our confidential information in plain text, but as I’ve just demonstrated through this article we can’t rely on obfuscation to protect our applications as I’ve demonstrated how it’s easy to crack any application that is relying only on obfuscation for protection, and i did it without any special tools in only a few minutes.

 

Thanks for reading and I’m waiting for your comments and feedback

 

kick it on DotNetKicks.com

Add to Technorati Favorites

Published Sunday, August 12, 2007 12:24 AM by Fady

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Don’t rely on obfuscation

Great article.
You are right, but you missed something, obfuscation is not for preventing code from being altered by decompilation, but it is for making Understanding the logic harder, the easy solution for altering the code is using Strong Name Key, so the assembly gets a checksum and every other assembly that reference this assembly gets the checksum code, so if the assembly changed even one letter, the checksum value is changed and doesn't match the original and the application fails.
so we can say that Obfuscation + Strong name keys are the best solution.
http://visualbasic.about.com/od/usingvbnet/a/FWTools5.htm
http://msdn.microsoft.com/msdnmag/issues/06/07/CLRInsideOut/default.aspx
Sunday, August 12, 2007 12:49 AM by Mohammed Hossam

# re: Don’t rely on obfuscation

Dear Mohammed thanks for passing by and posting a comment
well, actually i was emphasizing on developers whom think that obfuscation is the ultimate solution for cracking i.e assembly alternation
but yes obfuscation does not protect assemblies from being altered but they still have a valid point of view as by making code logic harder by obfuscation it's harder to crack, so i was giving a proof of concept that yes obfuscation might make cracking an application more harder but not that hard ;)

so the solution you have suggested is in fact a good one specially when cobined with obfuscation instead of relying only on obfuscation although i still have concerns about strong naming being overridden specially in the case of stand alone application, anyway i think i need to do more investigation on this topic

Sunday, August 12, 2007 1:46 AM by Fady

# re: Don’t rely on obfuscation

Everything can be cracked, the question is how much does it cost to crack ? if the cost is higher than buying the application, so you did a good job, with every layer of security you are more secure but not completely secured
Sunday, August 12, 2007 1:47 AM by Mohammed Hossam

# re: Don’t rely on obfuscation

exactly, and this is the same for the developer or the software house, it's more like a trade off between time/money and security
but for sorry mostly the application always cost more than the time required to crack it
Sunday, August 12, 2007 1:57 AM by Fady

# re: Don’t rely on obfuscation

here is an article showing how it's possible to remove strong name
http://www.codeproject.com/dotnet/StrongNameRemove20.asp
Sunday, August 12, 2007 2:32 AM by Fady

# re: Don’t rely on obfuscation

Good post, but this is CTP version as I think, and there are versions for commercial use. I've a question why Microsoft didn't encrypt their .net libraries??!

1- A collections of managed and unmanaged code.
2- That's very hard to keep your developer (Attacker) track how your code run how to copy and edit (simply make it from scratch).

And if we imagine that I've the query processor (source code) for MS SQL Server 2008, it's mean I can develop RDMBS, in my opinion that's nothing else looking at great code made by great developers, and the most expensive part in any project documentation, diagrams (Architecture, Component, Class,.....) diagrams.
By those you can start your implementation.
Sunday, August 12, 2007 3:14 AM by Ramy Mahrous

# re: Don’t rely on obfuscation

@Ramy
Thanks man for commenting
btw i've tried bot the community edition and the evaluation one which have all of the available features in the commercial release
yes offcourse the code isn't every thing in a software project but that doesn't nullify the importance of code specially when altering it may cause unwanted effects like passing an authenticaion scheme or cracking a license protection scheme
Sunday, August 12, 2007 3:20 AM by Fady

# re: Don’t rely on obfuscation

Very nice article...
Shows how careless developers are when it is a matter of seurity.
Fady exposed a very bad security practice. All developer's should read this articles to see how vulnerable their code is.

More copmlicated designs like distributing the security code on several areas of the file , among different files , encrypting fields , using encrypted registry keys , salted hashes ..etc are much more better security practices that developers must learn .

Every thing is crackable , even by file monitors, registry monitors , process monitors. But always the quote "The more comlpicated the more secure"
Sunday, August 12, 2007 1:48 PM by Tamer Maher

# re: Don’t rely on obfuscation

@Tamer
Thanks man for commenting
you have said it all but the complication must not exeed a certin limit or the project would never see the sun :D
Sunday, August 12, 2007 4:46 PM by Fady

# re: Don’t rely on obfuscation

Really great article, thanks man
Monday, August 13, 2007 9:55 PM by Sameh

# re: Don’t rely on obfuscation

@Sameh
Thanks man
Tuesday, August 14, 2007 12:42 PM by Fady

# re: Don’t rely on obfuscation

Good article +1 kick from me.
I agree with Fady when he says: "it's more like a trade off between time/money and security". Also I think obfuscation doesn't protect from altering assemblies (as you showed in your article) but it (and especially control flow obfuscation) raises the bar for somebody who tries to understand how your software works and maybe even prevents that person from stealing / copying your way of solving a problem.
Thursday, August 16, 2007 3:56 PM by tobsen

# re: Don’t rely on obfuscation

@tobsen
Thanks man for the feed back and the kick :)
"Also I think obfuscation doesn't protect from altering assemblies"
i agree with you and that what i'm trying to prove
Thursday, August 16, 2007 4:49 PM by Fady

# Soci blog » Blog Archive » Obfuscation + strong name == felt??r??sbiztos program?

# F??bio Pedrosa » Don’t rely on Obfuscation

Thursday, August 16, 2007 7:36 PM by F??bio Pedrosa » Don’t rely on Obfuscation

# re: Don’t rely on obfuscation

Some obfuscation tools disable the ability to run ILDASM against their objfuscated assemblies: http://www.remotesoft.com/salamander/obfuscator.html
Thursday, August 16, 2007 10:48 PM by Kory

# re: Don’t rely on obfuscation

@Kory
i don't guess so
i've just downloaded there evaluation version and obfuscated my assembly and here is the output after i used ildasm without any problems or crashes

.method private hidebysig instance void
         A(object A_1,
           class [mscorlib]System.EventArgs A_2) cil managed
 {
   // Code size       60 (0x3c)
   .maxstack  8
   IL_0000:  ldarg.0
   IL_0001:  call       instance bool A.A::a()
   IL_0006:  brfalse.s  IL_003b

   IL_0008:  ldarg.0
   IL_0009:  call       instance bool A.A::B()
   IL_000e:  brfalse.s  IL_003b

   IL_0010:  ldarg.0
   IL_0011:  call       instance bool A.A::A()
   IL_0016:  brfalse.s  IL_002a

   IL_0018:  ldstr      "access granted"
   IL_001d:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)
   IL_0022:  pop
   IL_0023:  ldarg.0
   IL_0024:  call       instance void A.A::A()
   IL_0029:  ret

   IL_002a:  ldstr      "invalid credentials"
   IL_002f:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)
   IL_0034:  pop
   IL_0035:  ldarg.0
   IL_0036:  call       instance void [System.Windows.Forms]System.Windows.Forms.Form::Close()
   IL_003b:  ret
 } // end of method A::A
Friday, August 17, 2007 3:56 AM by Fady

# re: Don’t rely on obfuscation

Thanks Fady .. Really it was Useful.

Friday, August 17, 2007 6:19 PM by Mohamed Gamal El-Din

# re: Don’t rely on obfuscation

@Mohamed
thanks man
Friday, August 17, 2007 6:51 PM by Fady

# re: Don’t rely on obfuscation

Nice article, well done!
Sunday, August 19, 2007 5:21 PM by Steve Trefethen

# re: Don’t rely on obfuscation

@Steve
Thanks man
btw i like the idea of Falafel Software name :D
http://www.falafel.com/
specially because it's one of my favorite foods back here in Egypt :D
Monday, August 20, 2007 12:34 PM by Fady

# The most common software security mistakes

Through my humble experience with software development I’ve seen developers making fetal security mistakes...
Tuesday, September 04, 2007 1:09 AM by Infinite Loop

# re: Don’t rely on obfuscation

Please can anyone gave me this program (Reflector) or give me any link to download it   .

this web site bellow is not working i don't know why .
http://www.aisto.com/roeder/dotnet/

if there is other link please mail me on : iemad2@hotmail.com
Wednesday, September 19, 2007 1:30 PM by Emad

# re: Don’t rely on obfuscation

xST0mb  <a href="http://huidesjdsmma.com/">huidesjdsmma</a>, [url=http://vzblzxurbaeh.com/]vzblzxurbaeh[/url], [link=http://uiatubkukaul.com/]uiatubkukaul[/link], http://ofmnzvxhpoka.com/
Wednesday, May 21, 2008 1:03 AM by gvbzhiibow

# re: Don’t rely on obfuscation

1.txt;5;7
Saturday, August 02, 2008 11:53 AM by olEKTxEbPPSPE

# re: Don’t rely on obfuscation

1.txt;5;7
Saturday, August 02, 2008 2:00 PM by vaisuFAOwS

# re: Don’t rely on obfuscation

1.txt;5;7
Saturday, August 02, 2008 4:00 PM by VJyDCNrhLcCwFJ

# re: Don’t rely on obfuscation

1.txt;7;8
Saturday, August 16, 2008 7:11 PM by WGzKGOxG

# re: Don’t rely on obfuscation

1.txt;7;8
Saturday, August 16, 2008 11:05 PM by HyRDyrZFqWshxJOrEYG

# re: Don’t rely on obfuscation

1.txt;7;8
Sunday, August 17, 2008 3:03 AM by cQrRcSoABEWIODeDgL

# re: Don’t rely on obfuscation

comment5, http://laris12.blog.ca/ map,  >:-)),
Friday, August 22, 2008 7:56 PM by jonn1

# re: Don’t rely on obfuscation

comment4, http://laris12.blog.ca/ map,  lwi,
Saturday, August 23, 2008 12:29 AM by jonn1

# re: Don’t rely on obfuscation

birthday gift
Sunday, May 03, 2009 8:39 PM by temasparapian

# re: Don’t rely on obfuscation

best apartment design
Saturday, May 09, 2009 5:20 AM by pimpinnevaslipi

# re: Don’t rely on obfuscation

Hi, you have a great site! I also recommend this site: <a href="http://www.ulzka6drnn.com">hi5aw</a> , thanks!
Tuesday, May 19, 2009 5:33 PM by wk7v3 sqahe

# re: Don’t rely on obfuscation

Hi, you have a great site! I also recommend this site: [url=http://www.ulzka6drnn.com]hi5aw[/url] , thanks!
Tuesday, May 19, 2009 5:33 PM by mjecwq b2rqay

# re: Don’t rely on obfuscation

Hi, you have a great site! I also recommend this site: http://www.ulzka6drnn.com hi5aw , thanks!
Tuesday, May 19, 2009 5:33 PM by cu7gh nzhkl

What do you think?

(required) 
required 
(required)