PDA

tdennist
June 22nd, 2005, 10:32
So I've put about 5 total hours of work into this crackme, and I finally came up with what seemed like a plausible solution, so I plugged it in, and it did not work. Part of this I'm sure is due to my ineptitude as a programmer, and the other part is due to my lack of understanding (yet!) of reversing this kind of crackme.

The task is to create a keyfile of the correct length and data. The length I was able to find right away (19 characters), and the data....well, that's where I ran into trouble.

I'll attach the crackme later for you wizards to take a look at, but for now I'll tell you what I deduced and the Java program I wrote to reverse the algorithm.

As near as I can tell, this is what happens in the crackme. The program reads in the string located in the keyfile, and goes through a loop 19 times: (EAX = 0 initially)
- take the next char in the sequence, store it in ECX
- ror eax,8
- xor eax,7B

And the end result is a word. The "correct" solution, which is compared to the calculated solution, is "0xAAE23242". So, I figure, simple enough, I'll just take that solution and run it through the algorithm backwards. This is what I deduced the backwards algorithm to be:
- EAX = 0xAAE23242 initially
- xor eax,7B
- rol eax,8
- AL is the next byte in the "good" sequence.
So if I just concatenate (spelling?) AL each turn through the loop onto the beginning of a variable, at the end I'll end up with the good sequence of characters, right? So I thought.

