Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / New Users / April 2006

Tip: Looking for answers? Try searching our database.

Control flow obfuscation.

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
q23r - 05 Apr 2006 17:55 GMT
Have you seen real control flow obfuscation(CFO)?
I mean not in advertising material but in work?

I suppose there are three main obfuscators, which supports CFO.
1. Xenocode
2. Dotfuscator
3. Demeanor

1. When you choose max CFO, produces a lot of trash classes, but don't
change flow of       your code.
2. Trial version. All settings are default.
   I can't see any CFO effect.
3. Trial version doesn't support CFO.
    All I have is the code sample from here:
http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/browse_t
hread/thread/53dcada5c3cdf7e3/d24076125868738a?lnk=st&q=control+flow+obfuscation
&rnum=1#d24076125868738a


Please, correct me, if I'm wrong.
And tell me may be there are another ones, which supports CFO?
Ben Callister - 05 Apr 2006 21:33 GMT
i use Xenocode and it works pretty good. once CFO has been applied, if you
load your assembly into a reflection tool (i use Reflector), and you look at
your various function bodies, you will see that they have indeed been
replaced with alot more simple-instructions and labels that make it very
difficult to reverse engineer. one thing that i have complained to them
already about is that there is currently no GUI for applying CFO to
*particular* function bodies. rather, it just defaults to all function
bodies within your assembly. you can do this, but you have to drop down into
the project file yourself which is a pain.

fyi... i typically use their 'conservative' filter so that i can keep my
public interfaces in tact since i share my components.

one thing to note is that your assembly size will grow substantially, which
is an indicator that CFO has indeed been apllied. i have been seeing about a
5x increase in size after CFO has been applied - which sucks, but i guess
thats what you get for buying into this managed world if you want to protect
yourself somewhat.

unfortunately, i cant speak for the other obfuscator utilities as i have not
used them.

hope this helps somewhat.
ben

> Have you seen real control flow obfuscation(CFO)?
> I mean not in advertising material but in work?
[quoted text clipped - 14 lines]
> Please, correct me, if I'm wrong.
> And tell me may be there are another ones, which supports CFO?
William Stacey [MVP] - 05 Apr 2006 21:43 GMT
I like Xenocode as well.  However, in no way is 5x worth it IMO.  I would
just obfuscate and call it a day.

Signature

William Stacey [MVP]

q23r - 06 Apr 2006 17:28 GMT
Example of xenocode work(trial version 3.1.6 build 2012):
CFO is max.

