Triad Crackme by Basse
 
Intro


The following tutorial hopefully will be clear and understandable. If not then please forgive me as I have come to the solution in a very roundabout way, so maybe this tut will end up the same way. If at times I do not go in depth into the code then it might just be because I assume that for this crackme you have solved some before. We will be dealing with some crypto stuff and you will need some "zen" for this one. If you didn't, please let me know how you solved this one.

Thanks to Detten for the initial password for the zip file.

Tools used: WDasm, Softice and heaps of paper and pencil.

The code
Lets go.

We are presented with a zip file containing 3 files. The crackme, readme.txt and info file. From the info file we see we need to find the password to unzip the files. From the info file we have the hint brutable. There are many tools available on the internet to find "lost" passwords for zipfiles. I had gone through serials up to 10 digits and still did not find anything. Eventual with the help from Detten I got the right serial. If you want to have a go yourself, use only Chars a-z and A-Z. The string is just 6 chars long, so only takes a few minutes. If you need the password just mail me.

That was the easy part.

We know from our info file that the .text section is encrypted and that the key (dword) lies within the readme file. Well, if you open the readme file you just see a bunch of letters that do not seem to make sense (yet). So how can we get around this. Let's try to find the dword needed to decrypt the .text section. If you look at most crackmes entry point you will find this:

00401000 6A00                    push 00000000
* Reference To: KERNEL32.GetModuleHandleA, Ord:0111h
00401002 E87D010000              Call 00401184
00401007 A3A0304000 mov dword ptr [004030A0], eax

of course we will have differences in the address for the call, and the dword used, but...

6A00E8?? ??????A3???????? -> most crackme's entry point
A34BD25F CC4B3ADB         -> our encrypted entry point

lets xor that what is known. As you see we have the first three bytes in the first dword, and the 4th byte in the second dword giving us the key C94B3A78. Now we need to find out how much of the .text section is encrypted, start with decryting the whole section and check the deadlisting. You'll find messed up code at the end, so start working your way back one dword at a time and you will have the decrypted .text very soon.

Now we can start cracking the protection and find a working password.

When you run the program and enter a random password, you are presented with a messagebox with a message in a language unknown to me. So we can assume already that the password is used to show aa correct message. Lets dig in the code.

Using WDasm we soon arrive at the code that retrieves our password.

