Hacking De4dot for Fun

Share Embed Donate


Short Description

Descripción: Changing the way De4dot works....

Description

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 don’t know how to use it or can’t 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 isn’t;

For our purposes we’ll 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, it’s 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 can’t 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.

Let’s 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 didn’t, your Visual Studio or de4dot project is not set up properly – use Google and figure it out. If you can’t 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.

Let’s find out where resourceDecrypterType is assigned in CO-good.exe:

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

Look at the code, it’s 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 it’s 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.

Let’s fix de4dot code. If you don’t 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, you’ll 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 You’ll notice that in CO-good.exe you’ll 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 you’ll 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 you’ll see that problem is in getDecrypterMethods(), which doesn’t return anything for CO-bad.exe 15.

Examine how getDecrypterMethods() works. It’s looking for specific methods in resourceDecrypterType class. Let’s 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”. Let’s add this new trick to getDecrypterMethods: static IEnumerable 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 you’ll see that CO-bad.exe is unpacked normally. Achievement unlocked! :-) That’s 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.

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF