[Essay01 - Coffee? Naw, gimme TEA. [yAtEs] crackme1 unwrapped by f0dder]

Download essay, bruteforcer source (et cetera) here: f-yates1.zip.

Looking a bit a [yAtEs] crackme number 1. That's a bit of an executable size -- 581k. Anyway. The code is pretty straightforward, and no work has been done to hide what is going on. However, you need to do quite an amount of work to reach your goal, and this crackme is definitely not for the untrained or impatient cracker. I'll rate this 6/10, a bit close to a 5 since it's so straightforward (well, hardly no obfuscation). It was a joy reversing this crackme, [yAtEs] :).

Looking at the section headers, we notice that there's the usual sections, and a last section called "yates". I assume something has been done to the main app, which an extra section (and a redirected entrypoint) is going to rectify -- if you have a correct key, at least :).

Another interesting thing is the "unpacked with procdump32" string in the header section...

Looking through the executable, we see that it's all full of garbage until the spot with a "runtime error" string (delphi?). The imports seem to be intact, at least there's a few more than you see in typical compressed / encrypted apps. Resources seem to have been left intact, you can verify this by loading _crackme.exe into a resource editor (resource hacker, for instance. Tell me if you know of a better resource editor).

Perusing the resource section, it really looks like some delphi or BCB crap. "TLabel", "TCaption" etc. Blah :).