:0040105D 68FF000000              push 000000FF
:00401062 6867304000 push 00403067 ; Location to store password
:00401067 68BB0B0000 push 00000BBB
:0040106C FF7508 push [ebp+08] * Reference To: USER32.GetDlgItemTextA, Ord:0102h :0040106F E84E040000 Call 004014C2
:00401074 0BC0 or eax, eax
:00401076 7507 jne 0040107F
:00401078 E9D6000000 jmp 00401153
:0040107D EB18 jmp 00401097 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401076(C) :0040107F 83F801 cmp eax, 00000001 ; if length password only 1
:00401082 7513 jne 00401097
:00401084 A067304000 mov al, byte ptr [00403067]
:00401089 E876090000 call 00401A04 ; Check first character valid
:0040108E 0BC0 or eax, eax
:00401090 7505 jne 00401097
:00401092 E9BC000000 jmp 00401153 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0040107D(U), :00401082(C), :00401090(C) * Possible StringData Ref from Data Obj ->"DDVDFGGGXVFVDVDVDXGAGVVGDFDGDDDGAVDVDDDGXVVFXX"
->"DGDDGADXXDAGXGDDADGXFGDVGVVVDGDFDGDDVAVDVGDDDG"
->"VVAG" :00401097 6806304000 push 00403006 ; Encrypted message
:0040109C E89B040000 call 0040153C ; Get length string
:004010A1 6A00 push 00000000
:004010A3 50 push eax
:004010A4 6867304000 push 00403067 * Possible StringData Ref from Data Obj ->"DDVDFGGGXVFVDVDVDXGAGVVGDFDGDDDGAVDVDDDGXVVFXX"
->"DGDDGADXXDAGXGDDADGXFGDVGVVVDGDFDGDDVAVDVGDDDG"
->"VVAG" :004010A9 6806304000 push 00403006
:004010AE 6866314000 push 00403166 ; location for decrypted message
:004010B3 E87C060000 call 00401734 ; Create string for MessageBox
:004010B8 6866314000 push 00403166
:004010BD E87A040000 call 0040153C ; Get length string
:004010C2 83C008 add eax, 00000008 ; Add extra 8 bytes to length
:004010C5 50 push eax
:004010C6 6A40 push 00000040 * Reference To: KERNEL32.LocalAlloc, Ord:01AFh :004010C8 E867040000 Call 00401534
:004010CD A3D8334000 mov dword ptr [004033D8], eax
:004010D2 8D3566314000 lea esi, dword ptr [00403166]
:004010D8 8B3DD8334000 mov edi, dword ptr [004033D8]
:004010DE B907000000 mov ecx, 00000007
:004010E3 F3 repz
:004010E4 A4 movsb
:004010E5 C60700 mov byte ptr [edi], 00
:004010E8 47 inc edi
:004010E9 806FF820 sub byte ptr [edi-08], 20 ; Small to Caps
:004010ED B90F000000 mov ecx, 0000000F
:004010F2 F3 repz
:004010F3 A4 movsb
:004010F4 66C7070A0D mov word ptr [edi], 0D0A ; Return
:004010F9 806FF120 sub byte ptr [edi-0F], 20 ; Small to Caps
:004010FD 83C702 add edi, 00000002
:00401100 B903000000 mov ecx, 00000003
:00401105 F3 repz
:00401106 A4 movsb
:00401107 C60720 mov byte ptr [edi], 20 ; Add space
:0040110A 806FFD20 sub byte ptr [edi-03], 20
:0040110E 47 inc edi
:0040110F B905000000 mov ecx, 00000005
:00401114 F3 repz
:00401115 A4 movsb
:00401116 C60720 mov byte ptr [edi], 20 ; Add space
:00401119 47 inc edi
:0040111A B903000000 mov ecx, 00000003
:0040111F F3 repz
:00401120 A4 movsb
:00401121 C60720 mov byte ptr [edi], 20 ; Add space
:00401124 47 inc edi
:00401125 B907000000 mov ecx, 00000007
:0040112A F3 repz
:0040112B A4 movsb
:0040112C C60720 mov byte ptr [edi], 20 ; Add space
:0040112F 47 inc edi
:00401130 B908000000 mov ecx, 00000008
:00401135 F3 repz
:00401136 A4 movsb
:00401137 C60700 mov byte ptr [edi], 00 ; End of String
:0040113A A1D8334000 mov eax, dword ptr [004033D8]
:0040113F 83C008 add eax, 00000008
:00401142 6A40 push 00000040
:00401144 FF35D8334000 push dword ptr [004033D8]
:0040114A 50 push eax
:0040114B FF7508 push [ebp+08] * Reference To: USER32.MessageBoxA, Ord:01BBh :0040114E E875030000 Call 004014C8 ; Our Message box The comments should speak for themselves, so lets trace into the following code :004010B3 E87C060000 call 00401734 ; Create string for MessageBox


as his should hold the key to show the right message. We enter the following code. Again comments should speak for themselves. In this crackme it is important to keep track of what is pushed before the call, just to make things harder.