Here is the Java program (I'm sorry, I would have written it in C, but I am terrible at C) I wrote to do this task:

Code:
```
//created 6/21/05 to reverse a keyfile checksumming algorithm
//practice practice practice makes perfect perfect perfect!

class ReverseKeyfile
{
public static void main(String[] args)
{
int[] bytes = {0xAA, 0xE2, 0x32, 0x42};
int result;
String keyfile = "";

for (int i=1; i<=19; i++) {
bytes[3] ^= 0x7B;
result = bytes[3];
keyfile = Integer.toHexString(result) + keyfile;

//rotate left by one byte:
int[] temp = {0,0,0,0};
temp[0] = bytes[1];
temp[1] = bytes[2];
temp[2] = bytes[3];
temp[3] = bytes[0];
bytes = temp;
}

System.out.println("Keyfile should contain: " + keyfile);
System.out.println("keyfile length: " + keyfile.length());
}
}
```

This printed out the hex string of the correct sequence of letters. It comes up with "99d13932e2aa424999d13932e2aa424999d139" which is 38 chars, or 19 bytes, so it's the correct length. Now, I wrote a C program to print out the character equivalents of each of those bytes:

Code:
```
#include <stdio.h>

int main() {
char code[] = {0x99,0xd1,0x39,0x32,0xe2,0xaa,0x42,0x49,0x99,0xd1,0x39,0x32,0xe2,0xaa,0x42,0x49,0x99,0xd1,0x39};
int i=0;
for (i=0; i<=18; i++) {
printf("%c",code[I]);
}
return 0;
}
```

And this prints out (I don't know if this will copy/paste correctly) "™Ñ92âªBI™Ñ92âªBI™Ñ9". So, I paste this final string into the keyfile, and launch the crackme with fingers crossed and....nope.

As you can see I've put quite a large amount of thought and effort into this, and now any help you can offer will be greatly appreciated.

Thanks for your time and help!

-tdennist
(target crackme attached; I hope that's all right.)

gabri3l
June 22nd, 2005, 13:28
The solution on our site uses a bruteforce method to find the correct serial.
because I dont think this algorithm is reversible. The reason being you add ECX to EAX. before performing the ROR and XOR operations.
When working backwards, ECX is the previous character in the serial. Which you do not know, so you cannot subtract it to find the current character.

SiGiNT
June 22nd, 2005, 14:18
gabi3l,

Then why was it rated a 2? I've encountered a couple low rated ones on crackmes.de where the only solution was to cut and paste a set of untypable characters into the serial box, (I was going to make a wise-ass comment about holding down Fn Ctl Shift Win keys while sticking your tongue out of the left side of your mouth and squinting then hitting the "e" key, but I won't), I realize that the point of a crackme is to make you think, but that seems a little over the top.

SiGiNT

Manko
June 22nd, 2005, 14:29
Hi!

It's easy to get stuck down the wrong lane...

But even if your approach worked, tdennist, it is not keygening, it's finding ONE serial. *But if you add some steps you could work it that way... just remember, you gotta sub, if you reverse...*

Quote:
 [Originally Posted by gabri3l]The solution on our site uses a bruteforce method to find the correct serial.

I'd like to point out that this is really quite a simple algo. No need for bruteforce bashing...

Just make a keyfile of for example 13 alfas and watch live what this algo does and you will hopefully see something funny after some thought/work...

Also, never mind my saying it's an easy algo, if you like bruteforcing it's ok too. Maybe you'll try both. or all three, for the experience....?

btw, gabriel, is the bruteforcing fast?

/Manko

gabri3l
June 22nd, 2005, 17:11
Ah! You are correct Manko. A little creative thinking and this is defeated.

Okay we know that this program runs through 19 digits and ends up adding and xoring the digits on top of each other. You could brute force until you discover the correct serial. But there is another way.

Rather than running arbitrary digits through this algorithm we can run 00's (00 as in NULL not 0 as an ASCII character) through it and examine how the program reacts.

What happens is basically nothing. The algorithm will xor 00 with 7B then rotate the digits right. And continue. After 4 rotations 7B is back in AL, where the program XOR's 7B with 7B. This makes AL = 00.
After 4 more rotations EAX equals 00000000 again!

This gives us a much easier algorithm to work with. And means that only the last 4 BYTES of the key.file are important now!

So lets assume we have a key.file and we pad the beginning with 00's and we'll call the last 4 BYTES D1, D2, D3, and D4

000000000000000000000000000000,D1,D2,D3,D4

Now lets run through the program until we get to the fourth to last byte being read in. So at the fourth to last digit EAX contains: 00007B00

Code:
```
00401107  |> 0FB60F         /MOVZX ECX,BYTE PTR DS:[EDI]             ;  ***MOVES DIGIT FROM SERIAL INTO ECX***
0040110A  |. 03C1           |ADD EAX,ECX                             ;  ***ADDS ECX TO EAX: EAX NOW CONTAINS: 00007BD1***
0040110C  |. C1C8 08        |ROR EAX,8                               ;  ***ROTATE BITS RIGHT, MOVE FROM LOW BYTE TO HIGH: EAX NOW CONTAINS: D100007B***
0040110F  |. 83F0 7B        |XOR EAX,7B                              ;  ***XOR EAX WITH 7B: EAX NOW CONTAINS D1000000***
00401112  |. 47             |INC EDI                                 ;  ***INCREMENT EDI BY 1***
00401113  |> 3BFE            CMP EDI,ESI                             ;  ***CHECK TO SEE IF END OF SERIAL***
00401115  |.^75 F0          \JNZ SHORT crackme0.00401107             ;  ***IF NOT END OF SERIAL LOOP AGAIN***

```

Now we go through again: EAX CONTAINS D1000000

Code:
```
00401107  |> 0FB60F         /MOVZX ECX,BYTE PTR DS:[EDI]             ;  ***MOVES DIGIT FROM SERIAL INTO ECX***
0040110A  |. 03C1           |ADD EAX,ECX                             ;  ***ADDS ECX TO EAX: EAX NOW So at the fourth to last digit EAX contains: D10000D2***
0040110C  |. C1C8 08        |ROR EAX,8                               ;  ***ROTATE BITS RIGHT, MOVE FROM LOW BYTE TO HIGH: EAX NOW CONTAINS: D2D10000***
0040110F  |. 83F0 7B        |XOR EAX,7B                              ;  ***XOR EAX WITH 7B: EAX NOW CONTAINS D2D1007B***
00401112  |. 47             |INC EDI                                 ;  ***INCREMENT EDI BY 1***
00401113  |> 3BFE            CMP EDI,ESI                             ;  ***CHECK TO SEE IF END OF SERIAL***
00401115  |.^75 F0          \JNZ SHORT crackme0.00401107             ;  ***IF NOT END OF SERIAL LOOP AGAIN***

```

So Now EAX CONTAINS D2D1007B

Code:
```
00401107  |> 0FB60F         /MOVZX ECX,BYTE PTR DS:[EDI]             ;  ***MOVES DIGIT FROM SERIAL INTO ECX***
0040110A  |. 03C1           |ADD EAX,ECX                             ;  ***ADDS ECX TO EAX: EAX NOW So at the fourth to last digit EAX contains: D2D100(7B+D3)***
0040110C  |. C1C8 08        |ROR EAX,8                               ;  ***ROTATE BITS RIGHT, MOVE FROM LOW BYTE TO HIGH: EAX NOW CONTAINS: (7B+D3)D2D100***
0040110F  |. 83F0 7B        |XOR EAX,7B                              ;  ***XOR EAX WITH 7B: EAX NOW CONTAINS (7B+D3)D2D17B***
00401112  |. 47             |INC EDI                                 ;  ***INCREMENT EDI BY 1***
00401113  |> 3BFE            CMP EDI,ESI                             ;  ***CHECK TO SEE IF END OF SERIAL***
00401115  |.^75 F0          \JNZ SHORT crackme0.00401107             ;  ***IF NOT END OF SERIAL LOOP AGAIN***

```

Now EAX contains (7B+D3)D2D17B
Last time through
Code:
```
00401107  |> 0FB60F         /MOVZX ECX,BYTE PTR DS:[EDI]             ;  ***MOVES DIGIT FROM SERIAL INTO ECX***
0040110A  |. 03C1           |ADD EAX,ECX                             ;  ***ADDS ECX TO EAX: EAX NOW So at the fourth to last digit EAX contains: (7B+D3)D2D1(7B+D4)***
0040110C  |. C1C8 08        |ROR EAX,8                               ;  ***ROTATE BITS RIGHT, MOVE FROM LOW BYTE TO HIGH: EAX NOW CONTAINS: (7B+D4)(7B+D3)D2D1***
0040110F  |. 83F0 7B        |XOR EAX,7B                              ;  ***XOR EAX WITH 7B: EAX NOW CONTAINS (7B+D4)(7B+D3)D2(D1 XOR 7B)***
00401112  |. 47             |INC EDI                                 ;  ***INCREMENT EDI BY 1***
00401113  |> 3BFE            CMP EDI,ESI                             ;  ***CHECK TO SEE IF END OF SERIAL***
00401115  |.^75 F0          \JNZ SHORT crackme0.00401107             ;  ***IF NOT END OF SERIAL LOOP AGAIN***

```

Well now EAX equals (7B+D4)(7B+D3)D2(D1 XOR 7B)

And we want it to equal: AAE23242

AA=(7B+D4); D4=2F
E2=(7B+D3); D3=67
32=D2; D2=32
42=(D1 XOR 7B); D1=39

So we now make a file named Key.file that is 32 bytes long. Padded in the beginning with zeros, and containing 3932672F at the end:

0000000000000000000000000000003932672F

Thats it! Hope that helped. Yeah it takes some definate thinking outside of the box.

Manko
June 23rd, 2005, 00:15
Hi!

Great work gabri3l!

Putting in zeros is good for evaluating the algo, but you need not stop there...
Try to put in anything (random numbers, like in my keygen) in all but the last 4 and you'll find that all you need to do is still just straighten out that last dword. It's like a checksum of the whole serial. Now you get a full keygen...

That also gives that if you want to bruteforce, you need only bruteforce to solve that last dword cause that is all that is protecting this sucker, and that will be much faster... The lesson here is to think more, so one can narrow down the needed work...

btw, tdennist, I'm sorry about telling you to not serialize this. Seems the object wasn't to keygen, but I still think it is good practice.
(I know I suck at math, so I haven't kegened anything til now.)

/Manko

SunBeam
May 15th, 2006, 07:23
I managed to crack it in 3-4 ways (including everything) :

- jumps
- registers on cmp checks
- finding real code
- making it load a Windows Key from registry

I'm a smart-ass Don't flame