We start with dissasembling the code using Wdasm (I guess we could have used IDA as well) and save the dead listing. Then we fire up wingate and choose "Info" and "Register". Ah - name and key... we have seen this before hav'nt we? Ok...lets begin! Chosse a name and a key (Cruehead - 123123) and then enter Softice and put a bpx on GetWindowTextA. Back to the program again and press OK. First of all we'll just put a breakpoing on the serial and forget the name for now. Why? Because we want to see how difficuly the program is to crack. Maybe there is a "memory echo" somewhere, and then we dont have to care about what calculations are made from the user name. So when softice breaks in, just hit CTRL-D (or F5) and softice will break in again. Press F11 to get out of the function. Trace the two first call and we'll see:
mov edi, dword ptr [ecx]
"D edi" and what do we see? Our key! This cracking stuff is amazing huh? Well, maybe that wasnt very exiting, but put a BPR on the location. Clear the breakpoint on GetWindowTextA (BC) and leave softice again. We will break in where the length of the key is calculated. Step out of the function and we'll see this:
:0042D97A 8B0E mov ecx, dword ptr [esi] :0042D97C 8941F8 mov dword ptr [ecx-08], eax :0042D97F 8B0E mov ecx, dword ptr [esi] :0042D981 5E pop esi :0042D982 C6040100 mov byte ptr [ecx+eax], 00 :0042D986 C20400 ret 0004Nothing new here, it just stores the length of the string at ecx-8 (ecx is a pointer to our key). So, "BPM ds:ecx-8 RW" and leave softice again. We'll see this code when softice breaks in again:
:00418C36 8378F818 cmp dword ptr [eax-08], 00000018 :00418C3A 7415 je 00418C51Ah - the key must have a length of exactly 18h (24 dec) chars. So, we'll have to clear all the breakpoints (BC *) and start over again. This time we'll choose another string (Cruehead - 123451234512345123451234). Do the above steps again until you reach the location where we left before.
Now I'll let you play some on your own for a while...see if you can find a memory echo somewhere...
Ok - you give up? No wonder - there is none :). We'll have to breakpoint on the location of the user name as well, in order to see what calculations are made from it. What are you waiting for? Do so (use the same meothod as above).
When you get back to code where we left before (00418C36) you should have 4 breakpoints. Two BPR's (both the user name and the key) and two BPM's (length of user name and length of key). You can now delete the two BPM's ... we dont need them anymore. Ok, leave softice again and it will break in at:
* Referenced by a Conditional Jump at Address: |:00418C8C | :00418C55 8D0C7D00000000 lea ecx, dword ptr [2*edi+00000000] :00418C5C 47 inc edi :00418C5D 030E add ecx, dword ptr [esi] :00418C5F 885DF3 mov byte ptr [ebp-0D], bl :00418C62 8A01 mov al, byte ptr [ecx] <-- We'll land here :00418C64 8845F0 mov byte ptr [ebp-10], al :00418C67 8D45F0 lea eax, dword ptr [ebp-10] :00418C6A 8A5101 mov dl, byte ptr [ecx+01] :00418C6D 8D4DF3 lea ecx, dword ptr [ebp-0D] :00418C70 51 push ecx :00418C71 8855F1 mov byte ptr [ebp-0F], dlThis code moves two bytes at a time from the key string to another location (EBP-10).
I dont think you want to see a lot of useless code here in this tutorial so I'll just skip the next piece of code and describe what it does instead.
It takes the first byte of the two that are located at EBP-10 and substracts 30 from it. Then it multiplies this value with A hex (10 dec), takes the
second byte and substracts 30 from it. Then it adds this value to the former value. This value is saved in location EBP-1C. This loop goes
on until the whole string is processed.
12 34 51 23 45 12 34 51 23 45 12 34
Got It? Good... so when you reach cs:00418C62 remove the BPR on your key, and BPR ds:ebp-1C ds:ebp-10 instead. Softice will break in many times when the string is copied into this location.
After the whole string has been copied, leave softice again, and when we get our beloved softice on the screen again the length of the user name is calculated again. After this we'll land when the user name is copied to another location. So remove the BPR and place it on the new location. Leave softice and it will pop up in a code that looks like this:
* Referenced by a Conditional Jump at Address: |:00418B47 | :00418B16 8D2C06 lea ebp, dword ptr [esi+eax] :00418B19 33D2 xor edx, edx :00418B1B 33DB xor ebx, ebx :00418B1D 83C104 add ecx, 00000004 :00418B20 8A5503 mov dl, byte ptr [ebp+03] <-- We'll land here :00418B23 8A5D01 mov bl, byte ptr [ebp+01] :00418B26 C1E210 shl edx, 10 :00418B29 83C604 add esi, 00000004 :00418B2C 0BD3 or edx, ebx :00418B2E 33DB xor ebx, ebx :00418B30 C1E208 shl edx, 08 :00418B33 8A5D02 mov bl, byte ptr [ebp+02] :00418B36 C1E310 shl ebx, 10 :00418B39 0BD3 or edx, ebx :00418B3B 33DB xor ebx, ebx :00418B3D 8A5D00 mov bl, byte ptr [ebp+00] :00418B40 0BD3 or edx, ebx :00418B42 3BFE cmp edi, esi :00418B44 8951FC mov dword ptr [ecx-04], edx :00418B47 77CD ja 00418B16Explanation of the code: The username is copied to another location and the space after the name will be filled with 00 bytes. Change the bpr once again (hope you're not getting tierd of all these changing of locations) and leave softice. Now for the first time we'll land in an interesting part:
Referenced by a CALL at Addresses: |:0041811B , :00418135 | ... ... ... :0041820C 8BC7 mov eax, edi :0041820E 8BCE mov ecx, esi :00418210 F7D0 not eax :00418212 23C3 and eax, ebx :00418214 23CF and ecx, edi :00418216 0BC1 or eax, ecx :00418218 03442414 add eax, dword ptr [esp+14] <-- We'll land here :0041821C 2D885B9528 sub eax, 28955B88 :00418221 03E8 add ebp, eax ... ... ... :00418A7F 015104 add dword ptr [ecx+04], edx <-- Important :00418A82 014108 add dword ptr [ecx+08], eax <-- Important :00418A85 01790C add dword ptr [ecx+0C], edi <-- Important :00418A88 8D7C2414 lea edi, dword ptr [esp+14] :00418A8C 33C0 xor eax, eax :00418A8E 5D pop ebp :00418A8F 017110 add dword ptr [ecx+10], esi :00418A92 B910000000 mov ecx, 00000010 :00418A97 F3 repz :00418A98 AB stosd :00418A99 5F pop edi :00418A9A 5E pop esi :00418A9B 5B pop ebx :00418A9C 83C448 add esp, 00000048 :00418A9F C20400 ret 0004I skipped a lot of code and only included the interesting part. Some calculcations are beeing done from out username, and the result will be saved in edx,eax and edi. At 00418A7F the result are beeing added to an already made value. So after these additions has been done take a look at ecx-4 (d ecx-4). These 12 bytes are VERY important, so write them down on paper. For my name (Cruehead) the values were "28 31 58 01 CB E7 42 95 45 8B A4 0F".
* Referenced by a Conditional Jump at Address: |:00418CDA | :00418CCE 8A4435D4 mov al, byte ptr [ebp+esi-2C] value calculated from username :00418CD2 304435E4 xor byte ptr [ebp+esi-1C], al xor this value with serial value :00418CD6 46 inc esi :00418CD7 83FE0C cmp esi, 0C :00418CDA 7CF2 jl 00418CCE :00418CDC 8B45EC mov eax, dword ptr [ebp-14] the last four bytes to eax :00418CDF 33F6 xor esi, esi :00418CE1 C1E810 shr eax, 10 keep the last 2 bytes :00418CE4 0FB7C8 movzx ecx, ax :00418CE7 51 push ecx :00418CE8 E843320000 call 0041BF30So....XOR...nice, is'nt it? Write the new string down and make a "table" of it, like this:
Original Value 12 34 51 23 45 12 34 51 23 45 12 34 Name Value 28 31 58 01 CB E7 42 95 45 8B A4 0F Xor'ed Value 3A 05 09 22 8E F5 76 C4 66 CE B6 3B
Now, we only need one BPR, the one on the new xor'ed string, so delete the other. The next interesting part is:
* Referenced by a CALL at Address: |:00418CF1 :0041BF40 E82B320000 call 0041F170 :0041BF45 8B4814 mov ecx, dword ptr [eax+14] :0041BF48 8BD1 mov edx, ecx :0041BF4A 8D0C89 lea ecx, dword ptr [ecx+4*ecx] :0041BF4D 8D0C89 lea ecx, dword ptr [ecx+4*ecx] :0041BF50 03CA add ecx, edx :0041BF52 8D0CCA lea ecx, dword ptr [edx+8*ecx] :0041BF55 C1E108 shl ecx, 08 :0041BF58 2BCA sub ecx, edx :0041BF5A 8D8C8AC39E2600 lea ecx, dword ptr [edx+4*ecx+00269EC3] :0041BF61 894814 mov dword ptr [eax+14], ecx :0041BF64 8BC1 mov eax, ecx :0041BF66 250000FF7F and eax, 7FFF0000 :0041BF6B C1E810 shr eax, 10 ... :00418CF0 46 inc esi :00418CF1 E84A320000 call 0041BF40 :00418CF6 304435E3 xor byte ptr [ebp+esi-1D], al :00418CFA 83FE0A cmp esi, 0000000A :00418CFD 7CF1 jl 00418CF0Then the value in AL will be xor'ed with the already xor'ed value (in my case 3A 05 09 22 8E F5 76 C4 66 CE). Note that only the 10 first bytes is xored this way! When this is done, you see this:
:00418CFF 8B55E4 mov edx, dword ptr [ebp-1C] :00418D02 8B4DE8 mov ecx, dword ptr [ebp-18] :00418D05 C1EA10 shr edx, 10 :00418D08 8B45EC mov eax, dword ptr [ebp-14] :00418D0B C1E910 shr ecx, 10 :00418D0E C1E810 shr eax, 10 :00418D11 668BDA mov bx, dx :00418D14 66C1EB08 shr bx, 08 :00418D18 02DD add bl, ch :00418D1A 02DC add bl, ah :00418D1C 025DE5 add bl, byte ptr [ebp-1B] :00418D1F 025DE9 add bl, byte ptr [ebp-17] :00418D22 025DED add bl, byte ptr [ebp-13] :00418D25 02D8 add bl, al :00418D27 02D9 add bl, cl :00418D29 02DA add bl, dl :00418D2B 025DE8 add bl, byte ptr [ebp-18] :00418D2E 025DE4 add bl, byte ptr [ebp-1C] :00418D31 3A5DEC cmp bl, byte ptr [ebp-14] :00418D34 752C jne 00418D62 :00418D36 837DE800 cmp dword ptr [ebp-18], 00000000 :00418D3A 7526 jne 00418D62 :00418D3C 8B450C mov eax, dword ptr [ebp+0C] :00418D3F 8A4DED mov cl, byte ptr [ebp-13] :00418D42 8B55E4 mov edx, dword ptr [ebp-1C] :00418D45 8808 mov byte ptr [eax], cl :00418D47 8B4510 mov eax, dword ptr [ebp+10] :00418D4A C745FCFFFFFFFF mov [ebp-04], FFFFFFFF :00418D51 8910 mov dword ptr [eax], edx :00418D53 E830000000 call 00418D88 :00418D58 B801000000 mov eax, 00000001 :00418D5D E9DCFEFFFF jmp 00418C3ESo...here is the accual check! First all values except the nine'th are add'ed. Then this value will be compared with the nine'th, and if it is a correct serial, they will be equal to eachother. Simply nop away the JNE (if we would do a patch, here is one of the locations we would change), and continue the stepping. Next it compares byte 4,5,6 and 7 with 00. They will be zero if it is a valid serial! So...those were the two checks...but isnt the code at offset 0041D3F interesting??? Have a look in the help file!
You may register with payment for a key that will not expire. Depending on your requirements, you may choose a license that suits. Keys are available for the following numbers of concurrent users.
╖ 2 Users
╖ 5 Users
╖ 10 Users
╖ unlimited
So...there are diffrent kinds of keys... Load the the dead-listing of wingate and search for "unlimitied". We'll land here:
:00402AED 682E040000 push 0000042E :00402AF2 51 push ecx :00402AF3 E8610F0300 call 00433A59 :00402AF8 803DB8DC4400FF cmp byte ptr [0044DCB8], FF :00402AFF 7513 jne 00402B14 * Possible StringData Ref from Data Obj ->"Unlimited"Ah - it compares a byte with FF, and if it's a match, it will be registered with unlimited of users! At offset 00418D45 you see that CL i moved to another location. If you want to be sure that this is the "number of users" byte, BPM this memory location, otherwise - take my word for it :).
So, now that we now all about how the check is done, lets get to the interesting part - how to calculate a valid serial!
First, lets write down what we know:
* The 9'th byte and the sum of all the other bytes must be equal.
* Byte 4,5,6 and 7 must be zero.
* Byte 10 must be FF
So...the serial must look like this (after all of the XOR steps): XX XX XX XX 00 00 00 00 ** FF XX XX, where ** = length of the rest of the bytes. So, now it's time for some thinking! Take a minute or two to think about this, and when you're done - continue reading.
The difficult part is that 9'th byte...But if we could calculate the sum right now, that would solve alot of problems. So...can we? YES WE CAN! The last two bytes will only be modifed ONCE, during the first XOR part. And we know that the two last bytes of the name values were A4 and 0F (for the name "Cruehead" thas is). So if we use 0000 as the four last numbers in our serial, the two last bytes will be A4 and 0F. So our serial (after all XOR) will look like this: XX XX XX XX 00 00 00 00 ** FF A4 0F. Now, what about the four first bytes? Well, lets make it simple - lets make them zero. So our final serial would be 00 00 00 00 00 00 00 00 ** FF A4 0F and now we can easily calculate the sum of the bytes. FF + A4 + 0F = B2 (well, accually 1B2, but we only use B2). And the correct serial for Cruehead after the XOR steps would be : 00 00 00 00 00 00 00 00 B2 FF A4 0F.
So, lets run Wingate again, but this time with the serial 123451234512345123450000. Do the same steps as described above, but wait when you're about to XOR the already XOR'ed value (at offset 00418CF6) and write down the values in AL for each XOR'ed byte. I got these values: "39 F2 C4 C9 FB 97 5B B5 6A F5".
Now we're almost done! Lets make a table again:
We want these bytes | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | B2 | FF | A4 | 0F |
The values we got from the second XOR | 39 | F2 | C4 | C9 | FB | 97 | 5B | B5 | 6A | F5 | 00 | 00 |
Result if we XOR them | 39 | F2 | C4 | C9 | FB | 97 | 5B | B5 | D8 | 0A | A4 | 0F |
Did I hear someone shouting "Hey! Why did you XOR them???" somewhere? Well, I'll try to explain it. The first byte we wanted after the second XOR step was 00 and we knew what value the first byte would be Xor'ed with (39).
Guess what XOR 39,39 is? Yes - 00!!!
So the bytes we wanted after the first XOR would be "39 F2 C4 C9 FB 97 5B B5 D8 0A A4 0F". Remeber the values calculated from username? Now we'll need them! As I said in the begining the serial entered was XOR'ed with the name values. What if we XOR'ed the name values with the values we wanted after the first XOR step?
WE WOULD GET THE CORRECT SERAIL!
So, here we are:
We want these values after the first XOR step | 39 | F2 | C4 | C9 | FB | 97 | 5B | B5 | D8 | 0A | A4 | 0F |
Name Values | 28 | 31 | 58 | 01 | CB | E7 | 42 | 95 | 45 | 8B | A4 | 0F |
Correct serial | 11 | C3 | 9C | C8 | 30 | 70 | 19 | 20 | 9D | 81 | 00 | 00 |
And we're done! The correct serial for Cruehead is 11C39CC8307019209D810000
Copyright © MiB 1997. All rights reversed.