L ZZZZZZ RRRRR SSSSS L Z R R S L aaa Z aaa R R u u S L a Z a RRRRR u u SSSSS XX L aaaa Z aaaa R R u u S XXXX L a a Z a a R R u u S XXXXXX LLLLLLL aaaaa ZZZZZZZ aaaaa R R uuuuu SSSSSS XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXX XXXXXX XXXX proudly presents his 29.Cracking Tutorial (16.10.1999) XX My solution of the Kraecker mAG Project I. Introduction I.1 The tools II. The essay II.1 The CrackMe II.2 PowerZip III. BTW I. Introduction I participated at the Cracking project of the German Scene EZine Kraecker. The targets to crack were |BadBoy|'s CrackMe 1.0 & 2.0 and Powerzip 5.0 - The better you crack them, the more points you achieve. I have to explain the solutions shortly, but as I don't like short solutions, I can write a tut about it. I.1 The tools For the CrackMe: SoftIce (I have v3.23) SmartCheck (I have v6.01) Visual Basic (to compile the KeyGen) For PowerZip: SoftIce W32Dasm (I have v8.9) HexWorkshop (I have v2.54) MASM (to compile the KeyGen) Intellectual support: The CD "Somewhere far beyond" from Blind Guardian II. The essay II.1 The CrackMe At first I disassembled the EXE file with W32Dasm just to see that it is compiled with fucking P-Code. Hm, one week ago I stated that I will never crack a P-Code CrackMe as IMHO P-Code is used, if the coder is not able to code a good protection. Whatever, now I have to (that doesn't mean the statement is wrong, as you see later ;) Alright, then I used SICE. After trying some common breakpoints like hmemcpy, which didn't bring me too far, I used the VB-relative of hmemcpy: MultiByteToWideChar - This one works quite often, and in this case it was a really good choice :) So, start the CrackMe and enter a serial for CrackMe 1. Then enter SICE and set a bpx on MultiByteToWideChar. Leave SICE and hit the TEST button. SICE breaks, to get out of the MultiByteToWideChar function hit F12. Now I knew that edi has quite an important role in this function. When I left the function I tried "d esi" and what did I see? The serial I entered and just one row above (esi-10h): A thing that looked like the real serial: 7657838. This assumptions gets harder, if you look into esi after the third break of MultiByteToWideChar. Here this value is directly in esi. OK, disable all breakpoints (bd*) and try this serial. (Don't worry, if your serial is different as it depends on the current date. This circumstance made me keygen the prog. In fact: The algo of the serial is sooooo easy, that I didn't even search for the code where it get's calculated. I just changed the date a few times and looked how the serial changed. I suggest you start at 1/1/1980 as Windows know no prior date. Here are the important serials: 1.1.1980: 7573576 2.1.1980: 7573620 3.1.1980: 7573664 ... 1.2.1980: 7573578 1.3.1980: 7573580 1.1.1981: 7577376 1.1.1982: 7581176 IMHO, it is quite clear what happens here: You can take date 1.1.1980 as base and then you can say: For every day that is added, the base value of 7573576 is increased by 44, for every month it is increased by 2 and for every year, it is increased by 3600. So, here is the source for the keygen (in VB): ---- Beginning Dim Msg As Long ;; define a variable as LongInteger Msg = 7573576 ;; that is the base value of 1.1.1980 Msg = Msg + (Month(Now) - 1) * 2 ;; + (month - 1) * 2 Msg = Msg + (Day(Now) - 1) * 44 ;; + (day of month - 1) * 44 Msg = Msg + (Year(Now) - 1980) * 3800 ;; + (year-1980) * 3600 Text1.Text = Msg ---- End Now, the second CrackMe: This time it is a name/serial combination. SICE didn't take me too far, so I was forced to use SmartCheck for the first time. I gotta admit, that I can't use it and the algo was again created with 50% trying. So, i started the CrackMe with SmartCheck and entered LaZaRuS as Name and 666999 as serial. As I had no options activated I only saw that - after I clicked the TEST button - some chars were taken from my name and some numbers were taken from the following string: 0110617121214051216101106141404110614140411091211100810101608040610121608100416 After I tried with 3 or 4 names I came to the conclusion that this algo is very easy, too and was quite lucky that I was able to get it by trying as the *many* messages that appeared after I checked all options in Smartcheck really confused me. The first step to find the algo was a correct serial. At the end of the SmartCheck listing of the things that happen, when the button was pressed I found the numbers 60728 and 423384. Hey, I saw this numbers before (in my useless SICE trials). But... I saw at the end of 60728 a "-". So I tried the serial "60728-423384" for "LaZaRuS" and it worked :) Now, the algo: For LaZaRuS I found in SmartCheck that it deals like this: Well, the chars 4,5,6,7 are read and something is done with parts of the big number-string. The values from this string were 106,171,212,140 So, I made this neat calculation: 4. Char (97) * 106 (3/3) + 5. Char (82) * 171 (6/3) + 6. Char (117) * 212 (9/3) + 7. Char (83) * 140 (12/3) = 60728 Well, that is exactly our first part of the serial - Thx for the easy algo :) Now for the second part: Here I found in SmartCheck that this is read: 11 (2/2) -- reads part of number-string: position 2, size 2 = 11 4. Char (97) 3. Char (90) 06 (4/2) -- reads part of number-string: position 4, size 2 = 06 5. Char (82) 4. Char (97) 17 (6/2) 6. Char (117) 5. Char (82) 12 (8/2) 7. Char (83) 6. Char (117) So, we got to find an algo, that returns the value 423384 with these values. It is 11*(97*90)+6*(82*97)+17*(117*82)+12*(83*117). btw: If the name is longer than LaZaRuS, both algos will go to the end in completely the same way as if the name is 7 chars. For 14 chars, for example, the algo 1 is taken from char 4 to 14. The source for the keygen: Dim vorgabe As String Dim sloop As Integer Dim sloop2 As Integer Dim zahl1 As Long Dim zahl2 As Long vorgabe = "0110617121214051216101106141404110614140411091211100810101608040610121608100416" sloop2 = 3 If Len(Textname.Text) > 4 Then For sloop = 3 To Len(Textname.Text) - 1 Step 1 zahl1 = zahl1 + (Asc(Mid$(Textname.Text, sloop + 1, 1)) * Val(Mid$(vorgabe, sloop2, 3))) sloop2 = sloop2 + 3 Next sloop sloop2 = 2 For sloop = 3 To Len(Textname.Text) - 1 Step 1 zahl2 = zahl2 + (Asc(Mid$(Textname.Text, sloop + 1, 1)) * Asc(Mid$(Textname.Text, sloop, 1)) * Val(Mid$(vorgabe, sloop2, 2))) sloop2 = sloop2 + 2 Next sloop Text3.Text = Trim$(Str(zahl1)) + "-" + Trim$(Str(zahl2)) Else Text3.Text = "Enter at least 5 chars" End If --- End of source Find out yourself, what it does: Only so far: Mid$ gets a part of the string, Val converts a string into a number and ASC gives the ASCII value of a char. II.2 PowerZip So, the first part is done. Now for the "real" program: PowerZip 5.01 - When you look at my earlier tuts, you can find a tut for PowerZip 4.51. But this doesn't help as both versions have a completely different serial algo. So, I needed to start again from scratch and was able to have twice the fun ;) So, pretty easy: Start the prog and go to the Register screen. Enter a name and a value for copies and a serial. Push OK and a MessageBox appears telling you that the serial is invalid. So, go in SICE and "bpx MessageBoxA". Now leave SICE and hit OK. SICE breaks and you leave SICE immediately with F12 to push the OK button of the MessageBox. SICE breaks again and we are deep in code segment of MFC42. Hit F12 until you are out and you will find yourself in DTUTIL. Now scroll the code up until you reach a conditional jump. I found it at 150228CC and put a bpx on 150228C5 to enter the call right in front of the jnz. I entered this call and kept tracing (F10) until 1501475D continously checking the registers. At this address I found a "cmp ecx, eax" and ecx contained A2D77 (which is hex of 666999 and the serial I used for trying). So, I checked with "? eax" what eax contains and tried this serial. It worked: Name: LaZaRuS Copies: 1 Serial: 3647999058 Well, now for the keygen. On the way I traced to the serial I found the following String: "1LAZARUSPowerZip 5, Standard Edition". So it seems, that the serial is calculated for COPIES+UPPERCASE(NAME)+"PowerZip 5, Standard Edition". After some checks I found out that all chars except letters and numbers are kicked off the name before the serial is calculated and that the it is not the number of copies which is added in front, but a number that is calculated from it. You find the calculation of the first value that is added in front of the string when you trace into the call at 1501469E and then 78003529. The important loop is located at 78003536-78003589 in the file MSVCRT.DLL. :78003536 55 push ebp :78003537 8BEC mov ebp, esp :78003539 837D1400 cmp dword ptr [ebp+14], 00000000 :7800353D 8B4D0C mov ecx, dword ptr [ebp+0C] :78003540 53 push ebx :78003541 56 push esi :78003542 57 push edi :78003543 0F8576E00000 jne 780115BF :78003549 8B7508 mov esi, dword ptr [ebp+08] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:780115C8(U) | :7800354C 8BF9 mov edi, ecx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:78003571(C) ;; HERE THE FIRST VALUE IS CREATED BY TAKING EVERY | ;; DIGIT OF THE COPIE-VALUE YOU ENTERED MOD 5 :7800354E 8BC6 mov eax, esi :78003550 33D2 xor edx, edx :78003552 F77510 div [ebp+10] :78003555 8BC6 mov eax, esi :78003557 8BDA mov ebx, edx :78003559 33D2 xor edx, edx :7800355B F77510 div [ebp+10] ;; [EBP+10] == 5 :7800355E 83FB09 cmp ebx, 00000009 :78003561 8BF0 mov esi, eax :78003563 0F8764E00000 ja 780115CD :78003569 80C330 add bl, 30 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:780115D0(U) | :7800356C 8819 mov byte ptr [ecx], bl :7800356E 41 inc ecx :7800356F 85F6 test esi, esi :78003571 77DB ja 7800354E :78003573 802100 and byte ptr [ecx], 00 :78003576 49 dec ecx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:78003583(C) ;; HERE, THE NEW NUMBER IS TURNED: for example: 1234->4321 | :78003577 8A17 mov dl, byte ptr [edi] :78003579 8A01 mov al, byte ptr [ecx] :7800357B 8811 mov byte ptr [ecx], dl :7800357D 8807 mov byte ptr [edi], al :7800357F 49 dec ecx :78003580 47 inc edi :78003581 3BF9 cmp edi, ecx :78003583 72F2 jb 78003577 :78003585 5F pop edi :78003586 5E pop esi :78003587 5B pop ebx :78003588 5D pop ebp :78003589 C3 ret For the complete calculation of the serial enter the call in front of the "cmp ecx, eax" explained above and you find the important part at 10001000-10001129 in the file ZLIB.DLL. Exported fn(): adler32 - Ord:0001h :10001000 56 push esi :10001001 8B74240C mov esi, dword ptr [esp+0C] :10001005 57 push edi :10001006 8B7C240C mov edi, dword ptr [esp+0C] :1000100A 8BCF mov ecx, edi :1000100C 81E1FFFF0000 and ecx, 0000FFFF :10001012 C1EF10 shr edi, 10 :10001015 85F6 test esi, esi :10001017 750A jne 10001023 :10001019 B801000000 mov eax, 00000001 :1000101E 5F pop edi :1000101F 5E pop esi :10001020 C20C00 ret 000C * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:10001017(C) | :10001023 53 push ebx :10001024 8B5C2418 mov ebx, dword ptr [esp+18] :10001028 85DB test ebx, ebx :1000102A 0F86EF000000 jbe 1000111F :10001030 55 push ebp * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:10001118(C) | :10001031 81FBB0150000 cmp ebx, 000015B0 :10001037 8BC3 mov eax, ebx :10001039 7205 jb 10001040 :1000103B B8B0150000 mov eax, 000015B0 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:10001039(C) | :10001040 2BD8 sub ebx, eax :10001042 83F810 cmp eax, 00000010 :10001045 0F8CA1000000 jl 100010EC :1000104B 8BE8 mov ebp, eax :1000104D C1ED04 shr ebp, 04 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:100010E6(C) ;; THIS LOOP ADDS ALL CHARS (UNTIL CharsLeft<10h) | :10001050 33D2 xor edx, edx :10001052 83E810 sub eax, 00000010 :10001055 8A16 mov dl, byte ptr [esi] :10001057 83C610 add esi, 00000010 :1000105A 03CA add ecx, edx :1000105C 33D2 xor edx, edx :1000105E 8A56F1 mov dl, byte ptr [esi-0F] :10001061 03F9 add edi, ecx :10001063 03CA add ecx, edx :10001065 33D2 xor edx, edx :10001067 8A56F2 mov dl, byte ptr [esi-0E] :1000106A 03F9 add edi, ecx :1000106C 03CA add ecx, edx :1000106E 33D2 xor edx, edx :10001070 8A56F3 mov dl, byte ptr [esi-0D] :10001073 03F9 add edi, ecx :10001075 03CA add ecx, edx :10001077 33D2 xor edx, edx :10001079 8A56F4 mov dl, byte ptr [esi-0C] :1000107C 03F9 add edi, ecx :1000107E 03CA add ecx, edx :10001080 33D2 xor edx, edx :10001082 8A56F5 mov dl, byte ptr [esi-0B] :10001085 03F9 add edi, ecx :10001087 03CA add ecx, edx :10001089 33D2 xor edx, edx :1000108B 8A56F6 mov dl, byte ptr [esi-0A] :1000108E 03F9 add edi, ecx :10001090 03CA add ecx, edx :10001092 33D2 xor edx, edx :10001094 8A56F7 mov dl, byte ptr [esi-09] :10001097 03F9 add edi, ecx :10001099 03CA add ecx, edx :1000109B 33D2 xor edx, edx :1000109D 8A56F8 mov dl, byte ptr [esi-08] :100010A0 03F9 add edi, ecx :100010A2 03CA add ecx, edx :100010A4 33D2 xor edx, edx :100010A6 8A56F9 mov dl, byte ptr [esi-07] :100010A9 03F9 add edi, ecx :100010AB 03CA add ecx, edx :100010AD 33D2 xor edx, edx :100010AF 8A56FA mov dl, byte ptr [esi-06] :100010B2 03F9 add edi, ecx :100010B4 03CA add ecx, edx :100010B6 33D2 xor edx, edx :100010B8 8A56FB mov dl, byte ptr [esi-05] :100010BB 03F9 add edi, ecx :100010BD 03CA add ecx, edx :100010BF 33D2 xor edx, edx :100010C1 8A56FC mov dl, byte ptr [esi-04] :100010C4 03F9 add edi, ecx :100010C6 03CA add ecx, edx :100010C8 33D2 xor edx, edx :100010CA 8A56FD mov dl, byte ptr [esi-03] :100010CD 03F9 add edi, ecx :100010CF 03CA add ecx, edx :100010D1 33D2 xor edx, edx :100010D3 8A56FE mov dl, byte ptr [esi-02] :100010D6 03F9 add edi, ecx :100010D8 03CA add ecx, edx :100010DA 33D2 xor edx, edx :100010DC 8A56FF mov dl, byte ptr [esi-01] :100010DF 03F9 add edi, ecx :100010E1 03CA add ecx, edx :100010E3 03F9 add edi, ecx :100010E5 4D dec ebp :100010E6 0F8564FFFFFF jne 10001050 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:10001045(C) | :100010EC 85C0 test eax, eax :100010EE 740C je 100010FC * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:100010FA(C) ;; THIS LOOP ADDS THE CHARS THAT ARE NOT ADDED IN THE FIRST LOOP | :100010F0 33D2 xor edx, edx :100010F2 8A16 mov dl, byte ptr [esi] :100010F4 03CA add ecx, edx :100010F6 46 inc esi :100010F7 03F9 add edi, ecx :100010F9 48 dec eax :100010FA 75F4 jne 100010F0 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:100010EE(C) | :100010FC 8BC1 mov eax, ecx ;; THIS CALCULATES THE NEW VALUE :100010FE 33D2 xor edx, edx :10001100 B9F1FF0000 mov ecx, 0000FFF1 :10001105 F7F1 div ecx :10001107 8BC7 mov eax, edi :10001109 BFF1FF0000 mov edi, 0000FFF1 :1000110E 8BCA mov ecx, edx :10001110 33D2 xor edx, edx :10001112 F7F7 div edi :10001114 85DB test ebx, ebx :10001116 8BFA mov edi, edx :10001118 0F8713FFFFFF ja 10001031 :1000111E 5D pop ebp * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:1000102A(C) | :1000111F 8BC7 mov eax, edi :10001121 5B pop ebx :10001122 C1E010 shl eax, 10 :10001125 5F pop edi :10001126 0BC1 or eax, ecx :10001128 5E pop esi :10001129 C20C00 ret 000C Here is the source of the MASM keygen: .IF ax==EN_CHANGE invoke GetWindowTextA,hwndEdit,ADDR buffer, 255 ;; Get Name cmp eax, 2 jb looser ;; then jump lea esi, buffer ;; ebx now poinst to that text lea edi, buffer push eax eins: ;; THIS LOOP CUTS ALL NON-LETTERS AND NON-DIGITS lodsb .IF ((al >= 'a')&&(al <= 'z'))||((al >= 'A')&&(al <= 'Z')||((al >= '0')&&(al <= '9'))) stosb .ENDIF or al, al je zwei jmp eins zwei: mov byte ptr [edi], 00 pop eax mov ebx, edi sub ebx, eax loophere: ;; CONVERT THE NAME TO UPPERCASE cmp eax, 0 ;; if length = 0 je weiterr ;; then jump dec eax ;; decrease eax (length) cmp byte ptr [ebx+eax], 'a' ;; if char to look at jb loophere ;; is below 'a' cmp byte ptr [ebx+eax], 'z' ;; or above 'z' ja loophere ;; jump sub byte ptr [ebx+eax], 20h ;; else subtract 20h (Convert to UpperCase) jmp loophere ;; loop for every char weiterr: invoke GetWindowTextA,hwndEdit2,ADDR buffer2, 255 ;; Get Name cmp eax, 0 je looser ;; then jump invoke atodw, ADDR buffer2 ;; FUNCTION BY ICZELION - ;; CONVERTS THE ASCII VALUE OF THE NUMBER TO EAX ;; IT IS A WEAKNESS AS IT CAN HANDLE ONLY NUMBERS UP TO ;; FFFFFFFF. THAT IS: IF YOU ORDER MORE THAN 4294967295 ;; COPIES, THE KEYGEN DOESN'T WORK :( lea esi, buffer2 zahll: ;; CALCULATE FIRST NEW VALUE xor edx, edx mov ecx, 5 idiv ecx mov ebx, edx add bl, 30h mov byte ptr [esi], bl inc esi test eax, eax ja zahll mov byte ptr [esi], 00 invoke lstrlen, ADDR buffer2 .IF eax>1 ;; TURN VALUE AROUND lea esi, buffer2 xor edx, edx xor ecx, ecx dec eax zahl2: mov bh, byte ptr [esi+ecx] mov bl, byte ptr [esi+eax] mov byte ptr [esi+ecx], bl mov byte ptr [esi+eax], bh inc ecx dec eax cmp ecx, eax jbe zahl2 .ENDIF invoke lstrcat, ADDR buffer2, ADDR buffer ;; VALUE + NAME invoke lstrcat, ADDR buffer2, ADDR string1 ;; VALUE + NAME + "PowerZip..." invoke lstrlen, ADDR buffer2 mov len, al xor ecx, ecx mov cl, len and ecx, 0Fh mov len, cl shr eax, 4 mov len2, al xor edx, edx mov ecx, 1 lea esi, buffer2 xor edi, edi .WHILE len2 > 0 ;; CALCULATE SERIAL (PART1) mov dl, byte ptr [esi] add ecx, edx add esi, 16 xor edx, edx mov dl, byte ptr [esi-0Fh] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-0Eh] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-0Dh] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-0Ch] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-0Bh] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-0Ah] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-09h] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-08h] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-07h] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-06h] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-05h] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-04h] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-03h] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-02h] add edi, ecx add ecx, edx xor edx, edx mov dl, byte ptr [esi-01h] add edi, ecx add ecx, edx add edi, ecx dec len2 .ENDW .WHILE len > 0 ;; CALCULATE SERIAL (PART 2) xor edx, edx mov dl, byte ptr [esi] add ecx, edx add edi, ecx inc esi dec len .ENDW mov eax, ecx ;; DIVIDE AND CALCULATE COMPLETE SERIAL xor edx, edx mov ecx, 0FFF1h div ecx mov eax, edi mov ecx, edx mov edi, 0FFF1h xor edx, edx div edi mov eax, edx shl eax, 10h or eax, ecx invoke wsprintf, ADDR buffer2, ADDR sprint, eax invoke SetWindowText, hwndEdit3, ADDR buffer2 ;; set text of second edit field ;; Fill Editfield jmp weiter looser: invoke SetWindowText, hwndEdit3, ADDR least ;; "Enter at least one char" weiter: .ENDIF So, I can get extra points, if I patch the prog, so be sure I'll do that: We can make a quite easy one-byte patch like this: Unpatched: :1501475D C7842498130000FFFFFFFF mov dword ptr [esp+00001398], FFFFFFFF :15014768 3BC8 cmp ecx, eax :1501476A 8D8C24A0130000 lea ecx, dword ptr [esp+000013A0] :15014771 751E jne 15014791 Patched: :1501475D C7842498130000FFFFFFFF mov dword ptr [esp+00001398], FFFFFFFF :15014768 3BC8 cmp ecx, eax :1501476A 8D8C24A0130000 lea ecx, dword ptr [esp+000013A0] :15014771 7500 jne 15014773 III. BTW Greets to: Err, scary task, this time. So many ppl I want to greet her. Well, I greet everyone in #cracking4newbies and especially [INSERTYOURNAMEHERE]. (I hope everyone is satisfied, as I don't forget anyone in this way ;).