Anda di halaman 1dari 4

Hacking de4dot for fun.

This is just a small introductory tutorial; it should be enough to get you started with fixing de4dot. It will not teach you how to analyze protections from scratch, or how to implement brand new features in de4dot that is a completely different subject. I chose to use CryptoObfuscator as an example because they usually make very small changes just enough to break public de4dot and nothing else. I will not waste time or space explaining basics of using Visual Studio. If you dont know how to use it or cant write any C# code please stop right now and come back later when you have learned that.

Requirements.
Visual Studio 2010. You will probably need the full version, not the Express; Some C# knowledge. The more you know, the easier it will be for you; Good .NET decompiler. Reflector works, so does ILSpy or justDecompile. None of them is perfect, so use whatever you like. 2 files packed with CryptoObfuscator one that is supported by de4dot, one that isnt;

For our purposes well use 2 versions of CryptoObfuscator: Last version of CO which is supported by de4dot 2.0.3 is "CryptoObfuscator v2012 R2 build 130111". You can get it here: http://www.multiupload.nl/WETXYQWH13 First version of CO which is NOT supported by de4dot 2.0.3 is "CryptoObfuscator v2012 R2 build 130114". You can get it here: http://www.multiupload.nl/GZD8NY91XB You could use any 2 executables protected with different versions of CryptoObfuscator but as you will see later, its much easier when you have same executable protected with 2 different versions of protector.

Let's start hacking.


1. Get de4dot source and make sure you can compile it and run it under Visual Studio debugger. Read the tutorial by 0xd4d: https://bitbucket.org/0xd4d/de4dot/wiki/Compiling%20de4dot If you cant make it work, this tutorial is not for you. Come back later when you have learned basics of programming. 2. 3. 4. Install both versions of CryptoObfuscator and copy both cryptoobfuscator.exe to some folders. Rename them to CO-good.exe and CO-bad.exe accordingly. Try to unpack CO-good.exe de4dot will work properly. Try to unpack CO-bad.exe de4dot will fail with the infamous "WARNING: Found unknown resource encryption flags: 0xF5". Find the message "Found unknown resource encryption flags". It's in the file de4dot.code\deobfuscators\CryptoObfuscator\ResourceDecrypter.cs", line 333 Open ResourceDecrypter.cs and study the design of resource decrypter. There are several interesting class variables

TypeDef resourceDecrypterType; byte[] buffer1 = new byte[BUFLEN]; byte[] buffer2 = new byte[BUFLEN]; byte desEncryptedFlag; byte deflatedFlag; byte bitwiseNotEncryptedFlag; FrameworkType frameworkType; bool flipFlagsBits; int skipBytes;

5.

Lets try running de4dot with both of our targets and see what the differences are. Set up command line arguments in Visual Studio:

6.

Put breakpoint on the line before the error message:

byte allFlags = (byte)(desEncryptedFlag | deflatedFlag | bitwiseNotEncryptedFlag); if ((flags & ~allFlags) != 0) Logger.w("Found unknown resource encryption flags: 0x{0:X2}", flags);

7.

Run de4dot under the debugger. Visual Studio should stop at the breakpoint. If it didnt, your Visual Studio or de4dot project is not set up properly use Google and figure it out. If you cant make it work, this tutorial is not for you. Come back later when you have learned basics of Visual Studio and C# programming.

8.

Examine the global variables. For a CO-good.exe we have:

9.

Repeat steps 7-10 for CO-bad.exe and see the global variables there:

OK, we have located the problem resourceDecrypterType is null in co-bad.exe. Also, skipBytes, flipFlagsBits and the flag values are wrong. 10. Lets find out where resourceDecrypterType is assigned in CO-good.exe:

There are 3 places where its assigned, lines 144, 169, 94. Ideally, you should fix all 3 procedures (for .NET1.0, .NET2.0+ and for Silverlight). But for this tutorial, well focus only on our executable. Lets put breakpoint on all three of them and try unpacking CO-good.exe. Breakpoint on line 94 will be triggered.

Look at the code, its searching for the class which has 5 fields, and those fields are of the special type (requiredTypes). We need to find name and token of the correct resource decrypter type, so use Visual Studio and its great UI. Class name is cff37ae02e27a0ea65f54ffc586d28228 and token is 0x0200001B. For some protections, name will be unreadable but token will always be correct. So, better learn to use tokens! 11. Find the class cff37ae02e27a0ea65f54ffc586d28228 in the decompiler.