before obfuscation:

   [STAThread]
   static void Main(string[] args)
   {
     System.Console.WriteLine("Please, enter number.");
     int number = Convert.ToInt32(System.Console.ReadLine());
     System.Console.WriteLine(String.Format("Factorial of {0} is {1}",
number, Calculate(number)));
     System.Console.WriteLine(String.Format("Squared {0} is {1}",
number, GetSquared(number)));
     double d = 53.0;
     System.Console.WriteLine(String.Format("Squared {0} is {1}", d,
GetSquared(d)));
     System.Console.WriteLine(String.Format("Square root of  {0} is
{1}", d, GetSquareRoot(d)));
   }

   private static int Calculate(int number)
   {
     if (number == 1)
         return 1;
     return number * Calculate(number - 1);
   }

   private static int GetSquared(int x)
   {
     return x*x;
   }

   private static double GetSquared(double x)
   {
     return x*x;
   }

   private static double GetSquareRoot(double x)
   {
     return Math.Sqrt(x);
   }
 }

after(decompiled by Salamander, RemoteSoft said that main method is
obfuscated):

       [STAThreadAttribute()]
       private static void xc447809891322395(string[]
xce8d8c7e3c2c2426)
       {
           int i;

           double d;

           Console.WriteLine("Please, enter number.");
           i = Convert.ToInt32(Console.ReadLine());
           ConstantExp: "Factorial of {0} is {1}"
           VariableExp: i
           ConstantExp: "Square root of  {0} is {1}"
           VariableExp: d
     IL_0054:  box        [mscorlib]System.Int32
           BoxingExp: xb1de1ba20faeeff8(i)
     IL_0064:  call       string
[mscorlib]System.String::Format(string, object, object)
     IL_0069:  call       void
[mscorlib]System.Console::WriteLine(string)
           Console.WriteLine(String.Format("Squared {0} is {1}", i,
x68479d4d6a6e9138(i)));
           d = 53.0;
           Console.WriteLine(String.Format("Squared {0} is {1}", d,
x68479d4d6a6e9138(d)));
     IL_008b:  box        [mscorlib]System.Double
           BoxingExp: x51c832559b7f7cb7(d)
     IL_009b:  call       string
[mscorlib]System.String::Format(string, object, object)
     IL_00a0:  call       void
[mscorlib]System.Console::WriteLine(string)
       }

       private static int xb1de1ba20faeeff8(int x78b0a0bc28459535)
       {
           if (x78b0a0bc28459535 == 1)
           {
               return 1;
           }
           else
           {
               return x78b0a0bc28459535 *
xb1de1ba20faeeff8(x78b0a0bc28459535 - 1);
           }
       }

       private static int x68479d4d6a6e9138(int x08db3aeabb253cb1)
       {
           return x08db3aeabb253cb1 * x08db3aeabb253cb1;
       }

       private static double x68479d4d6a6e9138(double
x08db3aeabb253cb1)
       {
           return x08db3aeabb253cb1 * x08db3aeabb253cb1;
       }

       private static double x51c832559b7f7cb7(double
x08db3aeabb253cb1)
       {
           return Math.Sqrt(x08db3aeabb253cb1);
       }
   }

Is there any CFO? I think no. The trash in the main method appears
because of xenocode damaged some metadata.

May be it is also restriction of trial version? If so why settings of
CFO is available?
Huihong - 06 Apr 2006 20:46 GMT
This kind of control flow obfuscation is not difficult to defeat. I
just didn't bother to add the support into our salamander decompiler.
Internally, I have a version that can easily remove those extra jump
instructions injected by obfuscators, and yield high quality source
code.

What CFO does is to insert extra br instructions to mess up stack
evaluation. Usually, decompiler assumes that a stack starts with empty,
and becomes empty during a straight excution path, e.g., path
immediately follow an if...else.. node. CFO uses jump instruction to
make this simple stack evaluation a bit more difficult, but still easy
to remove. I may release a version that defeats this kind of
obfuscation.

Be aware, extra jumps introduce more branches, and thus will slow down
the performance.

I still think if you use obfuscators, renaming is the key feature.
Otherwise, try our native compiler, it provides THE best protection
against reverse engineering by compiling everying into x86 code, and it
runs without .NET Framework, no IL code, no JIT compiling on execution
time.

Huihong
Remotesoft, Inc.
http://www.remotesoft.com

> Example of xenocode work(trial version 3.1.6 build 2012):
> CFO is max.
[quoted text clipped - 112 lines]
> May be it is also restriction of trial version? If so why settings of
> CFO is available?
q23r - 07 Apr 2006 16:21 GMT
> What CFO does is to insert extra br instructions to mess up stack
> evaluation.
How do you know kind of cfo in xenocode?

> I still think if you use obfuscators, renaming is the key feature.
I think know.
But it's easy to implement.

I want obfuscator which results can't be compiled code after cycle of
obfuscation, decompilation.
Is it possible to change some methadata(or smth else) so, that binaries
are correct to execute, but when decompiled, can't be compiled again
without human interference?

> Otherwise, try our native compiler, it provides THE best protection
> against reverse engineering by compiling everying into x86 code, and it
> runs without .NET Framework, no IL code, no JIT compiling on execution
> time.
If I want native code which runs without .net, I should have been using
c++ )
Huihong - 07 Apr 2006 16:57 GMT
No, we don't check whether the code is CFO-ed, rather than, perform
some optimization before stack evaluation for the decompiler. In your
case, the Main() method is failed to decompile, because some nodes with
branches are inserted, which can be completely removed. In other words,
CFO-enabled obfuscators make a straight execution path to become a
conditional path, I'll give you an example,

