Deobfuscating AutoIt scripts, part 2

kao

Almost 4 years ago, I wrote a blogpost about deobfuscating a simple AutoIt obfuscator. Today I have a new target which is using a custom obfuscator. ๐Ÿ™‚

Update: This obfuscator is called ObfuscatorSG and can be downloaded from Github. Thanks Bartosz Wรณjcik!

Author had a very specific request about the methods used to solve the crackme:

If I'm allowed to be picky, I'm primarily interested in scripted efforts to RegEx analyze strings/integers. Very little effort (as in none) went into hiding the correct string. The script was merely passed-through a self-made obfuscator.

In this article I'll show the things I tried, where and how I failed miserably and my final solution for this crackme. I really suggest that you download the crackme from tuts4you and try replicating each step along the way, that way it will be much easier to follow the article.

So, let's get started!
Read More

Solving “Find the flag” crackme by Extreme Coders

kao

Yesterday Extreme Coders posted a small crackme on Tuts4You. It's quite an easy one but solving it would require either lots of typing or some clever automation. Of course, being lazy I went for the automation route! ๐Ÿ™‚

Initial analysis

My preferred way is doing static analysis in IDA and - when necessary do dynamic analysis using OllyDbg. So, here is how it looks like in IDA:
extremecoders_cm1
As you can see, parts of code have been encrypted. 102 parts of code, to be exact. ๐Ÿ™‚

Decrypt the code

Since there is a lot of code that's encrypted, I need to automate decryption somehow. IDA scripting to the rescue!

auto ea;
auto addr;
auto size;
auto xorval;
auto x;
auto b;

ea = MinEA();
ea = FindBinary(ea, SEARCH_DOWN , "E8 00 00 00 00 5E 81 C6");
while (ea != BADADDR)
{
   addr = ea + 5 + 0x16;
   size = Dword(ea + 0xD);
   xorval = Byte(ea + 0x15);
   Message(form("Encrypted code parameters: start=%x size=%x key=%x\n", addr, size, xorval));
   for (x=0; x<size; x++)
   {
      b = Byte(addr + x);
      PatchByte(addr + x, b^xorval);
   }
   ea = FindBinary(ea +1, SEARCH_DOWN, "E8 00 00 00 00 5E 81 C6");
}

There's not much to comment. There's a big loop that's looking for the pattern of the decryption code. Then it extracts information about encrypted code address, size and used encryption key. Finally it decrypts the code.

Note - when you're patching binary data in IDA, it's always better to force IDA to reanalyze the affected fragment. I didn't do that here because changing end of _main() will force analysis automatically.

After decryption the code looks much better:
extremecoders_cm2

Well, it's better, but it still kinda sucks. We have 100 checks like this:

  movsx   edx, [esp+0B0h+enteredString+9]
  movsx   ecx, [esp+0B0h+enteredString+13h]
  add     ecx, edx
  movsx   edx, [esp+0B0h+enteredString+0Bh]
  lea     edx, [edx+ecx*2]
  movsx   ecx, [esp+0B0h+enteredString+1Dh]
  add     edx, ecx
  mov     ecx, dword ptr [esp+0B0h+enteredString]
  movsx   edi, ch
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+6]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+2]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+4]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+0Fh]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+0Ah]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+0Ch]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+11h]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+18h]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+0Eh]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+1Ah]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+17h]
  add     edx, edi
  movsx   edi, [esp+0B0h+enteredString+16h]
  movsx   ecx, cl
  add     edx, edi
  add     edx, ecx
  cmp     edx, 787h
  jnz     loc_40147B
  add     eax, 1    <--- increment number of passed checks
loc_40147B:

So, we're solving system of 100 linear equations with 32 variables. Great! Who wants to write down these equations based on disassembly and then solve them manually? Not me!

Decompile the code

Let's see if we can somehow make the problem easier for us. Hexrays decompiler provides nice output but it still needs a lot of cleanup:
extremecoders_cm3
Basically, the code responsible for encryption/decryption of checks is getting into our way.

Another IDA script to the rescue:

auto ea;
auto addr;
auto size;
auto xorval;
auto x;
auto b;

ea = MinEA();
ea = FindBinary(ea, SEARCH_DOWN , "60 E8 00 00 00 00 5E 81");
while (ea != BADADDR)
{
   for (x=0;x<0x1C;x++)
   {
      PatchByte(ea+x, 0x90);
   }
   MakeUnknown(ea, 0x1C, 0);
   MakeCode(ea);
   ea = FindBinary(ea +1, SEARCH_DOWN , "60 E8 00 00 00 00 5E 81");
}   