The added section looks clean in a hexeditor. Easy identifiable strings. "technics.dat" followed by "CORRECT KEY" - could this be the keyfile name? Run filemon, run the crackme. Well, at least it tries opening technics.dat, and that's the only file access the crackme does. Fine. Let's construct a rubbish keyfile, and have a look at a disassembly. I chose the following contents (ok, so the keyfile contents weren't RUBBISH, but probably not a good keyfile for crackme1 ;-). Yup, I created the keyfile before looking at the disassembly, just because I was in that mood.

appendix 1 - technics.dat 00000000 4F6E6520 52696E67 20746F20 72756C65 One Ring to rule 00000010 20746865 6D20616C 6C2C204F 6E652052 them all, One R 00000020 696E6720 746F2066 696E6420 7468656D ing to find them 00000030 2C0D0A4F 6E652052 696E6720 746F2062 ,..One Ring to b 00000040 72696E67 20746865 6D20616C 6C20616E ring them all an 00000050 6420696E 20746865 20646172 6B6E6573 d in the darknes 00000060 73206269 6E642074 68656D0D 0A496E20 s bind them..In 00000070 74686520 4C616E64 206F6620 4D6F7264 the Land of Mord 00000080 6F722077 68657265 20746865 20536861 or where the Sha 00000090 646F7773 206C6965 2E dows lie.

When disassembling the file, we can see that my assumption about redirected entrypoint was correct. Using IDA is a bit overkill, because of the encrypted (read on) code... I mean, IDA (of course) chokes on all the encrypted code, so using a faster disassembler would have been just as fine.

The code is quite simple. I have never been a fan of long dead listings, so you will not see any unless I find them important. The keyfile is opened OPEN_EXISTING and GENERIC_READ - quite a logical combination. Error checking (couldn't open file) is done. Note that there's a bit of queerness right after the call to ReadFile; [yAtEs] gets the value of numberOfBytesRead into eax, but does nothing with it before (next instruction) he loads in a DWORD that was read from the file - the second DWORD. After a little register/variable setup, a CALL is done...

Glancing quickly at the code snippet (which we can rightly call a function), I immediately saw what was going on -- bitwise shifts, xor, adds, and the magic number 0x9E3779B9h. What a freaky coincidence. The days before I messed with this crackme1, I had been working on an ASM implementation of TEA, because I couldn't think of anything better to waste my time with :-). And this was definitely the good old TEA decryption algorithm I was looking at. With a minor change, though. TEA usually has a keysize of 128 bits, split in four DWORD components. This version uses only a single DWORD, which is used in all four places where the individual components should have been used, thus reducing keysize to 32 bits. Sounds like it might be feasible to bruteforce, eh? You might want to find some TEA code in C on the net, or translate the asm yourself -- it helps getting a better view of what's happening. As you can see, it is pretty trivial, with very little setup, and no use of tables. But even though it's functionally simple, reversing it requires some crypto knowledge. Which I don't have. Sorry.

appendix 2 - decryption listing 00497198 tea_decrypt proc near ; CODE XREF: start+57 00497198 ; start+A7 00497198 pusha 00497199 mov ebp, 32 ; 32 rounds is the usual 0049719E mov eax, 0C6EF3720h; "delta" shl 5 004971A3 mov edx, ds:data_0 004971A9 mov ecx, ds:data_1 004971AF mov esi, ds:TEAkey 004971B5 @@round: ; CODE XREF: tea_decrypt+51 004971B5 mov edi, edx 004971B7 shr edi, 5 004971BA add edi, esi 004971BC mov ebx, edx 004971BE shl ebx, 4 004971C1 add ebx, esi 004971C3 xor edi, ebx 004971C5 lea ebx, [eax+edx] 004971C8 xor edi, ebx 004971CA sub ecx, edi 004971CC mov edi, ecx 004971CE shr edi, 5 004971D1 add edi, esi 004971D3 mov ebx, ecx 004971D5 shl ebx, 4 004971D8 add ebx, esi 004971DA xor edi, ebx 004971DC lea ebx, [eax+ecx] 004971DF xor edi, ebx 004971E1 sub edx, edi 004971E3 sub eax, 9E3779B9h ; delta value 004971E8 dec ebp 004971E9 jnz short @@round 004971EB mov ds:data_0, edx 004971F1 mov ds:data_1, ecx 004971F7 popa 004971F8 retn 004971F8 tea_decrypt endp

Well. If you run the crackme with a bogus keyfile, it doesn't crash. So there has to be a keyfile validation check, or checksum, or something. Quite simple, really. data_0 and data_1 have funky values on program start. [yAtEs] decrypts these with the second dword from the keyfile (the key), and checks for them being zero. If they aren't, goto(badBoyMessageBoxA). Nice and efficient check. This means bruteforcing will only have to be done on a single data item, 8 bytes, and not on a whole range of code followed by a checksum check. Also, it means we have known plaintext, but... I doubt that will help an average reverser very much :-).

With this knowledge, we could stop analyzing the rest of the crackme. We have enough information to write a bruteforcer and get ourselves a valid keyfile. But it's always good to look a bit at what you're dealing with -- no lazy boy today. What follows is a decrypt loop and a jump to OriginalEntryPoint. The decryptloop is trivial, but a bit queer at the same time. I don't know if [yAtEs] has been using a very bad compiler, has been smoking too many bad floppies, or if he has tried sprinkling a bit of obfuscation. Probably the last, but not very successful imho :). It's pretty straightforward. Fetch an encrypted datablock (8 bytes), stuff it into data_0 and data_1 (and a few other spots as well, just to confuse the cracker), call tea_decrypt, stuff decrypted values back to memory, update pointer, increase number of blocks processed, check if done. When done, display a good boy message, and finally jump to OEP.

Okay, time to write a bruteforcer. Goal is to find a tea-key that decrypts 9184EA05.26440640 to 00000000.00000000. A single datablock, thus [yAtEs] tea_decrypt seems to be efficient enough (however, for use with larger blocks than a single data item, many improvements could be made, also to security, to avoid that two identical data blocks are encrypted to the same encrypted data blocks -- which can happen pretty often in PE files, because of section FILEALIGNMENT). Rather than just scanning from one end to another, I choose to scan from both ends towards the middle. This is usually a big improvement over scanning from one end to another, and simple enough that dull boys like me can implement it in a hurry. You could also split the keyspace in two, and go towards both middles, but that's more complicated and...who cares :). See bruter.c for my bruteforcer.

On my athlon700, the initial revision of the bruteforcer took 2249 seconds - or ~37 minutes and 29 seconds. Not very good :). This was the LCC-compiled version. The speeds were around 1369019,1 tests per second for the LCC version, and surprisingly enough, 1396648,0 for VC6. Not a very big gain! I suppose a handwritten assembly implementation could bring down the execution time, but... hey...I've found the key now :). Oh yes, notice that your key doesn't have to be 12 bytes long, as [yAtEs] is not doing a check on the amount of bytes read. So just making it four dummy bytes followed by the four-byte key works like a charm.

