Tutorial for Falcon's 7th Crackme --------------------------------- By TSCube --------- +---------+ |1. Intro | +---------+ This crackme involves math/logic stuff, so if math makes you want to bump your head against the wall, don't even try this crackme. +----------------------------------------+ | 2. Understanding the protection scheme | +----------------------------------------+ Enter any serial, 'BPX HMEMCPY', then click on "Check it" -> SICE POPS. Now disable the breakpoint, hit F12 until you're back in the main code of the crackme and trace with F10 : :00456976 8B45FC mov eax, dword ptr [ebp-04] -> entered_serial :00456979 E8FED1FAFF call 00403B7C -> strlen :0045697E 83F804 cmp eax, 00000004 -> The serial must have at least 5 digits :00456981 7E3D jle 004569C0 :00456983 803D2498450000 cmp byte ptr [00459824], 00 :0045698A 7514 jne 004569A0 -> jump if first serial is correct :0045698C 8B45FC mov eax, dword ptr [ebp-04] -> entered_serial :0045698F E868FFFFFF call 004568FC -> serial check routine #1 :00456994 83F902 cmp ecx, 00000002 :00456997 7507 jne 004569A0 :00456999 C6052498450001 mov byte ptr [00459824], 01 -> first serial is correct * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0045698A(C), :00456997(C) | :004569A0 FE0525984500 inc byte ptr [00459825] :004569A6 803D2498450001 cmp byte ptr [00459824], 01 :004569AD 7511 jne 004569C0 -> fuck off if first serial is not correct :004569AF 803D2598450002 cmp byte ptr [00459825], 02 :004569B6 7508 jne 004569C0 :004569B8 8B45FC mov eax, dword ptr [ebp-04] -> entered_serial :004569BB E8FCFEFFFF call 004568BC -> serial check routine #2 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00456981(C), :004569AD(C), :004569B6(C) | :004569C0 33C0 xor eax, eax :004569C2 5A pop edx :004569C3 59 pop ecx :004569C4 59 pop ecx :004569C5 648910 mov dword ptr fs:[eax], edx :004569C8 68DD694500 push 004569DD * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004569DB(U) | :004569CD 8D45FC lea eax, dword ptr [ebp-04] :004569D0 E827CFFAFF call 004038FC :004569D5 C3 ret The above listing uses 2 values stored at [00459824] and [00459825]. In case you have not noticed it yet, the user must enter *2* different serials... the algo works like this : IF first_serial is not valid check first_serial END IF IF first_serial is valid IF it's the second time user clicks on "Check it" check_second_serial and display "registered" if it's correct END IF END IF +-----------------------------+ | 3. Finding the first serial | +-----------------------------+ The first serial check routine is at @4568FC. When the proggy returns from this function, ECX=2 if the first serial is correct * Referenced by a CALL at Address: |:0045698F | :004568FC 55 push ebp :004568FD 31C9 xor ecx, ecx :004568FF 8D30 lea esi, dword ptr [eax] :00456901 83C604 add esi, 00000004 :00456904 BB998F3337 mov ebx, 37338F99 } :00456909 AD lodsd } :0045690A 31C3 xor ebx, eax } :0045690C C1C330 rol ebx, 30 } :0045690F 81F300009999 xor ebx, 99990000 } :00456915 C1CB50 ror ebx, 50 } FIRST PART OF CHECK ROUTINE :00456918 83EE08 sub esi, 00000008 } :0045691B AD lodsd } :0045691C 01C3 add ebx, eax } :0045691E 81F399999999 xor ebx, 99999999 } :00456924 C1C370 rol ebx, 70 } :00456927 81FB75533D53 cmp ebx, 533D5375 } :0045692D 751C jne 0045694B -> fuck off :0045692F 41 inc ecx :00456930 83EE04 sub esi, 00000004 :00456933 AD lodsd } :00456934 89C3 mov ebx, eax } :00456936 AD lodsd } :00456937 C1CB80 ror ebx, 80 } SECOND PART OF CHECK ROUTINE :0045693A 81F399990000 xor ebx, 00009999 } :00456940 31C3 xor ebx, eax } :00456942 81FB998F3337 cmp ebx, 37338F99 } :00456948 7501 jne 0045694B -> fuck off :0045694A 41 inc ecx -> ECX=2 (first serial is correct) * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0045692D(C), :00456948(C) | :0045694B 5D pop ebp :0045694C C3 ret 3.1. the LODSD operand ====================== LODSD copies the DWORD pointed by ESI into EAX and adds 4 to ESI exemple ------- 401000 : 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 if ESI = 401000 then after this sequence : LOSDD MOV EBX,EAX LODSD you'll have EBX = 0x04030201 and EAX=0x08070605 3.2. The first part of the serial check routine =============================================== It uses LOSDD 2 times, so we can conclude that the first serial has 8 digits. From now, I will call serial_11 and serial_12 the two parts if the serial. exemple : if serial = "12345678" then serial_11 = 0x34333231 and serial_12 = 0x38373635 (because 0x31 is ASCII value of '1') OK, here is what the first part does (I hope the syntax is clear ;) : EBX = 0x37338F99 XOR serial_12 EBX = EBX ROL 0x30 EBX = EBX XOR 0x99990000 EBX = EBX ROR 0x50 EBX = EBX + serial_11 EBX = EBX XOR 0x99999999 EBX = EBX ROL 0x70 EBX = 0x533D5375 ??? in one line : ((((((0x37338F99 XOR serial_12) ROL 0x30) XOR 0x99990000) ROR 0x50) + serial_11) XOR 0x99999999) ROL 0x70 = 0x533D5375 ? 3.3. The second part of the serial check routine ================================================ EBX = serial_11 ROR 0x80 EBX = EBX XOR 0x9999 EBX = EBX XOR serial_12 EBX = 0x37338F99 ??? in one line : ((serial_11 ROR 0x80) XOR 0x9999) XOR serial_12 = 0x37338F99 ??? 3.4. Let's find serial_11 and serial_12 ! ========================================= We got 2 equations to solve : 1) ((((((0x37338F99 XOR serial_12) ROL 0x30) XOR 0x99990000) ROR 0x50) + serial_11) XOR 0x99999999) ROL 0x70 = 0x533D5375 2) ((serial_11 ROR 0x80) XOR 0x9999) XOR serial_12 = 0x37338F99 A few things to know before going on : X XOR Y = Z <=> X = Z XOR Y X ROL Y = Z <=> X = Z ROR Y X ROR Y = Z <=> X = Z ROL Y now we can rewrite the first equation : 1) ((((0x37338F99 XOR serial_12) ROL 0x30) XOR 0x99990000) ROR 0x50) + serial_11 = (0x533D5375 ROR 0x70) XOR 0x99999999 which gives : 1) ((((0x37338F99 XOR serial_12) ROL 0x30) XOR 0x99990000) ROR 0x50) + serial_11 = 0xCAECCAA4 The 2 equations are now : 1) ((((0x37338F99 XOR serial_12) ROL 0x30) XOR 0x99990000) ROR 0x50) + serial_11 = 0xCAECCAA4 2) ((serial_11 ROR 0x80) XOR 0x9999) XOR serial_12 = 0x37338F99 We can notice the same 0x37338F99 value in the two equations, let's isolate it in 1) : 1) ((((0xCAECCAA4 - serial_11) ROL 0x50) XOR 0x99990000) ROR 0x30) XOR serial_12 = 0x37338F99 2) ((serial_11 ROR 0x80) XOR 0x9999) XOR serial_12 = 0x37338F99 Now we transpose the 'XOR serial_12' : 1) (((0xCAECCAA4 - serial_11) ROL 0x50) XOR 0x99990000) ROR 0x30 = 0x37338F99 XOR serial_12 2) (serial_11 ROR 0x80) XOR 0x9999 = 0x37338F99 XOR serial_12 hey, the two right members are the same !!! This means that the left members are equal : 3) (((0xCAECCAA4 - serial_11) ROL 0x50) XOR 0x99990000) ROR 0x30 = (serial_11 ROR 0x80) XOR 0x9999 Now can erase the 'ROR 0x80' because it does NOTHING ! (if you rotate a 32 bits value 32*X times, the value doesn't change and of course 0x80 = 128 = 4*32) 3) (((0xCAECCAA4 - serial_11) ROL 0x50) XOR 0x99990000) ROR 0x30 = serial_11 XOR 0x9999 We transpose again : 3) 0xCAECCAA4 - serial_11 = (((serial_11 XOR 0x9999) ROL 0x30) XOR 0x99990000) ROR 0x50 As curious as it may seem, 'X XOR 0x9999 ROL 0x30 XOR 0x99990000 ROR 0x50 = 'X !!! (you can check by yourself if you don't believe me) 3) 0xCAECCAA4 - serial_11 = serial_11 Finally : ==> serial_11 = 0xCAECCAA4 / 2 = 0x65766552 Let's find serial_12 : 2) ((serial_11 ROR 0x80) XOR 0x9999) XOR serial_12 = 0x37338F99 ROR 0x80 does nothing : 2) (serial_11 XOR 0x9999) XOR serial_12 = 0x37338F99 Finally : 2) serial_12 = (serial_11 XOR 0x9999) XOR 0x37338F99 Which gives : ==> serial_12 = 0x52457352 3.5. The first serial ===================== serial_11 = 0x65766552 serial_12 = 0x52457352 the ASCII values of the serial are : 0x52 0x65 0x76 0x65 0x52 0x73 0x45 0x52 'R' 'e' 'v' 'e' 'R' 's' 'E' 'R' First serial = ReveRsER +------------------------------+ | 4. Finding the second serial | +------------------------------+ The first serial check is routine is at @4568BC * Referenced by a CALL at Address: |:004569BB | :004568BC 89C6 mov esi, eax :004568BE 31DB xor ebx, ebx :004568C0 31D2 xor edx, edx :004568C2 BB66563412 mov ebx, 12345666 :004568C7 AD lodsd :004568C8 81F312505500 xor ebx, 00555012 } :004568CE C1C350 rol ebx, 50 } :004568D1 F7D3 not ebx } :004568D3 31C3 xor ebx, eax } FIRST PART :004568D5 F7D3 not ebx } :004568D7 81C3636C6146 add ebx, 46616C63 } :004568DD 81FB85CC768B cmp ebx, 8B76CC85 } :004568E3 7514 jne 004568F9 -> fuck off :004568E5 AD lodsd } :004568E6 F7D0 not eax } :004568E8 2D00000050 sub eax, 50000000 } SECOND PART :004568ED 3D9496B168 cmp eax, 68B19694 } :004568F2 7505 jne 004568F9 -> fuck off :004568F4 E877FFFFFF call 00456870 -> registered !!! * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004568E3(C), :004568F2(C) | :004568F9 C3 ret :004568FA 8BC0 mov eax, eax OK, this one is easier, let's do it quickly : 4.1. First part =============== EBX = NOT(12345666 XOR 0x555012 ROL 0x50) = 0xF98BED9E EBX = EBX XOR serial_21 EBX = NOT(EBX) EBX = EBX + 0x46616C63 EBX = 0x8B76CC85 ??? => NOT(NOT(12345666 XOR 0x555012 ROL 0x50) XOR serial_21) + 0x0x46616C63 = 0x8B76CC85 ??? => NOT(0xF98BED9E XOR serial_21) = 0x45156022 => 0xF98BED9E XOR serial_21 = 0xBAEA9FDD XOR 0xF98BED9E => serial_21 = 0x43617243 4.2. Second part ================ EAX = serial_22 EAX = NOT(EAX) EAX = EAX-0x50000000 EAX = 0x68B19694 ? => NOT(serial_22) - 0x50000000 = 0x68B19694 => serial_22 = 0x474E696B 4.3. The second serial ====================== serial_21 = 0x43617243 serial_22 = 0x474E696B the ASCII values of the serial are : 0x43 0x72 0x61 0x43 0x6B 0x69 0x4E 0x47 'C' 'r' 'a' 'C' 'k' 'i' 'N' 'G' Second serial = CraCkiNG +---------+ |5. Outro | +---------+ Thx to Falcon for an interesting crackme Update (19/06/2000) : hey nh, next time you make a tut, check first if your serial is correct ;) ________ _______ _______ /__ __/\ / ____/\ / ____/\ \_/ /\_\/ / /\___\/ / /\___\/ / / / / /_/_ / / / / / / /____ /\ / / / / / / \___/ / / / / / / / / ____/ / / / /_/_ / / / /_____/ / /______/\ \__\/ \_____\/ \______\/ 01/06/2000 www.tscube.cjb.net