I took the previous script and modified it a bit. Now it finds both encryption and decryption loops and just nops them out. It also forces IDA to reanalyze the patched region - it's very important because otherwise IDA lost track of correct stack pointer and decompiled code was wrong.

Quick changes in Hexrays plugin options to use decimal radix and the decompiled code looks great!
extremecoders_cm4

Text editor magic

Beginning reversers commonly underestimate power of text editors. Sure, the Hexrays output we got is readable, but it's not really suitable for any sort of automated processing.

First, let's get rid of all extra spaces. Replace " " (2 spaces) with " " (one space). Repeat until no more matches. Now it looks like this:

 if ( enteredString[0]
 + enteredString[22]
 + enteredString[26]
 + enteredString[20]
 + enteredString[10]
 + enteredString[7]
 + 3 * enteredString[14]
 + 2
 * (enteredString[23]
 + enteredString[24]
 + enteredString[17]
 + enteredString[21]
 + enteredString[12]
 + enteredString[18]) == 2024 )
 v6 = 1;
 if ( enteredString[0]
 + enteredString[22]
 + enteredString[23]
... 98 more ifs...

Put each equation on one line. Replace "\r\n +" (new line,space,plus) with " +" (space,plus). Replace "\r\n *" (new line,space,star) with " *" (space,star).

 if ( enteredString[0] + enteredString[22] + enteredString[26] + enteredString[20] + enteredString[10] + enteredString[7] + 3 * enteredString[14] + 2 * (enteredString[23] + enteredString[24] + enteredString[17] + enteredString[21] + enteredString[12] + enteredString[18]) == 2024 )
 v6 = 1;
 if ( enteredString[0] + enteredString[22] + enteredString[23] + enteredString[26] + enteredString[14] + enteredString[24] + enteredString[17] + enteredString[12] + enteredString[10] + enteredString[15] + enteredString[4] + enteredString[2] + enteredString[6] + enteredString[1] + enteredString[29] + enteredString[11] + 2 * (enteredString[9] + enteredString[19]) == 1927 ) ++v6;
 if ( enteredString[14] + enteredString[10] == 148 ) ++v6;
 if ( enteredString[0] + enteredString[14] + enteredString[10] + enteredString[11] + enteredString[3] + enteredString[25] + 2 * (enteredString[22] + enteredString[27]) == 741 ) ++v6;
 if ( enteredString[24] + enteredString[29] == 229 ) ++v6;
... 95 more ifs ...

Get rid of those "if". Get rid of "++v6;". Replace "==" with "=".

enteredString[0] + enteredString[22] + enteredString[26] + enteredString[20] + enteredString[10] + enteredString[7] + 3 * enteredString[14] + 2 * (enteredString[23] + enteredString[24] + enteredString[17] + enteredString[21] + enteredString[12] + enteredString[18]) = 2024 )
enteredString[0] + enteredString[22] + enteredString[23] + enteredString[26] + enteredString[14] + enteredString[24] + enteredString[17] + enteredString[12] + enteredString[10] + enteredString[15] + enteredString[4] + enteredString[2] + enteredString[6] + enteredString[1] + enteredString[29] + enteredString[11] + 2 * (enteredString[9] + enteredString[19]) = 1927
enteredString[14] + enteredString[10] = 148
enteredString[0] + enteredString[14] + enteredString[10] + enteredString[11] + enteredString[3] + enteredString[25] + 2 * (enteredString[22] + enteredString[27]) = 741
enteredString[24] + enteredString[29] = 229
... 95 more equations ...

Finally, rename "enteredString" to "z" and get rid of those "[" and "]"

z0 + z22 + z26 + z20 + z10 + z7 + 3 * z14 + 2 * (z23 + z24 + z17 + z21 + z12 + z18) = 2024 )
z0 + z22 + z23 + z26 + z14 + z24 + z17 + z12 + z10 + z15 + z4 + z2 + z6 + z1 + z29 + z11 + 2 * (z9 + z19) = 1927
z14 + z10 = 148
z0 + z14 + z10 + z11 + z3 + z25 + 2 * (z22 + z27) = 741
z24 + z29 = 229
... 95 more equations ...

Congratulations, within one minute you got from ugly decompiled code to well-written system of equations!

And solve the problem

Nicely written system of equations is pointless, if you can't solve it. Luckily, there's an online solver for that right there! ๐Ÿ˜‰ Copy-pasting our cleaned system of equations into their webform gives us result:

This system has a unique solution, which is

{ z0 = 102, z1 = 108, z10 = 48, z11 = 108, z12 = 118, z13 = 101, z14 = 100, z15 = 95, z16 = 116, z17 = 104, z18 = 97, z19 = 116, z2 = 97, z20 = 95, z21 = 114, z22 = 49, z23 = 103, z24 = 104, z25 = 116, z26 = 33, z27 = 33, z28 = 33, z29 = 125, z3 = 103, z4 = 123, z5 = 89, z6 = 48, z7 = 117, z8 = 95, z9 = 115 }.