I guess I should be satisfied now. I have brute-forced a decryption key, and thus I can run my target. But this somehow isn't enough. The thought of running a relatively slow decryption loop every time I start the app! On a large amount of data (bloated delphi apps ;). And, had this been a real world situation where I had bought the application, I would have been insulted by the thought of the program doubting my right to run it! Ok, so I'm mad as a hatter, but I decided to remove the TEA wrapper.

We know that the decryptor starts decrypting from address 00401000h. It decrypts 0D780h blocks, or 441344 bytes. (Look at your listing if you can't follow me). Looking at _crackme.exe in a PE editor, we can see this range lies entirely within the first section (and doesn't fill up the entire section - 48 bytes (PhysicalSize) are left untouched. Easy peasy, the steps are simple:

The first two steps are done with a program, I don't feel like doing TEA decryption by hand. The rest of the steps are handled in HIEW, with some help from windows' calc.exe since I'm too dull to add hex numbers at 00:31.

I will not go into details of how decrypt.c works - it should be quite clear if you have understood the rest of this essay. And yes, the code is a bit dirty. Okay, backup _crackme.exe. Run decrypt once (once!). Now the code section should be decrypted. From our dead (or, if you use IDA, interactive) listing, we see that the OEP is at 0046CB70h. Find the RVA of this - either use an RVAtool, or manually subtract ImageBase. Open the file in HIEW (or any other PE editor, or in a raw hex editor if you're used to this stuff), and set the EntryPoint RVA to 0006CB70h - and do a run to see if it works. Mine did :). All this is probably trivial if you have a good knowledge of the PE file format (which is very important, imho. Iczelion PE-Tuts and the LUEVELSMEYER doc.)

Well, now we could be satisfied, I guess. No longer is the evil and slow TEA code run, and we have an application that we can look directly into with hex editors and disassemblers - no longer are we confined to a live image (that could be dumped, btw). However, this is still not good enough! Even though it's not executed, the TEA code is still there. Grumble. Good thing that HIEW makes life so easy for us. Get the header info dialog up (F8), switch to section/object view (F6), select the section named `yates' and press enter. Now we are located in the beginning of that section. F3 to enter edit mode, followed by F9 to truncate the file. Now we must nuke the IMAGE_SECTION_HEADER. This is easiest done in HIEWs section / object table, by removing the name string and putting zeroes in all other values. After that, you must decrease the count of sections in the header. Does the file still run? Yes, that's fine. Now we are finished, but just to be a really good citizen in the realm of windows :), you should update the Size Of Image field in the header as well. Put the RVA of the last section (0007C000h), plus it's PhysSize (0001A8A0h) rounded up to Section Alignment (1000h, resulting in 0001B000h) - a final value of 00097000h. Phew :-), now we should finally be completely done with this target. Time to add some CSS formatting to this document (hopefully before you see it), wrap it all up, and go to bed. 01:06. Yes, 24hr system, the real thing.

To quote [yAtEs]: hehe, i think this protection is very strong to the average
cracker,..prove me wrong and mail me @ Jamesluton@hotmail.com

Yup, it's pretty good stuff, [yAtEs] :). And if you had used an uncrippled TEA, I doubt that anybody would have bothered bruteforcing this. It might have been possible by finding equal encrypted values, and guessing that they are zeroes. However, the CODE section doesn't have a large zero padding as sections usually have, so one would have to look pretty damn hard :). Besides, even if you could find multiple places with the same 8-byte crypted blocks, and guessing they were zeroes... would it help you? Cryptanalysis is still way over my head. Anyway, if the TEA algorithm was uncrippled, and also chained (correct terminology?), for instance not clearing the delta-sum for each block, this would result in equal input-data NOT resulting in equal output-data; I've tested this with a few K's of 42h, and I couldn't spot any easy to find repetitions... TEA is a simple and neat algorithm. I think that was just about what I had to say about this matter. There. 01:49 and it's a wrap.

This essay has been revised a bit compared to the version I sent to http://crackmes.cjb.net, as I've had a couple of nice persons doing some proofreading. Also, the filename has been changed from "essay02" to "essay01", since that's the correct name, and I was pretty tired when I wrote the original. Sigh.

Essay by f0dder(a)flork.dk (f0dder.has.it), last edit at 2001-05-10.