Well, I publish this essay because I think that it is a very good introduction,
for beginners, to some simple reversing methods and
techniques that can be applied for OTHER PURPOSES, and not only
in order to build keygenerators... funny: I still do not understand, after all these years,
why people actually WRITE keygenerators... I mean, I understand if someone wants
to use an application without limits whatsoever
before registering it, but it beats me why on the
world a good reverser may enjoy giving ready made cracks to the drooling
zombies... instead of helping
them to grow up and learn reversing themselves.
Anyway I believe that the knowledge that
beginners will gain reading this essay may
also be used, on a much more higher level, in order
to understand, modify and ameliorate alien (or long-forgotten) code snippets.
Programmers beware: MisterE is quite right: if you have to write somewhere a warning
like "DO NOT REMOVE FILE SO-AND-SO" try to do it (dynamically of course) only AFTER the
client has registered, duh.
Now, please: no more keygenerator essays (that is, unless you have really found something new, which I doubt you will do writing keygens :-)
Hi Fravia I read your comments added to my essay (www.fravia.org/mre2.htm). I disagree with you that keygens aren't useful. If one wants to create a keygen, one has to FULLY UNDERSTAND the protection scheme. One can use this knowledge to discover new breakpoints (like lstrlenA for getting the length of a serial, which is the only breakpoint that works well on the GoScreen programs). And even if one understands the protection scheme, it is also a challenge (the reason why I crack) to write a generator. I also disagree with you that cracks shouldn't be published (for the zombies). OK, i also dislike the people who only COLLECT serials, just for the fun of it. But maybe that reverse engineers (like us), will only survive IF those crack are published. Imagine: if those cracks wouldn't be published, the cracking scene and the whole reverse engineering wouldn't be known to anyone. Most crackers would just be alone, not sharing other thoughts with other crackers, not in a group, cracking just for themselves. I think it is much more fun to be in a group (cracking groups or channels, like #cracking4newbies, etc.) sharing thoughts, and helping each other out. Second thought: Crackers like to damage Micro$oft, how can they do that if they don't publish cracks targeted on specific M$ products? Another thought: +ORC said in one of his tutorials: The best things should be free. But even that isn't completely true, cause he also said: Give a crack to a man, and he'll be hungry tomorrow, teach him how to crack and he'll never be hungry again. So +ORC actually means this: The best things should be free... for those who want to work for them. Sound fair to me. Waiting for your reply, MisterE MisterE@freemail.nl BTW: it's a combination of giving out cracks, and helping people. I do both.
Keyfiles: WinCtrl v1.41 Written by MisterE 10th August 1998 .----------------------------------------------------------------------------------------------. | Introduction | `----------------------------------------------------------------------------------------------' Hello, here's another essay about keyfiles. You might want to check out my other essay about keyfiles. In this essay i'll try to show most problems that can occur when you want to reconstruct a keyfile. In order to do this we'll make a keyfile generator, quite a challenge, eh? We'll even protect our generator from lamers, using the programs protection scheme!! .----------------------------------------------------------------------------------------------. | Programs | `----------------------------------------------------------------------------------------------' Programs I have used: - SoftIce V3.22 - W32dasm V8.9 - A Hexeditor (HexWorkShop v2.54) - WinCtrl v1.41 (http://titan.fpz.hr/~foetus) - not necessary: Filemon .----------------------------------------------------------------------------------------------. | Getting the name of the keyfile | `----------------------------------------------------------------------------------------------' When you want to download WinCtrl from the url above, you get a warning: DO NOT REMOVE WINCTRL.KEY How stupid...they could better have said that when you REALLY registered. Anyway, if had run good ol' Filemon, you would have seen the program trying to access winctrl.key. .----------------------------------------------------------------------------------------------. | Getting a working keyfile | `----------------------------------------------------------------------------------------------' First I want you to create a keyfile using your Hexeditor. Make the keyfile look this way: 00000000 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000010 1000 0000 0000 0000 0000 0000 0000 0000 ................ 00000020 2000 0000 0000 0000 0000 0000 0000 0000 ............... 00000030 3000 0000 0000 0000 0000 0000 0000 0000 0............... We're going to place a breakpoint (using SoftIce) at CreateFileA, looking in your win32.hlp, you get this information on the 16-bit version of this API: The CreateFile function creates, opens, or truncates a file, pipe, communications resource, disk device, or console. It returns a handle that can be used to access the object. It can also open and return a handle to a directory. HANDLE CreateFile( LPCTSTR lpFileName, // address of name of the file DWORD dwDesiredAccess, // access (read-write) mode DWORD dwShareMode, // share mode LPSECURITY_ATTRIBUTES lpSecurityAttributes, // address of security descriptor DWORD dwCreationDistribution, // how to create DWORD dwFlagsAndAttributes, // file attributes HANDLE hTemplateFile // handle of file with attributes to copy ); These value's (lpFileName, dwDesiredAccess, etc.) get pushed in reverse order in assembly, so you will have to look at the last value that get's pushed to get to know the filename. Now fire SoftIce, place a breakpoint at CreateFileA and look for winctrl.key. :004042DE 6A00 push 00000000 :004042E0 6880000000 push 00000080 :004042E5 51 push ecx :004042E6 6A00 push 00000000 :004042E8 52 push edx :004042E9 50 push eax :004042EA 8D4348 lea eax, dword ptr [ebx+48] <== put address of filename in eax :004042ED 50 push eax <== push it! :004042EE E83DCFFFFF Call kernel32!CreateFileA :004042F3 83F8FF cmp eax, FFFFFFFF <== Does the file exist? :004042F6 7429 je 00404321 <== If not..jump The 5th time you get get back into SoftIce (CreateFileA) is the one where WinCtrl looks for the keyfile, you can check this by looking at the contents of [ebx+48]. Now do some tracing...until you arrive here: :00437ABE E871C8FCFF call 00404334 <== Check if winctrl.key exists :00437AC3 E850ACFCFF call 00402718 :00437AC8 85C0 test eax, eax :00437ACA 0F85D1010000 jne 00437CA1 <== If so, continue checking :00437AD0 8BD7 mov edx, edi :00437AD2 8D85B0FEFFFF lea eax, dword ptr [ebp+FFFFFEB0] :00437AD8 E8E7C6FCFF call 004041C4 <== Important call I :00437ADD E836ACFCFF call 00402718 <== Not important :00437AE2 85C0 test eax, eax <== check something... :00437AE4 0F85A7010000 jne 00437C91 Now trace into the first important call until you arrive at a call to ReadFile: :004041D7 6A00 push 00000000 :004041D9 8BC4 mov eax, esp :004041DB 6A00 push 00000000 :004041DD 50 push eax :004041DE FF7308 push [ebx+08] <== [ebx+08] = E8h :004041E1 52 push edx :004041E2 FF33 push dword ptr [ebx] * Reference To: kernel32.ReadFile, Ord:0000h | :004041E4 E887D0FFFF Call 00401270 <== call ReadFile :004041E9 5A pop edx :004041EA 48 dec eax :004041EB 7507 jne 004041F4 :004041ED 3B5308 cmp edx, dword ptr [ebx+08] :004041F0 7515 jne 00404207 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004041FE(U) | :004041F2 5B pop ebx :004041F3 C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004041F0(C) | :00404207 B864000000 mov eax, 00000064 :0040420C EBEB jmp 004041F9 OK, this part gave me some problems. Remember the 'text eax, eax' at 00437AE2? Every time eax had the value 64h, which caused me to get an unregistered message.... Now I'll show you again how important your win32.hlp is. Here's some information about ReadFile: The ReadFile function reads data from a file, starting at the position indicated by the file pointer. After the read operation has been completed, the file pointer is adjusted by the number of bytes actually read, unless the file handle is created with the overlapped attribute. If the file handle is created for overlapped input and output (I/O), the application must adjust the position of the file pointer after the read operation. BOOL ReadFile( HANDLE hFile, // handle of file to read LPVOID lpBuffer, // address of buffer that receives data DWORD nNumberOfBytesToRead, // number of bytes to read LPDWORD lpNumberOfBytesRead, // address of number of bytes read LPOVERLAPPED lpOverlapped // address of structure for data ); Now take a look at the 'nNumberOfBytesToRead' ... The program wants to read E8h bytes, and take a look at the size of our keyfile. You see it? Our keyfile is 40h bytes long and the program wants to read E8h bytes...Let's solve that! Change your keyfile till it looks like this: 00000000 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000010 1000 0000 0000 0000 0000 0000 0000 0000 ................ 00000020 2000 0000 0000 0000 0000 0000 0000 0000 ............... 00000030 3000 0000 0000 0000 0000 0000 0000 0000 0............... 00000040 4000 0000 0000 0000 0000 0000 0000 0000 @............... 00000050 5000 0000 0000 0000 0000 0000 0000 0000 P............... 00000060 6000 0000 0000 0000 0000 0000 0000 0000 `............... 00000070 7000 0000 0000 0000 0000 0000 0000 0000 p............... 00000080 8000 0000 0000 0000 0000 0000 0000 0000 ................ 00000090 9000 0000 0000 0000 0000 0000 0000 0000 ................ 000000A0 A000 0000 0000 0000 0000 0000 0000 0000 ................ 000000B0 B000 0000 0000 0000 0000 0000 0000 0000 ................ 000000C0 C000 0000 0000 0000 0000 0000 0000 0000 ................ 000000D0 D000 0000 0000 0000 0000 0000 0000 0000 ................ 000000E0 E000 0000 0000 0000 0000 0000 0000 0000 ................ Now continue tracing...You should pass the 'test eax, eax' this time. :00437AE2 85C0 test eax, eax <== we have just passed this one :00437AE4 0F85A7010000 jne 00437C91 :00437AEA 8D85A8FDFFFF lea eax, dword ptr [ebp+FFFFFDA8] :00437AF0 8BD7 mov edx, edi ... some calls... and some more calls... :00437BBF 8D95ACFDFFFF lea edx, dword ptr [ebp+FFFFFDAC] :00437BC5 8D87C2000000 lea eax, dword ptr [edi+000000C2] :00437BCB B120 mov cl, 20 :00437BCD E81EADFCFF call 004028F0 :00437BD2 33DB xor ebx, ebx :00437BD4 8A1F mov bl, byte ptr [edi] :00437BD6 85DB test ebx, ebx <== another test... :00437BD8 7E10 jle 00437BEA OK, by now you should have noticed that must NOT jump in order to get the right keyfile... So at 00437BD6 ebx should be 1 or higher... If you take a look at [edi] you see a part of our keyfile. [edi] contains the first byte, which happens to be 00h, change it to 10h, and continue. The program is reading the 2nd till the 11th byte of our keyfile...you might already guess what this is for. We'll get back at it later. Continue tracing. :00437BDD 55 push ebp :00437BDE 8A06 mov al, byte ptr [esi] :00437BE0 E807FEFFFF call 004379EC <== a call...we'll get back at it :00437BE5 59 pop ecx LATER! :00437BE6 46 inc esi :00437BE7 4B dec ebx :00437BE8 75F3 jne 00437BDD * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00437BD8(C) | :00437BEA 33DB xor ebx, ebx :00437BEC 8A5F41 mov bl, byte ptr [edi+41] <== load the 41st in bl :00437BEF 85DB test ebx, ebx <== guess what? Another test... :00437BF1 7E10 jle 00437C03 :00437BF3 8D7742 lea esi, dword ptr [edi+42] This test is unimportant (for now) just check if the 41th (hex) byte has the value 00h. At 00437C0B you get the same thing. Continue tracing. :00437C32 668B87E6000000 mov ax, word ptr [edi+000000E6] <== get the E6th byte :00437C39 66C1E808 shr ax, 08 <== divide e6th byte by 100h :00437C3D E8AAFDFFFF call 004379EC <== again that call.. :00437C42 59 pop ecx :00437C43 8A87E3000000 mov al, byte ptr [edi+000000E3] <== get the E3rd byte :00437C49 3A45FF cmp al, byte ptr [ebp-01] <== compare them I! :00437C4C 750B jne 00437C59 :00437C4E 8A87E4000000 mov al, byte ptr [edi+000000E4] <== get the E3rd byte :00437C54 3A45FE cmp al, byte ptr [ebp-02] <== compare them II! :00437C57 7404 je 00437C5D * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00437C4C(C) | :00437C59 33DB xor ebx, ebx :00437C5B EB02 jmp 00437C5F * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00437C57(C) | :00437C5D B301 mov bl, 01 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00437C5B(U) | :00437C5F 84DB test bl, bl :00437C61 742E je 00437C91 OK, let's go on. First change your keyfile, add e0, e1 ... e9. At 00437C49 yo can see a CMP. The E3rd byte should be 22h, some lines below we see another check..the E4th byte should be F7h. By now our keyfile should look like this: 00000000 1000 0000 0000 0000 0000 0000 0000 0000 ................ 00000010 1000 0000 0000 0000 0000 0000 0000 0000 ................ 00000020 2000 0000 0000 0000 0000 0000 0000 0000 ............... 00000030 3000 0000 0000 0000 0000 0000 0000 0000 0............... 00000040 4000 0000 0000 0000 0000 0000 0000 0000 @............... 00000050 5000 0000 0000 0000 0000 0000 0000 0000 P............... 00000060 6000 0000 0000 0000 0000 0000 0000 0000 `............... 00000070 7000 0000 0000 0000 0000 0000 0000 0000 p............... 00000080 8000 0000 0000 0000 0000 0000 0000 0000 ................ 00000090 9000 0000 0000 0000 0000 0000 0000 0000 ................ 000000A0 A000 0000 0000 0000 0000 0000 0000 0000 ................ 000000B0 B000 0000 0000 0000 0000 0000 0000 0000 ................ 000000C0 C000 0000 0000 0000 0000 0000 0000 0000 ................ 000000D0 D000 0000 0000 0000 0000 0000 0000 0000 ................ 000000E0 E0E1 E222 F7E5 E6E7 E8E9 0000 0000 0000 ..."............ DISABLE, DO NOT CLEAR, your breakpoints and continue running the program. You might have to close to program completely (using CTRL-ALT-DEL or close the icon right-under on your screen). Now we can see it: WE'RE REGISTERED!!! But you might think: Didn't you said we would create a keyfile generator??? Yes, that's right! So read on.... .----------------------------------------------------------------------------------------------. | Getting a Keyfile-Generator | | part 1: decryption | `----------------------------------------------------------------------------------------------' OK, let's start here: :00437AE2 85C0 test eax, eax <= the 'readfile' test :00437AE4 0F85A7010000 jne 00437C91 :00437AEA 8D85A8FDFFFF lea eax, dword ptr [ebp+FFFFFDA8] :00437AF0 8BD7 mov edx, edi :00437AF2 E8EDB9FCFF call 004034E4 <= This call isn't important :00437AF7 8B85A8FDFFFF mov eax, dword ptr [ebp+FFFFFDA8] :00437AFD 8D8DACFEFFFF lea ecx, dword ptr [ebp+FFFFFEAC] :00437B03 668B97E6000000 mov dx, word ptr [edi+000000E6] :00437B0A E81DF5FFFF call 0043702C <= Very important call!!! Now check out that important call... * Referenced by a CALL at Addresses: |:00437B0A , :00437B55 , :00437BA4 , :004388CA , :004388E5 |:00438900 , :00438919 , :00445113 | :0043702C 55 push ebp :0043702D 8BEC mov ebp, esp :0043702F 83C4F0 add esp, FFFFFFF0 :00437032 53 push ebx .... boring stuff... only thing that happens is that esi gets the value of the E6-th byte + E7-th byte * 256 here starts the interesting part: :0043706A 8D45F0 lea eax, dword ptr [ebp-10] :0043706D 33D2 xor edx, edx :0043706F 8AD3 mov dl, bl :00437071 8A5417FF mov dl, byte ptr [edi+edx-01] <= HERE,load a byte from .key :00437075 0FB7CE movzx ecx, si <= Put word of esi into ecx :00437078 C1E908 shr ecx, 08 :0043707B 32D1 xor dl, cl <= perform decryption :0043707D E852C4FCFF call 004034D4 <= start unimportant stuff :00437082 8B55F0 mov edx, dword ptr [ebp-10] :00437085 8D45F8 lea eax, dword ptr [ebp-08] :00437088 E8B3C4FCFF call 00403540 <= end unimportant stuff :0043708D 33C0 xor eax, eax :0043708F 8AC3 mov al, bl :00437091 0FB64407FF movzx eax, byte ptr [edi+eax-01] <= load byte from the keyfile :00437096 6603F0 add si, ax <= do some arithmetic :00437099 6669C66DCE imul ax, si, CE6D :0043709E 6605BF58 add ax, 58BF :004370A2 8BF0 mov esi, eax :004370A4 43 inc ebx :004370A5 FE4DF7 dec [ebp-09] <= decrease counter =1st byte :004370A8 75C0 jne 0043706A <= did all decryption? |.key OK, this needs some explaination. This part is a decryption routine. It decrypts your name. Like this: 00000000 0700 4804 4CE0 BAB0 0000 0000 0000 0000 ..H.L........... (encrypted) to 00000000 074D 6973 7465 7245 0000 0000 0000 0000 .MisterE........ (decrypted) Now lets me try to explain the decrytion routine, starting from :00437071. Esi is our en/decrytion-key. It is the value of the E6-th byte + E7-th byte * 256. Using our old keyfile, esi would be: E7E6h. The program loads a byte from our keyfile. After that ecx := esi. Then shift right ecx 8 times to get ch (This only applies to this program). Now comes the most IMPORTANT part of the decryption scheme: The decryption. By XORRING dl, cl you get the decrypted value in dl!!! OK, some calls follow. These are MOSTLY unimportant, somewhere in these call, the program arranges your decrypted name, like the example above. Some comments about :004370A5: [ebp-9] checks if the program has decrypted all the letters of the name. If not, the program jumps to 0043706A to decrypt the next letter. .----------------------------------------------------------------------------------------------. | Getting a Keyfile-Generator | | part 2: checksum | `----------------------------------------------------------------------------------------------' This is gonna be quite easy. The program uses a kind of a checksum. The checksum gets called at: * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00437BE8(C) | :00437BDD 55 push ebp :00437BDE 8A06 mov al, byte ptr [esi] <= put byte from the keyfile in al :00437BE0 E807FEFFFF call 004379EC <= call checksum routine :00437BE5 59 pop ecx :00437BE6 46 inc esi <= point to next byte :00437BE7 4B dec ebx <= ebx is the length of the name :00437BE8 75F3 jne 00437BDD <= Did we do all the chars? * Referenced by a CALL at Addresses: |:00437BE0 , :00437BF9 , :00437C18 , :00437C2B , :00437C3D | :004379EC 55 push ebp :004379ED 8BEC mov ebp, esp :004379EF 8B5508 mov edx, dword ptr [ebp+08] <= point to address of checksum :004379F2 0042FF add byte ptr [edx-01], al <= add byte to checksum1 :004379F5 8B5508 mov edx, dword ptr [ebp+08] :004379F8 3042FE xor byte ptr [edx-02], al <= xor checksum2 by al :004379FB 5D pop ebp :004379FC C3 ret OK, here's some explaination. Let's use this for an example: 00000000 0700 4804 4CE0 BAB0 0000 0000 0000 0000 ..H.L........... (encrypted) First the program puts 00h(2nd byte) in al. Then adds it to the value in [edx-1] (checksum1). After that the program does a 'xor [edx-2], al' (checksum2). Note the start-values for checksum1(start-value = A5h) and checksum2(start-value = 5Ah). Ok, the program repeats this until it has done all the characters. These checksums occur some more times most are unimportant. They occur at 437BE9, 437C18 and 437C2B. The first 2 mentioned are unimportant as long as the 41h and the C2h byte are 00h. The last one is important, because the program will ALWAYS use the E6th byte to perform a checksum on it. Oh, yeah. I almost forgot. You might want to know where to program checks the checksum. Well, here you are: :00437C43 8A87E3000000 mov al, byte ptr [edi+000000E3] load E3-rd byte into al :00437C49 3A45FF cmp al, byte ptr [ebp-01] <= check it!! :00437C4C 750B jne 00437C59 :00437C4E 8A87E4000000 mov al, byte ptr [edi+000000E4] :00437C54 3A45FE cmp al, byte ptr [ebp-02] :00437C57 7404 je 00437C5D .----------------------------------------------------------------------------------------------. | Getting a Keyfile-Generator | | part 3: Creating the generator in Pascal | `----------------------------------------------------------------------------------------------' The first thing I did to create the keygen was to copy the DEcryption routine into Pascal. Here is my pascal DEcryptor: .----------------------------------------------------------------------------------------------. `----------------------------------------------------------------------------------------------' program KEYFILE_GENERATOR(input, output) ; var si, ax : word ; var hex_byte, check1, check2, counter, lengthname : byte ; var TableOut, name, temp : string ; var filename : file of byte ; begin assign(filename, 'winctrl.key') ; (* assign 'winctrl.key' to filename *) reset(filename) ; (* point to 1st byte of the keyfile *) seek(filename, $e7) ; (* point to the e7-th byte *) read(filename, hex_byte) ; (* get the info from e7-th byte *) ax := hex_byte ; ax := ax * 256 ; (* shl ax, 2 *) seek(filename, $e6) ; read(filename, hex_byte); ax := ax + hex_byte ; (* ax = si = our en/decryption key *) si := ax ; check1 := $a5 ; (* Start values for the checksum *) check2 := $5a ; for counter := 2 to $8 do (* Just do the first seven chars *) seek(filename, (counter-1)) ; read(filename, hex_byte) ; ax := ax div 256 ; (* shr ax, 8 *) ax := ax xor hex_byte ; (* xor dl, cl *) writeln('decrypted value: ', ax) ; (* the result...should be our name *) check1 := check1 + ax ; (* Update Checksum *) check2 := check2 xor ax ; (* '' '' *) TableOut := TableOut + chr(ax) + '.' ; (* store decrypted text in TableOut *) si := si + hex_byte ; (* Do some aritmetic (call to 43702C*) ax := si * $ce6d ; ax := ax + $58bf ; si := ax ; end ; seek(filename, $e7) ; read(filename, hex_byte) ; ax := hex_byte ; (* update checksum for call at 437C2B*) check1 := check1 + ax ; check2 := check2 xor ax ; writeln('The Checksum: ',check1,' ',check2) ; (* print checksum as it should be *) writeln(TableOut) ; (* write decrypted name *) close(filename) ; end. .----------------------------------------------------------------------------------------------. `----------------------------------------------------------------------------------------------' This program uses the winctrl.key to get the needed data for the DEcryption routine, it also provided you with the correct checksum. Now we must reverse it this way it provides is with the ENCRYPTED name. Well, it quite easy.....when you know where to look. Take a look at this part of the Decryptor: for counter := 2 to $8 do (* Just do the first seven chars *) begin seek(filename, (counter-1)) ; read(filename, hex_byte) ; ax := ax div 256 ; (* shr ax, 8 *) ax := ax xor hex_byte ; (* xor dl, cl *) writeln('decrypted value: ', ax) ; (* the result...should be our name *) check1 := check1 + ax ; (* Update Chechsum *) check2 := check2 xor ax ; (* '' '' *) TableOut := TableOut + chr(ax) + '.' ; (* store decrypted text in TableOut *) si := si + hex_byte ; (* Do some aritmetic (call to 43702C*) ax := si * $ce6d ; ax := ax + $58bf ; si := ax ; end ; The easiest is to modify it after the 'ax := ax div 256'. AX is some value and AFTER it is XORRED by the next byte from our keyfile it returns the decrypted letter. Let's put it in a formula: ax XOR encrypted letter = decrypted letter Well, we know what the decrypted letter should be: the x-th letter of the name. We also know ax, is is calculated for us. We only do NOT know what the encrypted letter should be. We shall know it within a few seconds. The mathematicians among us will probably know what to do: ax XOR encrypted letter = decrypted letter --= SO =-- encrypted = decrypted XOR ax !!!! We only have to modify/add a few lines to get the ENCRYPTED value!!!! name := 'MisterE' ; for counter := 2 to $8 do (* Just do the first seven chars *) begin seek(filename, (counter-1)) ; read(filename, hex_byte) ; ax := ax div 256 ; (* shr ax, 8 *) hex_byte := ax xor ord(name[counter-1]) ; writeln('ENcrypted value: ', hex_byte) ; ax := ax xor hex_byte ; (* xor dl, cl *) writeln('DEcrypted value: ', ax) ; (* the result...should be our name *) check1 := check1 + ax ; (* Update Chechsum *) check2 := check2 xor ax ; (* '' '' *) TableOut := TableOut + chr(ax) + '.' ; (* store decrypted text in TableOut *) si := si + hex_byte ; (* Do some aritmetic (call to 43702C*) ax := si * $ce6d ; ax := ax + $58bf ; si := ax ; end ; YEEHAAA, we did it!! Now we only have to write a keygenerator for it, that shouldn't be a problem because we know how the encrypted data. Here is my keyfile-generator!! .----------------------------------------------------------------------------------------------. `----------------------------------------------------------------------------------------------' var si, ax : word ; var var1, zero, bx, hex_byte, check1, check2, counter, lengthname : byte ; var string1, name, temp : string ; var filename : file of byte; procedure WriteInsideLogo ; begin ; writeln(''); writeln(' ÜÜÜ ²Ü Ü'); writeln(' ßÛÛ²ß ² ²ÜÜ Ü²ßßß ß ÜÜ ²ÜÜÜܲ ²ÜÜÜÜܲßßß ÜÜÜ'); writeln(' ° ÜÜÜ Üܲßßßß ßß²ÜÜ ²ßßß ÜÜÜÜÜÛÛÛÝ ßÛÛß ÜÜÜÜ ß ß²Üܲ ²ßßß ÜÜÜÜÜÜÛÛÛ²ÛÝ'); writeln(' ß²ÛÛÝ ²ß ÜÜÛÛÛÛÛÜÜ ² ÜÛ²ÛÛÛÛßßÛÛÛ ° ÜÜÜ ÞÛÛÛÛÛÛÜÜÜ ßß²ßÞÛÛÛÛÛßßßßÛÛÛÛß'); writeln(' ÛÛÛÛ þ ÛÛÛÛß ßÛÛÛÜ ²ÛÛÛÛÜ ° ßß ² ÛÛÛÛ° Û²ÛÝ ßßÛÛÛÛÜÜ ²ÛÛÛ ° ßßß'); writeln(' þÞÛÛÛÝ Þ²ÛÛÝ° ² ÞÛÛÛÝ ßßÛÛÛÛÛÛÜÜÜÜ ÞÛ²ÛÝ Û²ÛÝ Ü ° ßßÛÛÛÜ ÛÛÛÝ ÜÜÜÜ'); writeln(' Ü ÛÛ²Û ÛÛÛÛÝþ ÞÝ Û²ÛÛ ²ÜÜÜ ßßßßÛÛÛ²ÜßÛÛÛ ÞÛÛÛ ² ° ßÛÛ²ÜßÛÝßßß MtD! Ü'); writeln(' ² ÛÛÛ²Ýß²ÛÛ Ü Û Þ²²ÛÝ ßßßß ÜÜÛÛÛ²ÝÛÛÛÝÞÛ²Û ÜÜßß ß ÜÛÛÛ²ÝÛ² °'); writeln(' ÞÝÞÛÛÛÛ ßßß Ü ² ÞÛ²ÛÝÞÛÛÛÜÜÛÛÛÛÛÛÛ²ß ÛÛ²Ýßß ÜÜÜÜÜÛÛÛÛÛÛ²ßßÜÛÛÜÜÜÜÜÛÛ²²Ý ß'); writeln(' Û ßßß Üܲ ßß² ² ßßßß ²ÛÛÛßßßßß ÜÜÜ ßßßß ßÛÛÛÛÛßßßßß Ü Ü ÛÛÛÛÛßßßßßÛÛ²Û Û'); writeln(' ß² ßßßß² ß ßß ßßß²ÜÜÜ Üܲßßßß² ²ßßß ß ßßÛÜ ßßßß߲߲ÜÜÜ ÜÜÜ²ß²Ü ÜÜÜܲ'); writeln(' ß ß ß'); end ; begin assign(filename, 'winctrl.key') ; (* assign the name of our keyfile to filename *) rewrite(filename) ; (* Destroy old keyfile and create a new one *) WriteInsideLogo ; (* Write the iNSiDE logo *) writeln(' Keyfile Generator for WinCtrl v1.41'); writeln(' Written by MisterE (8th August 1998)'); writeln('') ; write(' Enter your name: ') ; (* Enter your name.... *) readln(name) ; (* read the name *) lengthname := length(name) ; (* get the lenght of the name *) write(filename, lengthname) ; (* write the length of the name to the keyfile*) si := $4d00 ; (* start-value for our encryption key ... *) ax := $4d00 ; (* BTW: if you would modify 4d (not the 00) *) (* the KeyFileGenerator should still work *) (* but then you also must modify line 66 *) check1 := $a5 ; (* Start-value for our checksum *) check2 := $5a ; (* Start-value for our checksum *) for counter := 2 to (lengthname+1) do (* Our modified call to 43702C *) begin (* This routine encrypts your name... *) (* ..and writes the info to winctrl.key *) ax := ax div 256 ; (* shr ax, 8 *) bx := ax xor ord(name[counter-1]) ; (* get the encrypted value of the ... *) (* counter-th letter of your name *) write(filename, bx) ; (* Write it to the keyfile *) ax := ax xor bx ; (* encrypt the counter-th letter *) check1 := check1 + ax ; (* update checksum for the call at 437BE0 *) check2 := check2 xor ax ; (* '' '' *) si := si + bx ; (* arithmetic part of the generator *) ax := si * $ce6d ; (* '' '' *) ax := ax + $58bf ; (* '' '' *) si := ax ; (* '' '' *) end ; ax := $4d ; (* start value *) check1 := check1 + ax ; (* update checksum for the call at 437C2B *) check2 := check2 xor ax ; (* '' '' *) zero := 0 ; (* needed to write zeros *) seek(filename, (lengthname+1)) ; for counter := 1 to ($e3 - (lengthname+1)) do write(filename, zero) ; (* write zeros till..*) write(filename, check1) ; (* till we reach the e3-rd byte of the .key *) write(filename, check2) ; (* Write those checksums!!! *) write(filename, zero); (* and write some more zeros ... *) write(filename, zero); zero := $4d; (* don't mail me about this...I didn't want *) (* to declare another variable *) write(filename, zero); (* write out encryption key *) seek(filename, $70) ; (* Write copyright message :-) *) string1 := 'This Keyfile was' ; for counter := 1 to length(string1) do begin var1 := ord(string1[counter]) ; write(filename, var1) ; end ; seek(filename, $81) ; string1 := 'brought to you' ; for counter := 1 to length(string1) do begin var1 := ord(string1[counter]) ; write(filename, var1) ; end ; seek(filename, $97) ; string1 := 'by' ; for counter := 1 to length(string1) do begin var1 := ord(string1[counter]) ; write(filename, var1) ; end ; seek(filename, $a4) ; string1 := 'MisterE' ; for counter := 1 to length(string1) do begin var1 := ord(string1[counter]) ; write(filename, var1) ; end ; (* STOP THE WRITING!! *) close(filename) ; (* Close the file, otherwise data loss could *) (* ... occur *) writeln ; (* skip a few spaces *) writeln ; writeln(' Your Keyfile has successfully been created!!') ; (* inform the user... *) writeln(' Copy the Keyfile(WinCtrl.key) to your WinCtrl program directory.'); end. (* terminate the program *) .----------------------------------------------------------------------------------------------. `----------------------------------------------------------------------------------------------' That is my generator. We're done. . .. ... Well, we don't have to be done with this. If you still aren't tired and want to go on some more, then this is your CHANCE. Here is another part. You don't need it to create a valid keyfile, but I thought it would be nice to use the protection scheme to protect my keyfiles!!! .----------------------------------------------------------------------------------------------. | Protecting the Keyfile-Generator | `----------------------------------------------------------------------------------------------' Wow, you're still reading!!!! That's cool! Did you try my soon-to-be-modified-generator and did you take a look at the generated keyfile using your favorite hexeditor? You see that 'This keyfile was brought to you by MisterE'? Well, there are some LOSERS on this world who like CHANGE THE AUTHORS NAME!! Changing MisterE to Loser('s name). AARRGghh, I can't stand these lamers. Let's PROTECT the keyfiles!! Ok, remember there were some more checksum checks? For example the call at 437BF9. The code used for this checksum check gets decrypted at 437B55, but the decrypted data isn't used in the 'about box'. The encrypted data will be used for the checksum. We only have to do a few things to protect to generator: a) include the copyright message in the checksum. b) adjust the checksum in the generator, because we added some other data. OK, lets start with a), remember this?: :00437BEC 8A5F41 mov bl, byte ptr [edi+41] <= load 41-st byte :00437BEF 85DB test ebx, ebx :00437BF1 7E10 jle 00437C03 :00437BF3 8D7742 lea esi, dword ptr [edi+42] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00437C01(C) | :00437BF6 55 push ebp :00437BF7 8A06 mov al, byte ptr [esi] :00437BF9 E8EEFDFFFF call 004379EC <= update checksum :00437BFE 59 pop ecx :00437BFF 46 inc esi :00437C00 4B dec ebx :00437C01 75F3 jne 00437BF6 We must modify the 41-st byte so that it includes the copyright message. Here's a keyfile that is registered to MisterE: 00000000 0700 4804 4CE0 BAB0 0000 0000 0000 0000 ..H.L........... 00000010 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000020 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000030 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000040 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000050 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000060 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000070 5468 6973 204B 6579 6669 6C65 2077 6173 This Keyfile was 00000080 0062 726F 7567 6874 2074 6F20 796F 7500 .brought to you. 00000090 0000 0000 0000 0062 7900 0000 0000 0000 .......by....... 000000A0 0000 0000 4D69 7374 6572 4500 0000 0000 ....MisterE..... 000000B0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000C0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000D0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000E0 0000 00AB 6600 004D ....f..M If we change to 41-st byte to 70h it would include the copyright message. Now we have to b) adjust the checksum for this. The easiest way is to run WinCtrl (with 41st byte = 70h) till the checksum check. Now take a look at the checksum values. Checksum1 should be FBh and checksum2 should be C2h. Adjust the pascal program: (only a part is printed here) si := ax ; (* '' '' *) end ; ax := $4d ; (* start value *) check1 := check1 + ax ; (* update checksum for the call at 437C2B *) check2 := check2 xor ax ; (* '' '' *) (* ADD THE FOLLOWING LINE *) writeln(check1,' ',check2) ; Run the program. Check1 = 171decimal and Check2 = 102dec. Calculation the adjustment needed for the checksum: Adjustment check1: FBh - 171decimal = 50h Adjustment check2: C2h XOR 102decimal = A4h Change the generator: si := ax ; (* '' '' *) end ; ax := $4d ; (* start value *) check1 := check1 + ax ; (* update checksum for the call at 437C2B *) check2 := check2 xor ax ; (* '' '' *) (* ADD THE FOLLOWING LINES *) check1 := check1 + $50 ; (* protection *) check2 := check2 xor $a4 ; (* protection *) So, that's that. We're almost done. ALMOST. We still have to write 70h to the 41-st byte. Below this part i'll print a part of the protected generator: .----------------------------------------------------------------------------------------------. `----------------------------------------------------------------------------------------------' ax := $4d ; (* start value *) check1 := check1 + ax ; (* update checksum for the call at 437C2B *) check2 := check2 xor ax ; (* '' '' *) check1 := check1 + $50 ; (* protection *) check2 := check2 xor $a4 ; (* protection *) zero := 0 ; (* needed to write zeros *) seek(filename, (lengthname+1)) ; for counter := 1 to ($e3 - (lengthname+1)) do write(filename, zero) ; (* write zeros till..*) write(filename, check1) ; (* till we reach the e3-rd byte of the .key *) write(filename, check2) ; (* Write those checksums!!! *) write(filename, zero); (* and write some more zeros ... *) write(filename, zero); zero := $4d; (* don't mail me about this...I didn't want *) (* to declare another variable *) write(filename, zero); (* write out encryption key *) seek(filename, $41) ; zero := $70 ; write(filename, zero) ; (* write 70h to the 41-st byte *) seek(filename, $70) ; (* Write copyright message :-) *) string1 := 'This Keyfile was' ; .----------------------------------------------------------------------------------------------. `----------------------------------------------------------------------------------------------' OK, we're done, completely. I can't think of any more thing to do with this program. I'am glad you made it this far. This is my longest tutorial (out of eleven). Even good ol' notepad couldn't handle the size of this tutorial :-(. Writing this tutorial was quite a challenge, because I didn't know if I would make it to the end: I hadn't written the generator when I started writing this tutorial. Anyway, I think it is quite complete. If you want to do some exercise then download MP3 Eden Tagger from the same url as WinCtrl. MP3 Eden Tagger has ALMOST the same protection. It shouldn't be a problem for you.... .----------------------------------------------------------------------------------------------. | Final Notes | `----------------------------------------------------------------------------------------------' Well, I hope you learned SOMETHING from this tutor. If you have any comments, questions, need help or whatever, mail me at MisterE@freemail.nl OR look for me at EFNET => #cracking4newbies or #cracking .----------------------------------------------------------------------------------------------. `----------------------------------------------------------------------------------------------'