Converting character codes to ANSI string is an equally simple exercice, I'm not gonna bore you with that.

And that's how you solve a crackme with nothing but a few scripts in IDA and a text editor.. ๐Ÿ˜‰

Sniffing correct serial in .NET crackmes

kao

Introduction

In this tutorial I'll show you a generic way how to break most of the crackmes written in VB.NET. It uses the fact that most crackmes made by beginners will calculate correct serial and do a simple comparison "if enteredSerial = correctSerial then"...

To break such a crackme, you only need to find this comparison and sniff the correct serial. This is a very common approach in x86 world but in .NET world it's not that popular yet.

As for my target, I'm using "RDG Simple Crackme .NET v4 2015"

GetProcAddress in .NET

In x86 world you can use GetProcAddress function to get address of any API function from any DLL. Can we do something similar in managed environment like .NET? It turns out that we can, but it's a little bit harder.

So, for example, to get address of Assembly.Load(byte[]) you need to do:

MethodBase mb = typeof(Assembly).GetMethod("Load", new Type[] { typeof(byte[]) });
IntPtr handle = mb.MethodHandle.GetFunctionPointer();
Console.WriteLine("Assembly.Load() = {0:X}", handle.ToInt32());

This works well with static classes and static methods. How about non-static methods like RijndaelManaged.CreateDecryptor(byte[], byte[])?

That's doable as well, like this:

RijndaelManaged rijndael = new RijndaelManaged();
mb = rijndael.GetType().GetMethod("CreateDecryptor", new Type[] { typeof(byte[]), typeof(byte[]) });
handle = mb.MethodHandle.GetFunctionPointer();
Console.WriteLine("RijndaelManaged.CreateDecryptor() = {0:X}", handle.ToInt32());

To make this reference almost complete - here's how to get address of .ctor:

ConstructorInfo ctor = typeof(MyClass).GetConstructor(Type.EmptyTypes);
IntPtr ctorPtr = ctor.MethodHandle.GetFunctionPointer();
Console.WriteLine("MyClass constructor = {0:X}", ctorPtr.ToInt32());

There are a few gotchas, however..

  • In case your target type is located in assembly that's not NGEN'ed yet, I suggest that you use ngen and install the assembly in cache. That can prevent certain problems later.
  • Addresses of functions are obviously different in .NET 2.0 and 4.0. You must compile for correct framework version and target the correct .NET assembly.
  • Addresses of functions are different for x86 and x64 framework versions, too. Make sure your assembly is compiled correctly.

Sniffing string compare

Suprisingly, string comparison in VisualBasic.NET and other .NET languages is different. It's caused by Option Compare statement present in Visual Basic language. So, if the crackme is made in VB.NET, you need to examine Operators.CompareString(string,string,bool) function. For crackmes made in other languages, you'll need to examine string.Equals(string) or some other variation of this method.

So, using the code I mentioned above, I learned that address of Operators.CompareString(string,string,bool) on my PC is 599F1D30. Now I need to sniff data passed to this function.

There are several possible approaches. You can try using VisualStudio & Reflector plugin as SpoonStudio tried, you can try using ILSpy and it's debugger plugin, or you can inject DLL into crackme process, as suggested by noth!ng - but I prefer to use OllyDbg.

Load crackme in OllyDbg, make sure that all the anti-anti-debug plugins are working, all the exceptions ignored, put a breakpoint on 599F1D30 and hope for the best.

Nope. Operators.CompareString is called literally thousands of times. So, we need to do something smarter.

For example, we can use conditional logging breakpoints in Olly. Those breakpoints are quite slow, but it's still faster than to write some sort of hooking DLL and inject it into crackme. So, we need to set 2 logging breakpoints - one for each string compared. Here is first one:
crackme_conditional_breakpoint
Place second breakpoint at the next instruction (59CD1D31) and log string at edx+8.

Run the crackme, enter some fake but easily recognizable serial and few minutes later we have the answer:
crackme_logged_results
My entered serial was "1234567890123456789012345678901234567890" and it's being compared to "C49476D583364356253377056314435396D456F44796C7A55746431564433544". Hmm, could that be the correct serial for my nickname? ๐Ÿ˜‰ Yes, it is!

Final notes

This was quite nice crackme and I only showed the simplest way to beat it. When you start looking into it, you'll find some nice anti-debug tricks, some nice anti-patching tricks and pretty nicely obfuscated code.

But that's a matter for another story. Have fun!