* Referenced by a CALL at Address:
|:004010B3 :00401734 55 push ebp
:00401735 8BEC mov ebp, esp
:00401737 83C4EC add esp, FFFFFFEC
:0040173A 53 push ebx
:0040173B 51 push ecx
:0040173C 52 push edx
:0040173D 57 push edi
:0040173E 56 push esi
:0040173F FF7510 push [ebp+10] ; Our Serial
:00401742 E859020000 call 004019A0 ; make string from serial
:00401747 8945FC mov dword ptr [ebp-04], eax ; memory loc1
:0040174A 837D1800 cmp dword ptr [ebp+18], 00000000
:0040174E 7507 jne 00401757 * Possible StringData Ref from Data Obj ->"8P3D1NLT4OAH7KBC5ZJU6WGMXSVIR29EY0FQBUTTON" :00401750 C745187C334000 mov [ebp+18], 0040337C ; key string * Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040174E(C) :00401757 FF75FC push [ebp-04]
:0040175A E8D5030000 call 00401B34 ; small to caps
:0040175F FF75FC push [ebp-04] ; loc1
:00401762 E8D5FDFFFF call 0040153C ; Get length string
:00401767 8945F0 mov dword ptr [ebp-10], eax
:0040176A 40 inc eax
:0040176B 50 push eax
:0040176C 6A40 push 00000040 * Reference To: KERNEL32.LocalAlloc, Ord:01AFh :0040176E E8C1FDFFFF Call 00401534 ; create loc3
:00401773 8945F8 mov dword ptr [ebp-08], eax ; handle loc3
:00401776 FF75F0 push [ebp-10] ; length loc1
:00401779 FF75F8 push [ebp-08] ; loc3
:0040177C FF75FC push [ebp-04] ; loc1
:0040177F E898060000 call 00401E1C ; copy loc1 to loc3
:00401784 6A01 push 00000001
:00401786 FF75F0 push [ebp-10] ; length loc1
:00401789 FF75F8 push [ebp-08] ; loc3
:0040178C E8A8010000 call 00401939 ; sort loc3 A-Z
:00401791 8B4514 mov eax, dword ptr [ebp+14]
:00401794 40 inc eax
:00401795 50 push eax
:00401796 6A40 push 00000040 * Reference To: KERNEL32.LocalAlloc, Ord:01AFh :00401798 E897FDFFFF Call 00401534 ; create loc4
:0040179D 8945F4 mov dword ptr [ebp-0C], eax ; handle loc4
:004017A0 FF7514 push [ebp+14] ; length encrypted message
:004017A3 FF75F4 push [ebp-0C] ; loc4
:004017A6 FF750C push [ebp+0C] ; encrypted message
:004017A9 E86E060000 call 00401E1C ; Copy encrypted message to loc4
:004017AE FF75FC push [ebp-04] ; loc1
:004017B1 FF7514 push [ebp+14] ; length encrypted message
:004017B4 E8C9010000 call 00401982 ; Length encrypted message int div length loc1, mul length loc1
:004017B9 8945EC mov dword ptr [ebp-14], eax
:004017BC 33C9 xor ecx, ecx
:004017BE 33DB xor ebx, ebx
:004017C0 8B75FC mov esi, dword ptr [ebp-04] ; loc1
:004017C3 8B7DF8 mov edi, dword ptr [ebp-08] ; loc3
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004017D9(U), :0040180A(U) :004017C6 8A0431 mov al, byte ptr [ecx+esi]
:004017C9 8BD9 mov ebx, ecx
:004017CB 43 inc ebx
:004017CC 3B4DF0 cmp ecx, dword ptr [ebp-10] ; is length loc1?
:004017CF 7502 jne 004017D3
:004017D1 EB39 jmp 0040180C * Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004017CF(C), :00401808(U) :004017D3 3B5DF0 cmp ebx, dword ptr [ebp-10] ; is length loc1
:004017D6 7203 jb 004017DB
:004017D8 41 inc ecx
:004017D9 EBEB jmp 004017C6
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004017D6(C) :004017DB 3A043B cmp al, byte ptr [ebx+edi]
:004017DE 7527 jne 00401807 ; find location in A-Z
:004017E0 33C0 xor eax, eax
:004017E2 8A043B mov al, byte ptr [ebx+edi]
:004017E5 50 push eax
:004017E6 8A0439 mov al, byte ptr [ecx+edi]
:004017E9 88043B mov byte ptr [ebx+edi], al
:004017EC 58 pop eax
:004017ED 880439 mov byte ptr [ecx+edi], al ; swap places chars
:004017F0 FF75EC push [ebp-14] ; result call 4017B4
:004017F3 FF75F0 push [ebp-10] ; length loc1
:004017F6 8BC3 mov eax, ebx
:004017F8 50 push eax
:004017F9 8BC1 mov eax, ecx
:004017FB 50 push eax
:004017FC FF75F4 push [ebp-0C] ; loc4
:004017FF E804010000 call 00401908 ; swap chars in loc4
:00401804 41 inc ecx
:00401805 EB03 jmp 0040180A ; next char loc1


What we can tell from the above is that our password gets processed, then this is used to decrypt the encrypted message, which then is converted to the real message using the key string. We need to check the call processing our password, the call swapping things around in our encrypted message and finally how the readable message is created.

lets start with the last one. This is the code we will come to when we have gone through the above:

:00401827 8B75F4                  mov esi, dword ptr [ebp-0C]
:0040182A 8B7D18 mov edi, dword ptr [ebp+18]
:0040182D 33C9 xor ecx, ecx
:0040182F 33D2 xor edx, edx * Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004018B4(U) :00401831 33DB xor ebx, ebx
:00401833 668B0431 mov ax, word ptr [ecx+esi]
:00401837 3C41 cmp al, 41
:00401839 7505 jne 00401840
:0040183B 83C300 add ebx, 00000000
:0040183E EB2B jmp 0040186B
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401839(C) :00401840 3C44 cmp al, 44

This is just the first part of the code. It goes on for quite a while. What happens here is that in our decrypted encrypted message, which goes something like this DVGDXAGVDD.... or something like that, we take two letters at a time in ax, which are checked in the above code, being transferred to a number, which corresponds with a location in our key string, to create the real message. Let me explain. The above code results in the following table:

