. . . . . . . . . . . . . . . . . . . . KeyGen Tutorial on mIRC 5.41 . . - . . (c) 1998 by Dynamite . . . . . . . . . . . . . . . . . . . . ( )Beginner (X)Intermediate ( )Advanced ( )Expert In this tut, we will get the information to write a keygen for the IRC client mIRC 5.41. The first time I worked on mIRC 5.41 I wondered, because the code was so clearly written. It was really fun to crack it :-) Because of the clearly written code (Thanks to Khaled Mardam-Bey) it is a nice target for a tut. Only one thing is very bad on mIRC. One time registered, you can't un-register it. I tried to delete it, but the registration was allready there. Maybe it keeps the registration info in the registry or in a file in the win95 dir. I don't know... For this tut you will need Soft-Ice 3.0+ and W32Dasm (I'm using v8.9). Run mIRC and select Register in the Help menu. Enter anything you want and press the register button. You're noting a msgbox poping up, which tells you, that your registration info was wrong. So let's set a BPX at MessageBoxA. Repeat the whole crap and you are in SIce, where the msgbox pops up. It is at :0043D29C. Write this address down and load MIRC32.EXE into W32Dasm. Once in W32Dasm, goto :0043D29C and then scroll up, until you find the next reference to a conditional jump. You'll find it at :0043D257. It comes from :0043D1C6. At :0043D1C6 you can see the following piece of code: :0043D1B5 68701E4D00 push 004D1E70 ; the s/n is pushed :0043D1BA 68B41B4D00 push 004D1BB4 ; the name is pushed :0043D1BF E844140500 call 0048E608 ; that's the check_function :0043D1C4 85C0 test eax, eax ; valid s/n entered? :0043D1C6 0F848B000000 je 0043D257 ; jump, when s/n was wrong Okay. Now we know where to search. We have to set a BPX at :0048E608. When I worked on mIRC, I traced through the whole check_function and all its sub-functions. Here follows the whole check_function well commented: * Referenced by a CALL at Addresses: |:0043D1BF , :0048E7CA | :0048E608 55 push ebp :0048E609 8BEC mov ebp, esp :0048E60B 53 push ebx :0048E60C 56 push esi :0048E60D 57 push edi :0048E60E 8B750C mov esi, dword ptr [ebp+0C] :0048E611 8B5D08 mov ebx, dword ptr [ebp+08] :0048E614 53 push ebx ; push name :0048E615 E8AE530200 call 004B39C8 ; get length of name :0048E61A 59 pop ecx :0048E61B 83F805 cmp eax, 00000005 ; the min. of chars is 5 :0048E61E 7304 jnb 0048E624 ; if name consist of 5 or more chars, jump. else leave func :0048E620 33C0 xor eax, eax :0048E622 EB6D jmp 0048E691 ; jump to end of function If you entered a name with 4 or less chars, then restart the whole process and enter a name with minimal 5 chars. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048E61E(C) | :0048E624 56 push esi :0048E625 53 push ebx :0048E626 E8FDFEFFFF call 0048E528 Here follows the function :0048E528. This is the main function of the whole protection, because it validates the s/n with the username. * Referenced by a CALL at Addresses: |:0048E626 , :0048E66E | :0048E528 55 push ebp :0048E529 8BEC mov ebp, esp :0048E52B 83C4F4 add esp, FFFFFFF4 :0048E52E 53 push ebx :0048E52F 56 push esi :0048E530 57 push edi :0048E531 8B750C mov esi, dword ptr [ebp+0C] ; move s/n to esi :0048E534 6A2D push 0000002D :0048E536 56 push esi ; push s/n :0048E537 E838540200 call 004B3974 ; check, if there is a "-" in the s/n :0048E53C 83C408 add esp, 00000008 :0048E53F 8BD8 mov ebx, eax :0048E541 85DB test ebx, ebx :0048E543 7507 jne 0048E54C ; jump if there is a "-" else leave function :0048E545 33C0 xor eax, eax :0048E547 E9B2000000 jmp 0048E5FE ; leave function You see, that your s/n have to contain a "-". If you didn't enter a "-" in your s/n then enter it now and restart the whole process. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048E543(C) | :0048E54C C60300 mov byte ptr [ebx], 00 ; replace the "-" in the s/n with a '00' byte :0048E54F 56 push esi ; push s/n :0048E550 E807A80200 call 004B8D5C ; check if the s/n consists of numbers :0048E555 59 pop ecx :0048E556 8945FC mov dword ptr [ebp-04], eax :0048E559 C6032D mov byte ptr [ebx], 2D :0048E55C 43 inc ebx :0048E55D 803B00 cmp byte ptr [ebx], 00 :0048E560 7507 jne 0048E569 :0048E562 33C0 xor eax, eax :0048E564 E995000000 jmp 0048E5FE * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048E560(C) | :0048E569 53 push ebx :0048E56A E8EDA70200 call 004B8D5C :0048E56F 59 pop ecx :0048E570 8945F8 mov dword ptr [ebp-08], eax :0048E573 8B4508 mov eax, dword ptr [ebp+08] :0048E576 50 push eax ; push name :0048E577 E84C540200 call 004B39C8 ; get length of name :0048E57C 59 pop ecx :0048E57D 8945F4 mov dword ptr [ebp-0C], eax :0048E580 33C0 xor eax, eax :0048E582 33DB xor ebx, ebx :0048E584 BA03000000 mov edx, 00000003 :0048E589 8B4D08 mov ecx, dword ptr [ebp+08] ; move name to ecx :0048E58C 83C103 add ecx, 00000003 :0048E58F 3B55F4 cmp edx, dword ptr [ebp-0C] :0048E592 7D1C jge 0048E5B0 Okay. Here follows a very interesting part of the protection. All chars of the name (excluded the first three chars) are multiplicated with special digits out of a table. The outcome is added to a checksum in EBX. You can look into the actual position of the table, if you enter 'D 4*eax+004CCB30' in SIce. To ease your cracking work, I'll give you the table. Num_of_char Digit --------------------- 4 0B 5 06 6 11 7 0C 8 0C 9 0E 10 05 11 0C 12 10 13 0A 14 0B 15 06 16 0E 17 0E 18 04 19 0B 20 06 21 0E 22 0E 23 04 24 0B 25 09 26 0C 27 0B 28 0A 29 08 30 0A 31 0A 32 10 33 08 34 04 35 06 36 0A 37 0C 38 10 39 08 40 0A 41 04 42 10 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048E5AE(C) | :0048E594 0FB631 movzx esi, byte ptr [ecx] ; move next char of name to esi :0048E597 0FAF348530CB4C00 imul esi, dword ptr [4*eax+004CCB30] ; multiplicate the char with one digit out of the table :0048E59F 03DE add ebx, esi ; add the outcome to EBX :0048E5A1 40 inc eax ; next char :0048E5A2 83F826 cmp eax, 00000026 :0048E5A5 7E02 jle 0048E5A9 :0048E5A7 33C0 xor eax, eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048E5A5(C) | :0048E5A9 42 inc edx :0048E5AA 41 inc ecx :0048E5AB 3B55F4 cmp edx, dword ptr [ebp-0C] ; end of string? :0048E5AE 7CE4 jl 0048E594 ; jump if not end of str * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048E592(C) | :0048E5B0 3B5DFC cmp ebx, dword ptr [ebp-04] ; compare checksum (ebx) with the first part of the s/n. :0048E5B3 7404 je 0048E5B9 ; jump when they match :0048E5B5 33C0 xor eax, eax :0048E5B7 EB45 jmp 0048E5FE ; else leave function At :0048E5B0 the checksum (EBX) is compared with the first part of the s/n. The first part of the s/n is the part before the "-". Here follows an example: If you entered "Dynamite" in the name box, you have to calculate as follows: ---------------------------------------- | Name: D y n a m i t e | | Number: 1 2 3 4 5 6 7 8 | | Digits: 0B 06 11 0C 0C | ---------------------------------------- CheckSum = (61 * 0B) + (6D * 06) + (69 * 11) + (74 * 0C) + (65 * 0C) CheckSum = 0x17DE Now you only have to convert 0x17DE to decimal number. 0x17DE ==> 6110 So the first part of your serial is '6110-'. The second part of the serial is calculated some lines below. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048E5EE(C) | :0048E5CD 0FB631 movzx esi, byte ptr [ecx] ; move one char to esi :0048E5D0 0FB679FF movzx edi, byte ptr [ecx-01] ; move one char before to edi :0048E5D4 0FAFF7 imul esi, edi ; multiplicate the 2 chrs :0048E5D7 0FAF348530CB4C00 imul esi, dword ptr [4*eax+004CCB30] ; multiplicate the outcome with the numbers out of the table :0048E5DF 03DE add ebx, esi ; add outcome to checksum in EBX :0048E5E1 40 inc eax :0048E5E2 83F826 cmp eax, 00000026 :0048E5E5 7E02 jle 0048E5E9 :0048E5E7 33C0 xor eax, eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048E5E5(C) | :0048E5E9 42 inc edx :0048E5EA 41 inc ecx :0048E5EB 3B55F4 cmp edx, dword ptr [ebp-0C] ; end of string? :0048E5EE 7CDD jl 0048E5CD ; jump if not end of str * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048E5CB(C) | :0048E5F0 3B5DF8 cmp ebx, dword ptr [ebp-08] ; compare checksum with second part of the s/n :0048E5F3 7404 je 0048E5F9 ; jump when they match :0048E5F5 33C0 xor eax, eax :0048E5F7 EB05 jmp 0048E5FE ; else leave This second calculation multiplicates every char of the name with the char before the char. The outcoming of this multiplication is multiplicated with a number out of the tabel. To get the second part of the s/n you have to calculate as follows: ---------------------------------------- | Name: D y n a m i t e | | Number: 1 2 3 4 5 6 7 8 | | Digits: 0B 06 11 0C 0C | ---------------------------------------- CheckSum = (('a' * 'n') * 0B) + (('m' * 'a') * 06) + (('i' * 'm') * 11) + (('t' * 'i') * 0C) + (('e' * 't') * 0C) CheckSum = 0xA1A6D Just convert it to a decimal number and you get 662125. Now you have to put the two parts together and you got the valid s/n for 'Dynamite'. name: Dynamite s/n: 6110-662125 I think you got all information to write a keygen for mIRC 5.41. I hope you enjoyed this tut by me. Sorry for my bad english :] Greetz are going to he +HCU and to NiKai ;-) (c) 1998 by Dynamite