[SecuritySafeCritical] internal sealed class cff37ae02e27a0ea65f54ffc586d28228 { // Fields private static readonly int c2289bb0b67fcac3ebe486ea02edb1e03; private static readonly object c4b1c56a15c6b9178ffa059dd91318507; private static readonly int c62d592d1205bb58fbb7e5ca7be4d7725; private static readonly MemoryStream c79d902cc2f2433e73ffd8e83032ebe4e; private static readonly MemoryStream cb970ada58c0d250bc99a32710cd9e92e;

Now find the same class in the CO-bad.exe. In this case we can use class name, so its easy. For other protections when you cannot use names, it takes some time and effort to locate correct class. There is no simple way, just trial and error. If you are using 2 totally different executables, this step will be the hardest. Therefore I strongly suggest that you use simple unpackmes (or the protectors themselves) for the research.

[SecuritySafeCritical] internal sealed class cff37ae02e27a0ea65f54ffc586d28228 { // Fields private static readonly int c2289bb0b67fcac3ebe486ea02edb1e03; private static readonly object c4b1c56a15c6b9178ffa059dd91318507; private static readonly byte c5ad5d623496e618ee3659a29b89a45d6; private static readonly int c62d592d1205bb58fbb7e5ca7be4d7725; private static readonly MemoryStream c79d902cc2f2433e73ffd8e83032ebe4e; private static readonly MemoryStream cb970ada58c0d250bc99a32710cd9e92e;

As you can see, in CO-bad.exe this class has 6 fields, not 5. Sneaky developers added unused field with type byte and it was enough to break de4dot! 12. Lets fix de4dot code. If you dont care about backwards compatibility, just rewrite the code to something like this:

static string[] requiredTypes = new string[] { "System.IO.MemoryStream", "System.Object", "System.Int32", "System.Byte", }; bool findDesktopOrCompactFramework() { resourceDecrypterType = null; foreach (var type in module.Types) { if (type.Fields.Count != 6) continue; if (!new FieldTypes(type).exactly(requiredTypes)) continue; var cctor = type.FindStaticConstructor(); if (cctor == null) continue;

This is a quick and dirty way and will break support for old CO versions; I suggest that you avoid it whenever possible! Proper fix will require a small rewrite and few extra lines of code:
static string[] requiredTypes = new string[] { "System.IO.MemoryStream", "System.Object", "System.Int32", }; static string[] requiredTypes2 = new string[] { "System.IO.MemoryStream", "System.Object", "System.Int32", "System.Byte", }; bool findDesktopOrCompactFramework() { resourceDecrypterType = null; foreach (var type in module.Types) { bool found = false; if ((type.Fields.Count == 5) && (new FieldTypes(type).exactly(requiredTypes))) found = true; if ((type.Fields.Count == 6) && (new FieldTypes(type).exactly(requiredTypes2))) found = true; if (!found) continue; var cctor = type.FindStaticConstructor(); if (cctor == null) continue;

13.

Try to unpack CO-bad.exe. You will still get the same error. But if you examine variables, youll see some progress:

resourceDecrypterType is correct now, but skipBytes and flags are wrong. 14. Using the same method as before, find all places where desEncryptedFlag is assigned. There are 4 of them. Put breakpoints on all of them and try to debug both CO-good.exe and CO-bad.exe Youll notice that in CO-good.exe youll break here:
if (constants.Count == 2) { desEncryptedFlag = (byte)constants[0]; deflatedFlag = (byte)constants[1]; return true; }

and call stack looks like this:

In CO-bad.exe youll break here:


void initializeHeaderInfo(ISimpleDeobfuscator simpleDeobfuscator) { skipBytes = 0; foreach (var method in getDecrypterMethods(resourceDecrypterType)) { if (updateFlags(method, simpleDeobfuscator)) return; } desEncryptedFlag = 1; deflatedFlag = 2; bitwiseNotEncryptedFlag = 4; }

and call stack will be different:

Debug the function initializeHeaderInfo() and youll see that problem is in getDecrypterMethods(), which doesnt return anything for CO-bad.exe 15. Examine how getDecrypterMethods() works. Its looking for specific methods in resourceDecrypterType class. Lets go back to decompiler and compare the methods of the class: In CO-good.exe:
static cff37ae02e27a0ea65f54ffc586d28228(); public cff37ae02e27a0ea65f54ffc586d28228(); internal static byte[] c5162ad2ffec729d3663e54f00333ff9f(Stream); private static string c8759c5becc4b90299ea32d127ec93830(Assembly); internal static byte[] c92a4a0ebc6854cdb7fb16ae50f50a727(sbyte, Stream); private static byte[] cd21099857b8d0e6111ea6ad3a793fef2(Assembly); internal static byte[] cdd10834bd099c5713d8462278a7aa315(sbyte, Stream);

In CO-bad.exe
static cff37ae02e27a0ea65f54ffc586d28228(); public cff37ae02e27a0ea65f54ffc586d28228(); internal static byte[] c5162ad2ffec729d3663e54f00333ff9f(Stream); private static string c8759c5becc4b90299ea32d127ec93830(Assembly); internal static byte[] c92a4a0ebc6854cdb7fb16ae50f50a727(long, Stream); private static byte[] cd21099857b8d0e6111ea6ad3a793fef2(Assembly); internal static byte[] cdd10834bd099c5713d8462278a7aa315(long, Stream);

OK, now they are using long (Int64), instead of sbyte. Lets add this new trick to getDecrypterMethods:
static IEnumerable<MethodDef> getDecrypterMethods(TypeDef type) { if (type == null) yield break; foreach (var method in type.Methods) { if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.IO.Stream)")) yield return method; else if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.Int64,System.IO.Stream)")) yield return method; else if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.Int32,System.IO.Stream)")) yield return method;

16.

Compile, run, examine global variables:

Everything looks correct. Continue the execution and youll see that CO-bad.exe is unpacked normally. Achievement unlocked! :-) Thats the end of the tutorial. I quickly showed you how to debug problems with unsupported versions of protectors, what to look for and what to compare in decompiler. I hope this will help you to start hacking de4dot and fixing issues with new versions of protectors. Have fun! kao.