Letter	Hex	val1 (dec)	val2(dec)
A 41 0 0
D 44 6 1
F 46 12 2
G 47 18 3
V 56 24 4
X 58 30 5

So if we have the string DVGDXAGV.., we would have DV checked first, giving us a value of 6 + 4 = 10, which in our keystring "8P3D1NLT4OAH7KBC5ZJU6WGMXSVIR29EY0FQBUTTON", results with the char at position 11 (first letter is pos 0) which gives us "A", in this manner we can turn the whole string into the real message. I think the above should be pretty clear, at least I hope.

So now the task is to get the correct decryption of our message, which means we would actually need to know what our message should read. Ok lets first look at what else is happening. Lets look at our password, what happens to that :-), we trace the call at 00401742 and land here;

:004019A0 55                      push ebp
:004019A1 8BEC mov ebp, esp
:004019A3 83C4F4 add esp, FFFFFFF4
:004019A6 51 push ecx
:004019A7 57 push edi
:004019A8 56 push esi
:004019A9 FF7508 push [ebp+08] ; Our Serial
:004019AC E88BFBFFFF call 0040153C ; Get length string
:004019B1 8945F8 mov dword ptr [ebp-08], eax
:004019B4 40 inc eax
:004019B5 50 push eax
:004019B6 6A40 push 00000040
* Reference To: KERNEL32.LocalAlloc, Ord:01AFh :004019B8 E877FBFFFF Call 00401534
:004019BD 8945FC mov dword ptr [ebp-04], eax ; handle loc1
:004019C0 6A1A push 0000001A
:004019C2 6A40 push 00000040 * Reference To: KERNEL32.LocalAlloc, Ord:01AFh :004019C4 E86BFBFFFF Call 00401534
:004019C9 8945F4 mov dword ptr [ebp-0C], eax ; handle loc2
:004019CC FF75FC push [ebp-04] ; loc1
:004019CF FF7508 push [ebp+08] ; Serial
:004019D2 E8C4030000 call 00401D9B ; mov ne chars in loc1
:004019D7 FF75F4 push [ebp-0C] ; loc2
:004019DA FF75FC push [ebp-04] ; loc1
:004019DD E8D3020000 call 00401CB5 ; create new string from loc1
:004019E2 8B75F4 mov esi, dword ptr [ebp-0C] ; loc2
:004019E5 8B7DFC mov edi, dword ptr [ebp-04] ; loc1
:004019E8 8B4DF8 mov ecx, dword ptr [ebp-08] ; length loc1
:004019EB F3 repz
:004019EC A4 movsb
:004019ED C60700 mov byte ptr [edi], 00 ; add end string loc1
:004019F0 FF75F4 push [ebp-0C] ; loc2
* Reference To: KERNEL32.LocalFree, Ord:01B3h :004019F3 E81C040000 Call 00401E14 ; clean up loc2
:004019F8 8B45FC mov eax, dword ptr [ebp-04]
:004019FB 5E pop esi
:004019FC 5F pop edi
:004019FD 59 pop ecx
:004019FE C9 leave
:004019FF C20400 ret 0004

Ok, first at 4019D2 we have the first handling of our serial. What happens in this routine is that from our password is removed characters that are the same, so AABBCC would become ABC. I'm not listing all the code, as it's getting quite long already and there is still a lot to come. The call at 4019DD does the following. From the letters A-Z all the letters in our new password are removed, then our password is placed in front of this, and the last two letters of the whole string are placed in front of this, then our string gets truncated to the original length of our password. Clear :-) example, our password JIHGFEDCBA, remove letters in A-Z gives KLMNOPQRSTUVWXYZ, Append, and last two to front gives YZJIHGFEDCBAKLMNOPQRSTUVWX, truncate to original length YZJIHGFEDC, our new password. If you're not clear on it yet trace the call and follow what I described.

So we return from this call. Then at 0040178C we have after our new password has been copied a call which sorts our copied password. So we end up with two strings, our new password YZJIHGFEDC, and the sorted one CDEFGHIJYZ.

At 004017B4 we have what later will be clear a very important call, which eventually will give us an indication of the length of our password. What happens here is and integer division and a multiplication. Result=(Length encrypted message) int div (length password) mul (length password)

Then at 004017C0 we start the following loop.

poss_pass =0
pos_y=0
while poss_pass <= length password {
pos_y = find char[poss_pass] in sorted password
swap sorted password char[poss_pass] and char[pos_y]
ecx = poss_pass
while pos_y <= Result {
swap encrypted message char[ecx] and char[pos_y]
add ecx, length password
add pos_y, length password
}
add poss_pass, 1
}

