²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ²² ____ __ __ ²²ßÛ ²² / _/_ _ __ _ ___ ____/ /____ _/ / ²² ÛßÛ ²² _/ // ' \/ ' \/ _ \/ __/ __/ _ `/ / ²² Û Û ²² /___/_/_/_/_/_/_/\___/_/ \__/\_,_/_/ ²² Û Û ²² ____ __ __ ²² Û Û ²² / __ \___ ___ _______ ___ ___/ /__ ____ / /____²² Û Û ²² / /_/ / -_|_-</ __/ -_) _ \/ _ / _ `/ _ \/ __(_-<²² Û Û ²²/_____/\__/___/\__/\__/_//_/\_,_/\_,_/_//_/\__/___/²² Û Û ²² ²² Û Û ²² Web: http://www.ImmortalDescendants.com ²² Û Û ²² Author: Lord Soth ²² Û Û ²² Date: 09/09/99 (mm/dd/yy) ²² Û Û ²² Topic: Cracking Jammer 1.95 ²² Û Û ²² Level: Intermediate ²² Û Û ²² ²² Û Û ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² Û Û ÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÛ Û ÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÛ ------------------------------------------------------------------------------ Don't be lame and distribute a keygen using this method. ------------------------------------------------------------------------------ Ok folks, I'm back again, but bringing a bit less of a knowledge load .. :) In this essay I want to explore one of the most widespread protections in existence, much to the programmer's regret, BTW. This protection is pretty straightforward. If you are not a registered user, Jammer will disable a few inside features and will limit itself to a 30 day evaluation period. If this was all, this could have been a pain for us reversers, because even if we can pretty easily defeat the time check, those remaining hidden features might be a bitch to find. However, much to our advantage, this is a product of commercial oriented programmers, who probably wanted it to go on the market ASAP, so they made a silly protection and added a registration dialog for us to tamper with. And they did this with guess what: MFC !!! That makes things SO MUCH better for us, because MFC routines use several standard API calls, we can trap the protection more easily! Ok enough ranting, I want to go on to the more important stuff. I will use this program as an example of how to code a serial gen. I know probly most of you know but I wanna show how simple it is to generate a serial gen that runs under winblows and has a nice GUI. The second section of this essay is devoted for that and explanation of my source code which is actually pretty simple. For now lets start the reversing part of this essay. Cracking Jammer 1.95 build 0809 ================================= Ok, first swoop, lets launch Jammer and see whats going on inside it. When it gets run, we can see several messages in its client window saying it is unregistered and blah blah blah (first we go into some config dialog, but after we're done with it, the main app runs). We can try to be smart and launch a disassembler and try to catch this strings and maybe work our way backwards to the validation routine, but I really don't like that approach much. Instead, lets do this. Lets find out (using a disassembly if you want), what imports this program is using. After we disassemble/Tdump the EXE file, we see that it does NOT use any of the standard APIs to catch text from a dialog box or a window, i.e: GetDlgItemTextA, GetWindowTextA and so on..... But we know that if we click on the Register option in the about menu, a dialog will pop up and let us input our user name and serial number. This is where M$ comes into action, in the form of MFC. Know this : when MFC ordinals are used to open a dialog box and manage its controls, you can bet on it to use a standard API. What do I mean ? If you want, you can look in a disassebly. Find a string resource that appears in the dialog box, and you will see several CALLs to MFC ordinals (routines). In these the actual creation of the dialog happens, but this is of no concern to us. All we need to do is break on the code getting the text from the dialog, and this can be accomplished (pretty sure whenever they code in MFC) by the following BP: bpx getwindowtexta Now fill in the fields in whatever you like, I put : Lord Soth, and for the serial I put 12345 - 67890. That turned out as a mistake later on, but lemme get to that. Now after we try to register, we break inside SI on GetWindowTextA. P RET (F12), and wham, we're back inside the caller routine which is if you notice, not in the normal code area of normal programs. In fact its dwelling in 5XXXXXXX and above which suggests a DLL code, and my guess would go to MFC42.DLL (mainly because the program does not have any other DLLs hehe). So we broke on the code and we see this: :u 5f41423c l 20 023F:5F41423C 50 PUSH EAX 023F:5F41423D E8FCE5FEFF CALL 5F40283E 023F:5F414242 50 PUSH EAX ; push a pointer to where text would be... 023F:5F414243 56 PUSH ESI 023F:5F414244 FF1588B5495F CALL [USER32!GetWindowTextA] 023F:5F41424A 8B4D10 MOV ECX,[EBP+10] ;<<< we land here!!! 023F:5F41424D 6AFF PUSH FF 023F:5F41424F E8CFE4FEFF CALL 5F402723 023F:5F414254 5F POP EDI 023F:5F414255 5E POP ESI 023F:5F414256 5D POP EBP 023F:5F414257 C20C00 RET 000C 023F:5F41425A 8B4510 MOV EAX,[EBP+10] Ok, so we got back, and we see we're inside the MFC DLL, there won't be any interesting code in here, so lets try to go back a little. Before we do that, in these situations (almost any reference to an API), I tend to put a BP on the PUSH instructions themselves. This is done so the next time the code will be executed, I can watch the parameters the APIs are receiving. If you'll do this who thing again, and watch what's passed to the CALL, you'll see some pointer values and if you display those memory locations (do it before the CALL because afterwards they change), you'll see the input you put appear there. Anyways, back to business. I want to see what happens here, so after I display my name in the data window, which I will do by redoing all this again, and doing a D EAX, I'll wanna put a BP on memory read where the username I put dwells. This would normally lead you straight to the calculation routine, but in this case it won't. When the registration information is invalid, the program opens up a MessageBox and yells at you that its invalid. After I breaked 3 times on GetWindowTextA (3 fields of input), I noticed that it automatically popped the MessageBox without breaking on memory read! I asked myself how could this be, and the answer came a little while afterwards. I decided to continue stepping through code and seeing what happened. I figured lets first return to the real program and out of M$'s code. I returned a few more times and then I saw I was back in the code area of Jammer. It looked pretty odd, not what you would expect from a Win program. It was just PUSHs and CALLs. It certainly looked liked the higher level of a VB program, or an MFC app! Some of the CALLs were repetitive, so I decided to step over them, and in the repeating ones, SI broke on the BPs I set earlier (remember the BP on 5F414242 , that PUSH EAX ? ).. SI breaks whenever you got a BP inside a CALL you step over because stepping over activates all the sticky BPs as noted in the SI manual. So, checking the programs view (F4), I saw the MessageBox wasn't there, it hasn't called it yet. That was good, and I continued to step over code and then got to a RET, and that took me to a different area completely. A bit of stepping and I found this snippet of code (almost right after I RET from the above mentioned code): :u eip l 100 023F:0041BE3A 8B4664 MOV EAX,[ESI+64] ; loading of address of username! 023F:0041BE3D FF766C PUSH DWORD PTR [ESI+6C] 023F:0041BE40 8B40F8 MOV EAX,[EAX-08] 023F:0041BE43 8945E4 MOV [EBP-1C],EAX 023F:0041BE46 8B4668 MOV EAX,[ESI+68] 023F:0041BE49 8B78F8 MOV EDI,[EAX-08] 023F:0041BE4C FF1550594200 CALL [00425950] 023F:0041BE52 8D5E60 LEA EBX,[ESI+60] 023F:0041BE55 8945E0 MOV [EBP-20],EAX 023F:0041BE58 8BCB MOV ECX,EBX 023F:0041BE5A C7042480134300 MOV DWORD PTR [ESP],00431380 023F:0041BE61 E8EE480000 CALL 00420754 023F:0041BE66 33C9 XOR ECX,ECX 023F:0041BE68 394DE0 CMP [EBP-20],ECX 023F:0041BE6B 0F8452010000 JZ 0041BFC3 023F:0041BE71 837DE408 CMP DWORD PTR [EBP-1C],08 ; compare length of name with 8 , has to be greater than 023F:0041BE75 0F8C48010000 JL 0041BFC3 ;if not, go to bad cracker 023F:0041BE7B 83FF0A CMP EDI,0A ;compare length of serial entered, gotta be 10 or bigger 023F:0041BE7E 0F8C3F010000 JL 0041BFC3 ;if not, go to bad cracker 023F:0041BE84 8B4664 MOV EAX,[ESI+64] ; loading again, this time for real! 023F:0041BE87 8D7901 LEA EDI,[ECX+01] ; put something in ECX (0,1,2,...) 023F:0041BE8A 8A1408 MOV DL,[ECX+EAX] ; get char from start of name 023F:0041BE8D 2BC1 SUB EAX,ECX ; reduce pointer to name by ECX 023F:0041BE8F 8855F3 MOV [EBP-0D],DL ; store char on stack 023F:0041BE92 8B55E4 MOV EDX,[EBP-1C] 023F:0041BE95 8BCF MOV ECX,EDI ; restore ECX from before 023F:0041BE97 8A4410FF MOV AL,[EDX+EAX-01] ; get last char from name 023F:0041BE9B 0FAFCF IMUL ECX,EDI ; multiply ECX by itself 023F:0041BE9E 0FBED0 MOVSX EDX,AL ; take char and zero everything else 023F:0041BEA1 0355E0 ADD EDX,[EBP-20] ; add the first serial field to the last char 023F:0041BEA4 0FBE45F3 MOVSX EAX,BYTE PTR [EBP-0D]; move into EAX and clear the rest 023F:0041BEA8 03D1 ADD EDX,ECX ; now add ECX * ECX 023F:0041BEAA B9FF000000 MOV ECX,000000FF ; load 255 023F:0041BEAF 03C2 ADD EAX,EDX ; now add first char to the whole thing 023F:0041BEB1 33D2 XOR EDX,EDX ; zero EDX (remainder register) 023F:0041BEB3 F7F1 DIV ECX ; divide result with 255 023F:0041BEB5 0FB6C2 MOVZX EAX,DL ; get the remainder and zero everything else 023F:0041BEB8 50 PUSH EAX ; push on stack 023F:0041BEB9 8D45EC LEA EAX,[EBP-14] 023F:0041BEBC 68600B4300 PUSH 00430B60 023F:0041BEC1 50 PUSH EAX 023F:0041BEC2 E8FF4B0000 CALL 00420AC6 ; turn the hex code into a string that looks the same 023F:0041BEC7 83C40C ADD ESP,0C 023F:0041BECA 8D45EC LEA EAX,[EBP-14] ; pointer of end of serial is here! 023F:0041BECD 8BCB MOV ECX,EBX 023F:0041BECF 50 PUSH EAX 023F:0041BED0 E8F74E0000 CALL 00420DCC ; append to pointer of end of serial! 023F:0041BED5 8BCF MOV ECX,EDI ; restore ECX 023F:0041BED7 83F905 CMP ECX,05 ; check if 5 023F:0041BEDA 7CA8 JL 0041BF84 ; if not yet, go back for another run 023F:0041BEDC FF7668 PUSH DWORD PTR [ESI+68] 023F:0041BEDF FF33 PUSH DWORD PTR [EBX] 023F:0041BEE1 E894520000 CALL 0042117A ;compare actual serial to what we put in 023F:0041BEE6 59 POP ECX 023F:0041BEE7 85C0 TEST EAX,EAX ;based on this branch to good/bad cracker.. 023F:0041BEE9 59 POP ECX 023F:0041BEEA 7407 JZ 0041BEF3 023F:0041BEEC 6A00 PUSH 00 023F:0041BEEE E9D1000000 JMP 0041BFC4 023F:0041BEF3 FF7664 PUSH DWORD PTR [ESI+64] 023F:0041BEF6 8D85B0FEFFFF LEA EAX,[EBP-0150] 023F:0041BEFC 50 PUSH EAX 023F:0041BEFD E88A520000 CALL 0042118C 023F:0041BF02 FF766C PUSH DWORD PTR [ESI+6C] 023F:0041BF05 8D857CFFFFFF LEA EAX,[EBP-0084] 023F:0041BF0B 50 PUSH EAX 023F:0041BF0C E87B520000 CALL 0042118C 023F:0041BF11 8D857CFFFFFF LEA EAX,[EBP-0084] 023F:0041BF17 685C0B4300 PUSH 00430B5C 023F:0041BF1C 50 PUSH EAX 023F:0041BF1D E852520000 CALL 00421174 023F:0041BF22 FF7668 PUSH DWORD PTR [ESI+68] 023F:0041BF25 8D857CFFFFFF LEA EAX,[EBP-0084] 023F:0041BF2B 50 PUSH EAX 023F:0041BF2C E843520000 CALL 00421174 023F:0041BF31 83C420 ADD ESP,20 023F:0041BF34 8D857CFFFFFF LEA EAX,[EBP-0084] Now I've found my problem. Look at address 0041BE71. The length of the serial I entered on the second field is compared with 10 decimal, and if its smaller, than we go to bad cracker routine, and without even picking one character ! This is why SI didn't break when I put a memory BP on my user name. The program never got to the calculation routine! Ok so I fixed this, and put a 10 digit serial in the 2nd field, and now this check has passed as well and we're off to some interesting stuff. Notice, after we passed the inital length checks, the real calculation starts in address 0041BE84. What is happening here ? only way to know for sure is to go with SI and reverse this part! A disassembly will not help you to see the pattern here. Ok so in SI, we go from address 0041BE84 and we see what happens. Everything is pretty normal right untill address 0041BE87. That instruction puzzled me actually. It pointed to a weird area in memory and ECX took its address. I didn't figure it out untill I did the whole loop again. Everytime, ECX is loaded with 1 more than it used to be, and it used to be 0 because there's a XOR ECX,ECX somewhere in there. Everytime we passed on this instruction, ECX would increase in 1 actually, nothing special to it. Then we see some handling of the value in EAX, which holds the pointer for the beginning of the username. This is very weird I thought in the beginning, only to realize what is happening in the next pass. In the first pass ECX was 0, and we took the first char pointed to by EAX (natually). Then the length of the username is added and stuff, and reduced by 1 and we end up with the last char in the name. The whole trick is that when we do this loop again, ECX will be bigger, and walla, look how it automagically takes the next char and with the same calculation takes the next char from the end! pretty slick :) Ok in each pass it takes the end and the beginning char and it moves inward to the center of the string. Each time, you can pretty easily see whats going on : it takes the number we entered in the first field of the serial, it adds to it our last char's value. Then it takes ECX which is basically a counter, it multiplies by itself (power of 2) and added to the result. Then the first char is added again. We get a result which we divide by 255 and the remainder is a number between 0 to FE in hex, and that gets turned into a string, exactly how it looks, and then added to the serial number string (yes, the good 1). The the whole process repeats itself , to a total of 5 times. This gives us a serial number of 10 digits, and it has to be uppercase as we will see when we try to enter it :) I wanna note that the 1st field of the serial can be anything we want , the second gets calculated by the first. Anyways, I included my serial gen along with its source and resource, I hope you find it useful... Yet again, another silly protection was annihilated , mainly because being a good programmer does not mean being a good protectionist, and as companies want money, they lack :) Even this company's name is all about Jammer, suggesting it was created just for this project, another company was created just for the quick buck.. Ah well.... On to the serial code! Serial code coding :) ====================== In this zip (hopefully), you'll find Jammser.cpp, my serial generator. It runs under winblows and is pretty simple to understand. The main function (WinMain), starts and without even registering a window class or opening a window, it calls a dialog box for the serial (dont need more than a dialog box). Notice that if you don't pass the owner window's handle to the dialog box function (because there is none!), the desktop will be the owner window, so this is a way to make custom windows without having to register your window classes :) the CALLBACK function 'gen' is the message loop of the dialog box. It detects when the Generate button has been pressed and acts accordingly. Look at the little loop there, its the whole serial calculation in short hehe Then the serial is loaded back into the Dialog and we're done. If the exit button is pressed we end the dialog there (notice that the switch is used to determine which message and which button was clicked). This is standard win programming, not too fancy , pretty short tho :) This is the source with all the includes and resources , you could insert your calculation code and make yourself a keygen if you don't really know about programming, but I suggest learning :) Anyways folks, thats all for now, I hope you enjoyed this little dumb protection. Learn how to bash MFC !!! :) For questions, comments and stuff: lordsoth8@hotmail.com ICQ # 5178515 Lord Soth PS Greetings to everyone I know! and those I don't :)