Before:

int sum = 0;
sum = x + y;

After CFO: (for illustration only, not exact, should use IL
instructions to accurate description)

int sum = 0 ;
push sum
if (sum ! = 0) goto L1; <=== extra node with branched
pop x;                  <== this exectution path's stack does not start
with empty
L1:
sum = x + y;

Pretty much, this is the techniques CFO uses. It relies on branches to
mess up the decompilers, but as you can see, a bit more advanced stack
evaluation will undo the work.

As for native compilation vs programming with native C++, I think it's
totally different concept. Native compilation means you still use .NET
programming languages, such as C# and VB.NET, so you still can take
advantage of the productivity of these wonderful languages, and only if
you are really serious about reverse engineering, you have an option to
compile into native code. Garbage collection is still in place.

On the other hand, if you program in C++, you don't gain productivity,
so it's totally different stories.

> > What CFO does is to insert extra br instructions to mess up stack
> > evaluation.
[quoted text clipped - 16 lines]
> If I want native code which runs without .net, I should have been using
> c++ )
q23r - 08 Apr 2006 13:04 GMT
I assume it is just very simple example of CFO or most obfuscators
really use the same technics?

Do you know obfuscators which use more complex CFO strategy? You know
there are plenty of theory about it, but i didn't see it in practice...
Huihong - 09 Apr 2006 00:20 GMT
What I can say is that decompiler always wins in these kinds of
battles. There are some variations of CFO, but the principle is same,
trying to mess up decompiler's control analysis by inserting extra
nodes with in/out edges.

What protection measure to take really depends on your comfort level.
If you think "well, I better do something", then obfuscators are fine.
If you think "God, I really need to protect my code", then try native
compilation. I wrote my salamander decompiler in java languages, before
release, I tried obfuscation, but didn't meet my goals, so I converted
java source code to C++ (for this I developed our java to C++ Octopus
translator), and recompiled it to native executables. Today, I still
develop in Java for the decompiler, but generate native code for
release.

This will be true for C#, with our native compiler:

http://ww.remotesoft.com/linker

Huihong

> I assume it is just very simple example of CFO or most obfuscators
> really use the same technics?
>
> Do you know obfuscators which use more complex CFO strategy? You know
> there are plenty of theory about it, but i didn't see it in practice...
Huihong - 07 Apr 2006 16:57 GMT
No, we don't check whether the code is CFO-ed, rather than, perform
some optimization before stack evaluation for the decompiler. In your
case, the Main() method is failed to decompile, because some nodes with
branches are inserted, which can be completely removed. In other words,
CFO-enabled obfuscators make a straight execution path to become a
conditional path, I'll give you an example,

Before:

int sum = 0;
sum = x + y;

After CFO: (for illustration only, not exact, should use IL
instructions to accurate description)

int sum = 0 ;
push sum
if (sum ! = 0) goto L1; <=== extra node with branched
pop x;                  <== this exectution path's stack does not start
with empty
L1:
sum = x + y;

Pretty much, this is the techniques CFO uses. It relies on branches to
mess up the decompilers, but as you can see, a bit more advanced stack
evaluation will undo the work.

As for native compilation vs programming with native C++, I think it's
totally different concept. Native compilation means you still use .NET
programming languages, such as C# and VB.NET, so you still can take
advantage of the productivity of these wonderful languages, and only if
you are really serious about reverse engineering, you have an option to
compile into native code. Garbage collection is still in place.

On the other hand, if you program in C++, you don't gain productivity,
so it's totally different stories.

> > What CFO does is to insert extra br instructions to mess up stack
> > evaluation.
[quoted text clipped - 16 lines]
> If I want native code which runs without .net, I should have been using
> c++ )

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.