this should not be to hard to understand so I leave this with you. If you need more help on this, just ask.

Now we can learn from all the above that our maximum length of the password can be 26 (1Ah), the number of letters in alphabet.

Now comes the interesting part as with all the above we know we are in actuall fact still a 1000 years from finding the password. Of course bruting is always an option, but with A-Z and not knowing the length, maybe 26 chars, this could taken an eternity, so we need to try something else. Remember it's a crackme so should be possible to break. We need some "zen" or whatever you're into. When you've done some of Basse's crackme's and look at the message that appears in the message box we have a message that looks like this

Title 7 chars

First word 15 chars
3 chars space 5 chars space 3 chars space 7 chars space 8 chars

Looking at some of the other crackmes, I was able to reconstruct the following

Success

Congratulations
You found the correct password

Now that we know the final message, we should be able to get close to finding a password.

Using the table we created I made the first encryption and got the following, we need to go from:

DDVDFGGGXVFVDVDVDXGAGVVGDFDGDDDGAVDVDDDGXVVFXXDGDDGADXXDAGXGDDADGXFGDVGVVVDGDFDGDDVAVDVGDDDGVVAG

VDGDFGFGXDVDVDFGDGAXGVVVDVDDGDDADVDDVGDGAXVDXFDGGDXVDGGDAXAGDDDXXDFGDGVVVVXDFGDDADDVVDVDGGDGVVAG

As you see the last three letters (6 chars) do not change. So to be sure all the others get swapped around our Result needs to be 5Bh (60-6).

Some checking results in a password length of 13. So I created two tables like this

         DDVDFGGGXVFVD
         VDVDXGAGVVGDF
         DGDDDGAVDVDDD
         GXVVFXXDGDDGA
         DXXDAGXGDDADG
         XFGDVGVVVDGDF
         DGDDVAVDVGDDDGVVAG
         VDGDFGFGXDVDV
         DFGDGAXGVVVDV
         DDGDDADVDDVGD
         GAXVDXFDGGDXV
         DGGDAXAGDDDXX
         DFGDGVVVVXDFG
         DDADDVVDVDGGDGVVAG

I hope the next will be clear. We have our two strings YZJIHGFEDC and CDEFGHIJYZ. Now with the reversed routine before, we see the following happen. We take "Y", position 1, in our second string this is position 9, these get swapped, eventually resulting in our first string again. These positions are also swapped in our encrypted message. Now using this knowledge we reverse it on our encrypted message and do the following. Starting with the first vertical line

                    12
         DDVDFGGGXVF|V|D
         VDVDXGAGVVG|D|F
         DGDDDGAVDVD|D|D
         GXVVFXXDGDD|G|A
         DXXDAGXGDDA|D|G
         XFGDVGVVVDG|D|F
         DGDDVAVDVGD|D|DGVVAG
          1
         |V|DGDFGFGXDVDV
         |D|FGDGAXGVVVDV
         |D|DGDDADVDDVGD
         |G|AXVDXFDGGDXV
         |D|GGDAXAGDDDXX
         |D|FGDGVVVVXDFG
         |D|DADDVVDVDGGDGVVAG

So we see 1 gets swapped with 12, hope you see what I'm doing. So lets say we start with a sorted string ABCDEFGHIJKYZ (remember last two are added to our string) Then we'll get the following, use paper and pencil to swap.

         YBCDEFGHIJKAZ 1<->12
         YZCDEFGHIJKAB 2<->13
         YZFDECGHIJKAB 3<->6
         YZFDECGHIJKAB 4<->4
         YZFDKCGHIJEAB 5<->11
         YZFDKGCHIJEAB 6<->7
         YZFDKGEHIJCAB 7<->11
         YZFDKGEHIJCAB 8<->8
         YZFDKGEHIJCAB 9<->9
         YZFDKGEHIACJB 10<->12
         YZFDKGEHIAJCB 11<->12
         YZFDKGEHIAJBC 12<->13

So now using our knowledge of how our password was recreated, we get rid of the first two characters, and we add two in the range L-Z at the end. And there we have a working password.

FDKGEHIAJBCLM, is one option. There are numerous options of passwords that will work, so if you want to find another one, go ahead, there might even be a real word for a password, but I'm not going to try to find one.

 

 

 

 

Final notes


There you have it, it's been a very long tutorial and I'm sure it might be confusing at times, so go through it slow and if you have any questions email me through Detten.

cya all.

Chiwaka

 

Back to tutorials

www.biw-reversing.cjb.net