P-Code Compiled Visual Basic 5 |
|
|
by Gimmee |
||
|
||
|
||
Reversing a P-Code Compiled Registration Algorithm
from yet another Visual Basic program
Written by Gimmee
CodeFumbler sent me this program quite sometime ago to help him crack it. Jetskiing, Snowboarding, and other assorted recreational activities (Some legal, some not. Mostly legal as I'm too old to be getting up to mischief these days) conspired against me and it got put in the 'to do' basket. I don't know if CodeFumbler finished the crack or put it in the 'to do' basket as well. A few choice injuries at the end half of 2001 have left me with time to burn on the puter again and looking in the 'to do' basket out popped "Now You're Cooking". I was interested in it because it was a Visual Basic program (if Visual Basic then cracked at once) and also it was compiled to p-code which seems to mystify some newbies. Hopefully this tutorial goes someway to making things clearer.
Now You're Cooking! (NYC) is versatile cooking software to help you organise and maintain your recipes, plan healthy meals, create shopping lists, and manage grocery costs. Blah, blah, blah. Over 500 beta testers participate in review of new NYC versions, a testament to our firm belief in code that is powerful and reliable.
Gary E . Hauser (Programmer)
President, Loginetics, Inc.
WKTVDebugger: This is a p-code debugger for use instead of softice. Never been able to get it to work for me. Worth a look though.
Exdec: A dissasembler for P-Code compiled VB programs. Didn't use it this time except for explaining code in this tutorial. Very useful for seeing code flow.
Extract from MSDN:
P-Code, or pseudo code, is an intermediate step between the high-level instructions in your Basic program and the low-level native code your computer's processor executes. At run time, Visual basic translates each p-code statement to native code.
P-Code compiled exe's are smaller than when compiling to native code but native code can provide substantial gains in speed over the interpreted versions of the same application; (yes wait for it, theres always a however) however, this is not always the case.
What does this mean to us crackers:
The main difference you will notice is when debugging in softice. You will spend most of your time in the msvbvm50.dll (in our case because the program is written in VB5), if it was a VB6 program you would spend most of your time in the msvbvm60.dll. Occassionally you will jump into the actual program code when calling a function however. Also when stepping through the code it doesn't seem linear, you always seem to be jumping all over the place which can be confusing compared to debugging a native compiled VB program. It's not as confusing as it seems though after taking some time to step through a couple of p-coded compiled programs.
The esi register holds a pointer to the next line of code or opcode that is going to be executed from the main program executable. To break on this you need to set a memory breakpoint ie:bpm esi or bpm 59a89d (a breakpoint used in NYC).
I will take a small extract from NYC and step you through the loading of the opcodes and the jump to the actual code manipulation so you get an idea what is going on:
59a7e6: 04 FldRfVar local_0094 :Load variable or push onto stack 59a7e9: 04 FldRfVar local_020C :Same as above 59a7ec: 0a ImpAdCallFPR4: _rtcLeftCharVar :Get characters to left of variable 59a7f1: 04 FldRfVar local_020C :Load variable or push onto stack 59a7f4: 04 FldRfVar local_0094 :Same again 59a7f7: Lead0/eb FnLenVar :Get length of variable 59a7fb: 28 LitVarI2: (local_022C) 0x4 (4) :Constant 4 pushed onto stack 59a800: Lead0/9c SubVar :Subtract something from somethingLet's see what this little snippet of code looks like in p-code and how to step through it:
59a7e6: 04 FldRfVar local_0094 :Load variable or push onto stackIn p-code this instruction will always look like this when stepping through with softice:
:0f0fd9a2 0fbf06 movsx eax, word ptr [esi] :Land here :0f0fd9a5 03c5 add eax, ebp :eax now holds pointer to variable :0f0fd9a7 50 push eax :Push pointer onto stack :0f0fd9a8 33c0 xor eax, eax :Clear eax for next opcode :0f0fd9aa 8a4602 mov al, [esi+02] :Next opcode into al :0f0fd9ad 83c603 add esi, 03 :0f0fd9b0 ff248594ed0f0f jmp [eax*4+0f0fed94] :Jmp to opcode routineAny time you go through an opcode routine once you get to the xor eax, eax you know that the next 3 lines load the next opcode and jump to that routine. Easy aye. So above the first 3 lines of code, load a pointer and push it onto the stack. If you dump the memory that eax points to (d eax) it doesn't always show the pointer or variable right in your face. You might see a data window in softice like below with a 08 or 02 where a pointer should be:
EAX=0084F2D4 EBX=0084F15C ECX=00000002 EDX=00000000 ESI=0059A7E7 EDI=00000000 EBP=0084F368 ESP=0084EFE4 EIP=0F0FD9A8 o d I s z A P C CS=015F DS=0167 SS=0167 ES=0167 FS=29D7 GS=0000 ------------------------------------------------------------------------------------------- eax = 84F2D4 ebx = 84F15C ecx = 2 edx = 0 ------------------------------------------------------byte------------------PROT---(0)----- 0167:0084F2D4 08 00 84 00 C5 A5 05 0F-38 B1 6A 00 0B 00 00 00 ........8.j.....In this case we have an 08 in our data window. Usually I just count this number of bytes to the right and our pointer or sometimes a variable will be there. This is not always the case though in which case just look for anything that looks like a valid pointer along this line of 16 bytes and dump it. I notice when this pointer gets copied from one place to the next it copies the whole 16 bytes. Doing this with the softice dump above we get to the pointer 38B16A. Remember this is reverse in memory so do a data dump (d 6AB138) of this pointer and you will see the variable that is being pointed to. In this case it is the string you entered in the registration box. When this pointer is copied or moved it seems to takes the whole 16 bytes that is shown in the softice dump above not just the 38B16A value. Hopefully someone can explain this little quirk to me sometime. Next is the xor eax, eax so we know that the next opcode is about to be loaded into the al register. In our case it will be a 4 because looking at our dissasembly the next line of code is:
59a7e9: 04 FldRfVar local_020C :Load variable or push onto stackWell you should be able to work out where its going to jump to as it is the same instruction as the last. If you are in softice and stepping through the code you will see you land at the start of the routine we have just been explaining.
:0f0fd9a2 0fbf06 movsx eax, word ptr [esi] :Land hereYou have already stepped through this small routine once so the second time you already know what's going on and where to look for all the important information and what to disregard. It won't take long to recognise the opcode routines and after a few p-code programs you will be able to F8 and F10 as fast through the code as any normal linear program without getting confused or lost in the dark codewoods. You know now where the opcode for the next instruction is loaded into the al register, yes, step over it and you will see 0a loaded into the said register. This is the opcode for any _rtc function calls, ie: _rtcRightCharVar, _rtcLeftCharVar, rtcBeep, and actually takes us back to the main program executable for a short time. Our next line of the disassembly from above is:
59a7ec: 0a ImpAdCallFPR4: _rtcLeftCharVar :Get characters to left of variableYou can see the 0a opcode and in this case it is for _rtcLeftCharVar. This returns a string containing a specified number of characters from the left side of the specified string. With the 0a routine you always have a Call eax. This is our red flag in this routine and to see what function is going to be called you should step into it (F8). Below is what it looks like when stepping through with our beloved softice.
:0f0fe7a6 0fb70e movzx ecx, word ptr [esi] :Land here :0f0fe7a9 8b55ac mov edx, [ebp-54] :0f0fe7ac 8b048a mov eax, [eecx*4+edx] :0f0fe7af 0bc0 or eax, eax :0f0fe7b1 0f8478790000 jz 0f10612f :0f0fe7b7 0fb77e02 movzx edi, word ptr [esi+02] :0f0fe7bb 83c604 add esi, 04 :0f0fe7be 03fc add edi, esp :0f0fe7c0 ffd0 call eax :Step into this call to see calling function :0f0fe7c2 3bfc cmp edi, esp :0f0fe7c4 0f855b790000 jnz 0f106125 :0f0fe7ca 33c0 xor eax, eax :Clear eax for next opcode :0f0fe7cc 8a06 mov al, [esi] :Next opcode into al :0f0fe7ce 46 inc esi :0f0fe7cf ff248594ed0f0f jmp ]eax*4+0f0fed94] :Jmp to opcode routineDon't worry about any code except the call eax. F8 or step into this call and you will see this:
jmp [MSVBVM50!rtcLeftCharVar]F10 or step over this instruction and you will land in the following code:
********* 9 lines of code before this ********* :0f00b8d6 8d7df0 lea edi, [ebp-10] :0f00b8d9 ff750c push dword ptr [ebp+0c] :0f00b8dc 52 push edx :0f00b8dd e81b000000 call MSVBVM50!rtcLeftVar :0f00b8e2 8bf0 mov esi, eax :0f00b8e4 a5 movsd :0f00b8e5 a5 movsd ********* 13 lines of code after this **********When you land here there are a couple of ways to proceed. If you want to you can step over (F10) the rtcLeftVar function and do a dump of the eax register as this holds a pointer to what values have been returned from the called function. Remember though that the memory that eax points to doesn't always have the pointer or variable right in our face as I explained before. If you have forgotten already click here. In this case the pointer is at eax + 08. Do a dump of this pointer at eax + 08 will show what values have been returned from the rtcLeftvar function.
59a7f1: 04 FldRfVar local_020C :Load variable or push onto stack 59a7f4: 04 FldRfVar local_0094 :Same againYou have already stepped through this FldRfVar routine twice before so you should make quick work of it yourself. Not so hard really is it. Now you know the drill, stepping over the xor eax, eax instruction so we better get ready for the next opcode. Looking at our disassembly the next instruction is:
59a7f7: Lead0/eb FnLenVar :Get length of variableThe opcode we are expecting to be loaded into the al register would then be eb, but what we see get loaded is fb. If you ever get something like this or as we shall see a fe opcode for SubVar don't panic. Take the jump and you will land straight into another 4 lines of code with the first instruction being a xor eax, eax. Looks familiar no. Yep clearing the al register for the next opcode. Continue stepping through the next couple of instructions and you will see the al register being loaded with the expected eb opcode. This is used to find the length of a string or variable. FnLenVar could possibly mean FindLengthVariable or FunctionLengthVariable, something like that but at least we know what it is going to do. The opcode is loaded and now we take the jump and land here:
:0f106890 0fbf06 movsx eax, word ptr [esi] :0f106893 03c 5 add eax, ebp :0f106895 50 push eax :0f106896 e85583fdff call MSVBVM50!__vbaLenVar :Step in here :0f10689b 50 push eax :0f10689c 33c0 xor eax, eax :You know what this is by now! :0f10689e 8a4602 mov al, [esi+2] :Load next opcode :0f1068a1 83c603 add esi, 03 :0f1068a4 0fbf06 jmp [eax*4+0f0fed94]I will walk you through this function a little bit more, just as an example but you really should be getting a handle on this p-code debugging by now after these few disassembled commands. You know there are 3 methods as described above in regards to finding what values functions return. This time I will take you through the second method as it varies slightly from what I mentioned above but hopefully it will give you an insight into how there can be slight variations to the return values thus you will be able to tackle these yourself when you come across them. What you need to do is step into (F8) the __vbaLenVar function call and you will land smack into the following code:
********* 8 lines of code before this ********* :0f0dec04 57 push edi :0f0dec05 e875000000 call MSVBVM50!__vbaLenVarB :Bytes required by string :0f0dec0a 8b4708 mov eax, [edi+08] :Result from funct call into eax :0f0dec0d 99 cqd :Basically clear edx register :0f0dec0e 2bc2 sub eax, edx :0f0dec10 d1f8 sar eax, 1 :Divide eax by 2, holds true length :0f0dec12 894708 mov [edi+08], eax :Store true string length :0f0dec15 8bc7 mov eax, edi :0f0dec17 5f pop edi :Restore registers :0f0dec18 5e pop esi :0f0dec19 5d pop ebp :0f0dec1a c20800 ret 0008 :Let's get the fuck outta hereNow in the second method I said 'Stepping through the code for a few instructions you will come to MSVBVM50!SomeFunction. Step over this (F10) and the returning call updates the eax register with a pointer to what values have been returned from the called function. An easy dump of the eax register (d eax) shows exactly what we need in our data window of softice'. This is not quite true with __vbaLenVar. What we should be expecting back from something like this function call is not a pointer but an actual variable in hex of the length of the string or variable passed to this function. So we think that eax register holds the length then, well yes and no, read on. Visual Basic uses unicode to store and manipulate strings. Unicode uses 2 bytes for each character instead of one thus the value that is returned into the eax register is the actual bytes used to store the string or variable which is two times the size of the string. Look at the 4th line of code after the __vbaLenVar call. SAR is the assembly instruction for shift arithmitic right. This is the same as doing a divide on the destination register. In this case we have sar eax, 1 which is the same as doing a division by 2 (eax / 2) on the eax register. If you don't understand why this is you need to read up on bitwise arithmetic. This then leaves us with the true length of the string or variable that was originally passed to the __vbaLenVar function. So the eax register does hold the return result after the function call but the true result was not revealed until several more assembly instructions were performed. Keep an eye out for these little variations when debugging your p-code functions. As for the __vbaLenVar function you know exactly where to go when you come across this again to get the relevant information.
59a7fb: 28 LitVarI2: (local_022C) 0x4 (4) :Constant 4 pushed onto stackSo the opcode for this instruction is 28 and that is what we see getting loaded into the al register as you step through the code. Not too sure what LitVarI2 means actually, but from the disassembly you can see it is getting a variable in this case 4 and storing it away for later use. The next opcode is a SubVar (Subtract a variable) so you should be able to guess that this 4 is going to be used in that subtraction in some way. Anyway when you jump to the LitVarI2 code it looks very similar to the FldRfVar asm code. Here is what you land in:
:0f0fdecb 0fbf3e movsx edi, word ptr [esi] :0f0fdece 66c70402f0200 mov word ptr [ebp+edi], 0002 :0f0fded4 668b4602 mov ax, [esi+02] :Move variable 4 into ax :0f0fded8 83c604 add esi, 04 :0f0fdedb 6689442f08 mov [ebp+edi+08], ax :Store it here for later :0f0fdee0 03fd add edi, ebp :0f0fdee2 57 push edi :0f0fdee3 33c0 xor eax, eax :Not even going to say any more :0f0fdee5 8a06 mov al, [esi] :SubVar opcode 9c into al :0f0fdee7 46 inc esi :0f0fdee8 ff248594ed0f0f jmp [eax*4+0f0fed94] :Jump to SubVar routinePretty straight forward on that opcode aye. Well for the last disassembled instruction we are going to use method 3 to cut to the heart of the overbloated VB code to see what is happening and you will see how truely overbloated this VB code really is. A mamoth amount of assembly that our poor tired computers have to execute just to do a simple subtraction. You can then understand why these Visual Basic programs compile to a ridiculous size that take up more and more of my miniscule 20Gb hard drive. Anyway here is the last instruction that I am going to trace through with you from our disassembly:
59a800: Lead0/9c SubVar :Subtract something from somethingYou take the jump at :0f0fdee8 and land here:
:0f0fe0b3 8d1dcd24100f lea ebx, [MSVBVM50!__vbaVarSub] :Self-explanitory :0f0fe0b9 eb26 jmp 0f0fe0e1 :Jmp 10 lines down :0f0fe0bb 8d1d8619100f lea ebx, [MSVBVM50!__vbaVarMul] :0f0fe0c1 eb1e jmp 0f0fe0e1 :0f0fe0c3 8d1d7b13100f lea ebx, [MSVBVM50!__vbaVarDiv] :0f0fe0c9 eb16 jmp 0f0fe0e1 :0f0fe0cb 8d1deb20100f lea ebx, [MSVBVM50!__vbaVarIdiv] :0f0fe0d1 eb0e jmp 0f0fe0e1 :0f0fe0d3 8d1dcc0e100f lea ebx, [MSVBVM50!__vbaVarAdd] :0f0fe0d9 eb06 jmp 0f0fe0e1 :0f0fe0db 8d1db20a100f lea ebx, [MSVBVM50!__vbaVarAnd] :0f0fe0e1 0fbf3e movsx edi, word ptr [esi] :Land here :0f0fe0e4 03fd add edi, ebp :0f0fe0e6 57 push edi :0f0fe0e7 ffd3 call ebx :Eventually takes us to actual sub calc :0f0fe0e9 57 push edi :0f0fe0ea 33c0 xor eax, eax :0f0fe0ec 8a4602 mov al, [esi+02] :Load our next opcode into al :0f0fe0ef 83c603 add esi, 03 :0f0fe0f2 ff248594ed0f0f jmp [eax*4+0f0fed94] :Jmp to opcode routineYou've probably guessed that anytime the program does a multiply, division, signed integer division, addition, logical and, you will always end up in this piece of code. The effective address is loaded into the ebx register and several lines below this you will see a call ebx which will lead us eventually through the dark codewoods to the mathmatical calculation that is being performed. Lets start tracing through all this code and see where eventually the actual subtraction calculation takes place. Step through the code till you get to the call ebx at line :0f0fe0e7. Step into (F8) this call and you will land in this screenfull of code. I won't display it all here, just the first and last five lines of code. There are 32 lines of code, just step over all of it until you get to the jmp instruction. Take this jump. Here is an excerpt of this code leading to our subtraction calculation.
:0f1024cd 55 push ebp :Step through all this code :0f1024ce 33c0 xor eax, eax :0f1024d0 8bec mov ebp, esp :0f1024d2 83ec18 sub esp, 18 :0f1024d5 390564f0100f cmp [0f10f064], eax ......... ......... :32 lines of code in all before the jmp ......... :0f102525 03c1 add eax, ecx :0f102527 3d43010000 cmp eax, 00000143 :0f10252c 0f8710650000 ja 0f108a42 :0f102532 0fb6902828100f movzx edx, byte ptr [eax+0f102828] :0f102539 ff2495c825100f jmp [edx*4+0f1025c8] :Take this jmpWe now land at the following 3 lines of code. Getting close to our actual subtraction calculation now. In fact the variables that are going to be used in the subtraction are loaded into two registers.
:0f108bbe 0fbf4f08 movsx ecx, word ptr [edi+08] :Constant 4 into ecx :0f108bc2 8b7608 mov esi, [esi+08] :Registration Code length :0f108bc5 e99b99ffff jmp 0f102565The first line loads a constant (4) into the ecx register. If you remember a short way back in the tutorial or look at your disassembly for the code instruction before the SubVar you should no doubt understand where this 4 came from. If you have forgotten already click here. Stands to reason then that this constant (4) that was pushed onto the stack is one of the variables that will be used in the subtraction calculation. The next line of code loads for me 0a into the esi register or in decimal terms the number 10. It just so happens that my registration code serial I entered was 10 characters long and also two instructions before on our disassembled code we had:
59a7f7: Lead0/eb FnLenVar :Get length of variableIt returned the length of our registration code serial we entered. That being 10 or 0a hexadecimal which is what has been loaded into the esi register. Now we know what is going to be subtracted, Registration Code - 4 = Result. Take the jmp and you will land in this final piece of code where the actual subtraction takes place and the result is stored:
:0f102565 8bd6 mov edx, esi :edx holds Registration Code length :0f102567 8bf9 mov edi, ecx :edi now holds constant 4 :0f102569 2bd1 sub edx, ecx :Actual subtraction, finally :0f10256b 33fe xor edi, esi :0f10256d b800000080 mov eax, 80000000 :0f102572 85f8 test eax, edi :0f102574 0f8530680000 jnz 0f108daa :0f10257a 66be0300 mov si, 0003 :0f10257e 895308 mov [ebx+08], edx :Save result, 0a - 4 = 6 :0f102581 668933 mov [ebx], si :0f102584 8bc3 mov eax, ebx :0f102586 5f pop edi :Restore stack :0f102587 5e pop esi :0f102588 5b pop ebx :0f102589 8be5 mov esp, ebp :0f10258b 5d pop ebp :0f10258c c20c00 ret 000c :Lets get the fuck outta hereSo after over 40 lines of code and I don't know how many cpu clock cycles we finally get to the heart of the calculation. It should be self explanitory no! Our registration code length is moved from the esi register to the edx register. The constant (4) is moved from the ecx register to the edi register, then we finally get our subtraction, edx (0a) - ecx (4) = 6, which is left in the edx register. 6 lines down the result is stored to memory address [ebx+08] for later use. The stack is then cleaned up and we take the ret instruction which takes us all the way back to the instruction after our call ebx. What an effort for just one simple subtraction and you can start to understand why computer programs are getting more overbloated and take more cpu power to run when a simple subtraction requires this much code. You have delved deep into the dark codewoods here and have an in depth knowledge now of what is happening where and when thus if you ever come across a subtraction again you can step quickly through to the relevant code instructions and not be confused.
When I cracked this program I used smartcheck and softice. Stepping through the code as explained above in part 1 of this tut and printing page after page of assembly code. By the time I had finished there was over 100 pages of assembly that made up the algorithm. In fact so much I almost couldn't be bothered with it all. I coded the key generator in Visual Basic 6 (P-Code compiled of course, ha the irony of it all) and it came out to a mere 4 pages of VB code before it was compiled. Hmmmmm. Anyway for the purpose of this tutorial I have used Exdec to copy and paste the relevant parts of the p-code disassembly into the tut. You will get a better understanding still if you use softice to delve into any functions that you are not clear with. If I wrote the tutorial how I cracked it as in part 1 with all the relevant softice code you can see it would really be a book now wouldn't it, not a tutorial.
Fire up Now You're Cooking. After the program has loaded the About dialog box pops up. Says about must registering after 60 days and also has a registration button to click on if we decide to register. Down the bottom of the main screen you have Owner: (UNREGISTERED) and also Reg. # (NONE). Now we know where our registered name and serial is going to show in the program. Before I moved on I ran the program with a file and registry monitor program to see if anything interesting popped up. Nothing of interest showed in the registry monitor. Under file monitor it showed access to the MSVBVM50.dll (Microsoft Visual Basic Virtual Machine 5) so we know that the program is a Visual Basic 5 compiled program, good to know straight off for debugging purposes. After alot of scrolling we see the program accessing a NYC.ini file and a NYC.key file in the \NYC511\USER directory. Well looks like NYC uses a key file for its protection scheme. Open it up with a hexeditor and we see a small key file only 06d hex long with unregistered user glaring at us like a neon sign. Not even encrypted at all. Straight away we can guess this is where our registered name will be placed. In fact I just overwrite this with Cracked by Gimmee and after 15 minutes of debugging to make the key file accept our new registered name the NYC.key file was cracked. As you will see shortly the programmer put a huge amount of effort into locking the front door to keep us out but the back door was wide open. Anyway this was not the purpose of the exercise but was interesting nevertheless. We are going to go through the locked and bolted front door. We can either boot the fucka in with brute force or use our skills as reversers and pick the lock thus nobody ever know we have been there.
Well we know it's a Visual Basic program so first thing to do is run it through Smartcheck, our VB debugger. Open NYC.exe with Smartcheck and it pops up that NYC.exe has been compiled to p-code. Damn, this means Smartcheck will be of limited value but can still be useful to glean gems of information. Start the program under Smartcheck and after a while you will be at the main screen of NYC with the About dialog box in your face. Click on Register..., then click on Reg Code...Now we end up with a form that asks us for our password. I thought this might be where our registered name would come from so I entered gimmeegimmee then clicked the OK button. As you will see later on this was somewhat off base but didn't matter, we gotta start somewhere. Next up we are asked for our Registration Code. I entered 1234567890. Click on OK button and a messagebox pops up telling us "Improper Code. Try again". Knew it couldn't be that easy aye. Go back to the Smartcheck mainscreen and End program. This terminates NYC so we can have a looksee at some of the results. At the main program results window you will see about 7 or 8 lines of code. The last two are what interests us. Should look something similar to this in your Nyc.exe - Program Results window in Smartcheck:
213 + frmGCMDI_Load 2072 frmAbout_Activate 2078 + cmdAboutReg_Click 2438 + cmdRegCode_Click 2619 + Command1_Click :Expand this thread, ie: click + sign 2902 + cmdOKCS_ClickCommand1_Click is the event when we entered our Password (gimmeegimmee) and clicked the OK button. cmdOKCS_Click is the event when we entered our Registration Code (1234567890) then clicked the OK button. Click on the plus sign (+) of Command1_Click to expand this thread so we can see what's going on. You can see that the Password we entered (gimmeegimmee) has each character uppercased starting at the first letter. This is done 10 times for each character. At the end of this a variant appears, in our case 511511. From this we don't know why each letter is uppercased 10 times or the variant 511511 is calculated. Doesn't matter for now, just write down what we know and the variant 511511. We come across it again later on so we are already a little wiser where it comes from when it does appear again. Your program results screen should have looked something similar to this:
2861 Mid(VARIANT:String:"gimmeegi...", long:12, VARIANT:Integer:1) 2862 UCase(VARIANT:String:"e") 2863 UCase(VARIANT:String:"511511") 2864 Left(VARIANT:String:"511511", long:1)Next click on the plus sign (+) of cmdOKCS_Click to expand this thread. Only about 30 lines displayed this time. At the bottom you see the messagebox function which is what was displayed for our Improper Code. Try again message. Not much else that would help us to solve why the messagebox popped up so now it's time to use our lockpick called softice and start opening this front door in stealth. Close Smartcheck and make sure you have softice and any required exports loaded to debug Visual Basic 5. As a minimum for VB make sure msvbvm50.dll and oleaut32.dll are loaded or enter the following in your winice.dat file:
59A781: 3e FLdZeroAd local_01FC ...... Code removed, will use space to comment on previous instructions ...... 59A78A: 0a ImpAdCallFPR4: _rtcTrimVarHopefully you still get a good understanding of what is going on though. Lets do it. You should see something similar to the following:
Proc: Main Reg Routine 59A76C: 4b OnErrorGoto 59A76F: 04 FLdRfVar local_01FC 59A772: 21 FLdPrThis 59A773: 0f VCallAd 7b3fc348 59A776: 19 FStAdFunc local_01F8 59A779: 08 FLdPr local_01F8 59A77C: 0d VCallHresult 7b3fbe88 59A781: 3e FLdZeroAd local_01FC ...... Should land about here in softice ...... 59A7A3: 28 LitVarI2: ( local_020C ) 0x1 (1) 59A7A8: f5 LitI4: 0x4 4 (....) 59A7AD: 04 FLdRfVar local_0094 59A7B0: 04 FLdRfVar local_021C 59A7B3: 0a ImpAdCallFPR4: _rtcMidCharVar :1234567890 ...... A constant (1) and (4) are pushed onto the stack right before the Mid function. _rtcMidCharVar will return the 4th character from the start of our Registration Code (1234567890). Result = 4 ...... 59A7C2: 28 LitVarI2: ( local_020C ) 0x1 (1) 59A7C7: f5 LitI4: 0x2 2 (....) 59A7CC: 04 FLdRfVar local_0094 59A7CF: 04 FLdRfVar local_021C 59A7D2: 0a ImpAdCallFPR4: _rtcMidCharVar :1234567890 ...... A constant (1) and (2) are pushed onto the stack right before the Mid function. _rtcMidCharVar will return the 2nd character from the start of our Registration Code (1234567890). Result = 2 ...... 59A853: 04 FLdRfVar local_024C 59A856: Lead0/ef ConcatVar :13567890 59A85A: Lead1/f6 FStVar ...... All the code leading up to the contantation function is removing the 2nd and the 4th characters from our Registration Code. Which was the 2 and 4 thus after the ConcatVar function our Registration Code will look like this: 13567890 ...... 59A865: 04 FLdRfVar local_0134 59A868: 04 FLdRfVar local_0144 59A86B: Lead0/ef ConcatVar :42 ...... This ConcatVar has taken our 2nd (2) and 4th (4) character that was removed from our Registration Code before and joined them together in reverse. So now we have 42 ...... 59A890: 0a ImpAdCallFPR4: 516d60 :Call to subroutine, will check out soon 59A895: 04 FLdRfVar local_0174 59A898: 04 FLdRfVar local_0164 59A89B: Lead0/40 NeVarBool :Something compared here 59A89D: 1c BranchF: 59A8A3 :Must jump here 59A8A0: 1e Branch: 59b0f4 :Lamer, didn't even pass 1st checkWe have our first check on our registration code here. Have a look where the Branch 59b0f4 takes you in your disassembly. Yep all the way to the end of the subroutine and Improper Code message boxes. We need to check what two values are being compared at 59a89b: NeVarBool so we take the BranchF jump.
:0f106e8f 0fbf4308 movsx eax, word ptr [ebx+08] :0f106e93 8b550c mov edx, [ebp+0c] :0f106e96 8945fc mov [ebp-04], eax :0f106e99 db45fc fild dword ptr [ebp-04] :0f106e9c dc5208 fcom real8 ptr [edx+08] :0f106e9f dd5df8 fstp real8 ptr [ebp-08] :0f106ea2 dfe0 fstsw ax :0f106ea4 9e sahf :0f106ea5 721b jb 0f106ec2 :0f106ea7 dd4208 fld real8 ptr [edx+08] :0f106eaa dc5df8 fcomp real8 ptr [ebp-08] ......Some strange instructions here, FILD, FCOM, FSTSW. These are FPU (Floating Point Unit) or arithmetic coprocessor instructions. Softice has a FPU window and you turn it on with the softice command WF. A little explanation about the coprocessor follows for those not in the know. I have lamely torn it straight from a tutorial "Cracking a simple protection IF you understand the coprocessor" by Wyatt, 29 June 1998. Hey Wyatt if you object let me know and I will reword it. Just this tut is already getting way to long and I'm starting to get lazy with it. Thanks.
Register ST Content (80 bit) 0 1 2 3 4 5 6 7 status word register 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 B C3 ST C2 C1 C0 IR P U O Z D ILet us concentrate on bit 14, bit 10-8 and bit 13-11. Bit 14 and bit 10-8 represent the "condition code". This is really important for comparing two numbers. But more later. Bit 13-11 represent the TOS (Top of Stack). The TOS is therefore a pointer to the Top of our 8 register stack. All instructions without a special operand uses the TOS (or better the register at which the TOS points) to calculate. So for example the register 3 has the value 5 and the TOS has the value 3.
TOS=3 ST(0)=Reg3=5 ST(1)=Reg4=? ST(2)=Reg5=? ST(3)=Reg6=? ST(4)=Reg7=? ST(5)=Reg0=? ST(6)=Reg1=? ST(7)=Reg2=?The instruction "FCHS" will then change the sign of Reg3. So after this the content of Reg3 is -5. I will explain now only some typical commands because it is too much to show all in one essay.
TOS=4 ST(0)=Reg4=7 ST(1)=Reg5=? ST(2)=Reg6=? ST(3)=Reg7=4 ST(4)=Reg0=? ST(5)=Reg1=? ST(6)=Reg2=? ST(7)=Reg3=?The instruction "Fadd ST(3), ST(0)" will then add ST(0) to ST(3).
condition code C3 C2 C1 C0 0 0 0 0 Operand1 greater Operand2 0 0 0 1 Operand1 less than Operand2 1 0 0 0 Operand1 equals Operand2 1 x x 1Operands not comparable
:0f106e8f 0fbf4308 movsx eax, word ptr [ebx+08] :0f106e93 8b550c mov edx, [ebp+0c] :0f106e96 8945fc mov [ebp-04], eax :0f106e99 db45fc fild dword ptr [ebp-04] ...... Loads 23 into ST0 register. Don't know where number has been derived from yet. ...... :0f106e9c dc5208 fcom real8 ptr [edx+08] ...... Compares 23 in ST0 register with real8 value stored at [edx+08] ...... :0f106e9f dd5df8 fstp real8 ptr [ebp-08] :0f106ea2 dfe0 fstsw ax :0f106ea4 9e sahf :0f106ea5 721b jb 0f106ec2 :0f106ea7 dd4208 fld real8 ptr [edx+08] :0f106eaa dc5df8 fcomp real8 ptr [ebp-08] ......We know we have a decimal value 23 is loaded into the ST0 register and compared with a real8 value at [edx+08]. We don't know where the value 23 came from yet but what is it being compared to? You can't just do a data dump of [edx+08] because you will only see the value in real8 format. We could assemble a couple of instructions that convert the real8 to an integer and load it into the eax register but in this case there is an easier way. Step down to the line of code:
:0f106ea5 721b jb 0f106ec2 :0f106ea7 dd4208 fld real8 ptr [edx+08]Because our compare failed we will end up taking this jump. But look at the instruction following the jb 0f106ec2. fld real8 ptr [edx+08]. Fuck me if that's not the address that holds the real8 value we compared 23 with. Fld is the instruction that converts a real8 number to an integer and loads it into the ST0 register. So all we have to do is make sure we don't take the jump and step over the Fld instruction and our second compare value will be loaded into the ST0 register for our tired squinting eyes. To do this step down to the jb 0f106ec2 instruction and change the ip register to execute the next line of code :0f106ea7 (reip = 0f106ea7). Step over (F10) the fld real8 ptr instruction and you will see the number 42 loaded into the ST0 register. We know where that came from and if you don't you haven't been paying attention at all. Maybe best you go to bed or pick up the PS2 instead.
...... 59A890: 0a ImpAdCallFPR4: 516d60 :Better look in here now 59A895: 04 FLdRfVar local_0174 59A898: 04 FLdRfVar local_0164 59A89B: Lead0/40 NeVarBool :42 compared to something ......We need to see what is happening in the subroutine called by the instruction ImpAdCallFPR4: 516d60. Its not very long and I have cut it down to the few important instructions. See below:
516CE2: Lead0/eb FnLenVar :Return length of registration code (13567890) = 8 516CFD: Lead3/68 ForVar: (when done) 516D4A :Loop through code 516D13: 0a ImpAdCallFPR4: _rtcMidCharVar :Get each character 516D2F: 0b ImpAdCallI2 _rtcAnsiValueBstr :Get Ansi value of character 516D37: Lead0/94 AddVar local_00D4 :Add to previous value and total 5516D44: Lead3/7e NextStepVar: (continue) 516D03 :Do for all characters 516D50: 28 LitVarI2: ( local_00C4 ) 0x64 :Value to be used in Mod calc 516D55: Lead0/a4 ModVar :Total divided by 64 hex, use remainder as check 516D5C: 14 ExitProcNow we know where our second compare value comes from and it's all pretty straight forward. The subroutine takes our Registration Code (13567890), 2nd and 4th value removed obviously, and returns the Ansi value of each character in the registration code and adds them together.
Code Ansi Value (Hex) Addition (Hex) 1 31 31+0=31 3 33 33+31=64 5 35 35+64=99 6 36 36+99=CF 7 37 37+CF=106 8 38 38+106=13E 9 39 39+13E=177 0 30 30+177=1A7So we get for our total 1A7 hex. This total is divided by 064 hex and the remainder is what is used as the second compare value. In softice eax = 1A7, ebx = 64h, and the remainder will be stored in the dx register. After the IDIV instruction dx = 17h which is 23 decimal and that's exactly the unknown value we saw for the compare. We can now put this into our Registration Code. Remember that the 4th and 2nd characters are swapped for the compare so we must put the 2 at the 4th position and the 3 at the 2nd position. Our registration code will now look like this:
59A8A3: 28 LitVarI2: ( local_020C ) 0x1 (1) 59A8A8: f5 LitI4: 0x4 4 (....) 59A8B3: 0a ImpAdCallFPR4: _rtcMidCharVar ...... Get 4th character from Registration Code (13567890) :Returns 6 ...... 59A8C2: 28 LitVarI2: ( local_020C ) 0x1 (1) 59A8C7: f5 LitI4: 0x3 3 (....) 59A8D2: 0a ImpAdCallFPR4: _rtcMidCharVar ...... Get 3rd character from Registration Code (13567890) :Returns 5 ...... 59A8E1: f5 LitI4: 0x3 3 (....) 59A8EC: 0a ImpAdCallFPR4: _rtcLeftCharVar ...... Get 1st 3 characters from Registration Code (13567890) :Returns 135 ...... 59A8F7: Lead0/eb FnLenVar 59A8FB: 28 LitVarI2: ( local_022C ) 0x4 (4) 59A900: Lead0/9c SubVar ...... Gets length of Registration Code (13567890) = 8 and subtracts 4 :Result = 8-4=4 ...... 59A90C: 0a ImpAdCallFPR4: _rtcRightCharVar ...... Get Registration Code starting at the end of code - 4 (13567890) :Returns 7890 ...... 59A914: Lead0/ef ConcatVar ...... Join 123 & 7890. Effectively removed the 6th character from original Registration Code. Result = (1237890) ...... 59A923: f5 LitI4: 0x2 2 (....) 59A92E: 0a ImpAdCallFPR4: _rtcLeftCharVar ...... 59A939: Lead0/eb FnLenVar 59A93D: 28 LitVarI2: ( local_022C ) 0x3 (3) 59A942: Lead0/9c SubVar ...... 59A94E: 0a ImpAdCallFPR4: _rtcRightCharVar ...... 59A956: Lead0/ef ConcatVari'm sure you can even work out what is happening here from the disassembly. Basically it removes the 3rd character (actually the 5th character in our original Registration Code). So we end up with a Registration code looking like this now, 137890, character 5 was removed this time. Pretty straight forward so far. Next we have a whole page of code that looks like this
59A97C: 3a LitVarStr: ( local_022C ) E 59A981: f5 LitI4: 0x5 5 (....) 59A986: 6c ILdRf local_0260 59A989: Lead1/b0 Ary1StVarCopy 59A98B: 3a LitVarStr: ( local_022C ) A 59A990: f5 LitI4: 0x6 6 (....) 59A995: 6c ILdRf local_0260 59A998: Lead1/b0 Ary1StVarCopy 59A99A: 3a LitVarStr: ( local_022C ) N 59A99F: f5 LitI4: 0x7 7 (....)All that is happening here is that an array is being set up so E=5, A=6, N=7 and so on for the word EANRLIOSTH. So the array looks like this:
E = 5 A = 6 N = 7 R = 8 L = 9 I = 4 O = 3 S = 2 T = 1 H = 0All very straight forward yes! The next piece of code we encounter is a little strange. What it does is load our 5th and 6th characters from our original Registration Code, remember the characters 5 and 6 that were removed, and compares them with the array (EANRLIOSTH) from above. If it does find a match then the corresponding letter will be exchanged for its equivalent array number, other than that I know no more. Well we know that 5, 6 is not going to equal any of the characters from the array. We have numbers and the array is letters of the alphabet for a start. This only needs to concern us if our next beggar off cracker check fails us anyway which is coming up mighty quick. Here's the extremely cut down version of the routine I have just been talking about:
59AA1F: Lead3/68 ForVar: (when done) 59AA6A :Start of compare loop 59AA30: Lead1/96 Ary1LdRfVar :Load letter from array 59AA32: Lead0/33 EqVarBool ...... Compare array character with our 5th Registration Code variable (5) ...... 59AA34: 1c BranchF: 59AA43 :Take branch if not equal, we branch here ...... The above loads a letter from the array (ie: H, T, S, O, etc), loads our 5th Registration Code variable (5) and compares them. If not equal then jmp to below otherwise do something that I have not worked out what it is. As you will see soon it is of no concern to us. ...... 59AA4E: Lead1/96 Ary1LdRfVar 59AA50: Lead0/33 EqVarBool ...... Compare array character with our 6th Registration Code variable (6) ...... 59AA52: 1c BranchF: 59AA61 :Take branch if not equal, we branch here ...... The above loads a letter from the array (ie: H, T, S, O, etc), loads our 6th Registration Code variable (6) and compares them. If not equal then continue with loop otherwise do something that I have not worked out what it is. As you will see soon it is of no concern to us. ...... 59AA64: Lead3/7e NextStepVar: (continue) 59AA25 ...... Keep looping until array compare finished ......Almost at our next compare now, you can just about feel it can't you. Here is what we get:
59AA6A: 04 FLdRfVar local_01E4 :Our variable (6) 59AA6D: 3a LitVarStr: ( local_022C ) . :A decimal place (. or 2e hex) 59AA72: Lead0/ef ConcatVar :Join em, result = 6. 59AA76: 04 FLdRfVar local_01D4 :Our variable (5) 59AA79: Lead0/ef ConcatVar :Join em, result = (6.5) ...... Fairly basic aye! Just gets our 6th (6) and 5th (5) variable from our original Registration Code (1332567890) and adds a decimal place in between them thus turning it into a fraction, 6.5. ...... 59AA94: cd NeR8 :Compare our 6.5 with something 59AA95: 2f FFree1Str local_01FC 59AA98: 1c BranchF: 59AB54 :Our 2nd test, need to take this jmp :Beggar off loser only passed 1 test crackerStep into the NeR8 function so we can see what is being compared to what. A good guess is that the value 6.5 will be compared to something, but what we don't yet know. No surprise though that if you step over the compare and end up at the BranchF you will not take the jump and end up being sent to some new message boxes, "The registration code you are entering is valid only for version, it is not valid for this version of NYC", blah blah blah. Make sure you have your coprosessor window (wf) on in softice and you will see this:
EAX=000000CD EBX=FFFFFE04 ECX=007200DC EDX=00000000 ESI=0059AA95 EDI=0084F70C EBP=0084FA08 ESP=0084F708 EIP=0F0FEC1E o d I s z a P c CS=015F DS=0167 SS=0167 ES=0167 FS=1087 GS=0000 --------------------------------------------------------------------------------------------- ST0 0 ST4 empty ST1 6.5 ST5 empty ST2 empty ST6 empty ST3 empty ST7 empty --------------------------------------------------------------------------------------------- :0F0FEC1E D9C9 fxch st(1) :Exchange ST(0) with ST(1) :0F0FEC20 DED9 fcompp :Compare ST(0), ST(1) and increment :TOS by 2Hmmmmm what is happening here? You see our variable 6.5 in the ST(0) register then it is exchanged with whatever is in the ST(1) register thus our 6.5 moves to ST(1) and a value 0 is loaded into ST(0). Then there is a compare of the two registers.
59AB59: 04 FLdRfVar local_0094 ...... Our Registration Code minus 2nd, 4th, 5th, 6th characters ...... 59AB5C: 3a LitVarStr: ( local_022C ) - :A hash character, 2D hex = "-" 59AB66: Lead3/fe FnInStr4Var :VB function, Find in string a variable 59AB70: Lead0/33 EqVarBool 59AB75: 1c BranchF: 59AB7B :Another pin falls from the locked front door 59AB78: 1e Branch: 59b0f4 :The programmer wins, beggar off loserAhhhhh this snippet of code is just too easy. Programmers the world over always seem to check for a hash character. What you see is our Registration Code loaded minus the 2nd, 4th, 5th and 6th characters. 1231007890 which leaves us with 137890. The command FnInStr4Var is a function in Visual Basic and translates to something like, Find in this String this Variable, with our string being the remainder of our Registration Code and the variable obviously a hash or - or 2d hex. Better go back to our Registration Code then and add a hash, at the moment it doesn't matter where, the function is only looking to see if we have a hash so it doesn't matter what position. My Registration Code now looks like this which gets us past the 3rd check. The lock on that front door is slowly being picked, 3 pins down and more to tumble.
59AB7B: f5 LitI4: 0x6 6 (....) 59AB86: 0a ImpAdCallFPR4: _rtcRightCharVar ...... Get six characters of our Registration Code (1378-90) starting from the end. Returns 378-90 ...... 59AB8B: f5 LitI4: 0x1 1 (....) 59AB96: 0a ImpAdCallFPR4: _rtcLeftCharVar ...... Get the first character from the returned result from above. 1st character of 378-90 = 3 ...... 59AB9E: 3a LitVarStr: ( local_022C ) - 59ABA4: Lead0/40 NeVarBool ...... Compare our 3 with another hash, - ...... 59ABAD: 1c BranchF: 59ABB3 :You pass the 4th check 59ABB0: 1e Branch: 59b0f4 :He he, beggar off loserOhhh this is just all too easy isn't it. Now we know that the hash we inserted into our Registration Code should be positioned at the 6th position from the end of our code. At this point I decided that our Registration Code seemed too short. Was just a gut feeling and I also wanted to add some letters of the alphabet anyway. Thus with the letters added and the hash repositioned our new extra modified Registration Code looks like this:
59ABB6: Lead0/eb FnLenVar 59ABBA: 28 LitVarI2: ( local_022C ) 0x6 (6) 59ABBF: Lead0/9c SubVar ...... Get length of our Registration code and subtract 6. This is the position of the hash. ...... 59ABCB: 0a ImpAdCallFPR4: _rtcLeftCharVar ...... Get all the Registration Code characters to the left of the hash. Returns 137890a ...... 59ABD3: f5 LitI4: 0x5 5 (....) 59ABDE: 0a ImpAdCallFPR4: _rtcRightCharVar ...... Get all the Registration Code characters to the right of the hash. Returns bcdef ...... 59ABE6: Lead0/ef ConcatVar ...... Join them together, Registration Code is now without the hash, 137890abcdef ......That was easy aye. Well the next piece of code should look mighty familiar if you have been paying attention. Yep another check for a hash. Looks like the programmer removed the hash we just checked for so he could check if we have another hash in our Registration Code and we better because we are coming up quickly to our 5th beggar off loser or continue on good cracker branch.
59AC0E: 04 FLdRfVar local_0094 ...... Our Registration Code minus 2nd, 4th, 5th, 6th, hash ...... 59AC11: 3a LitVarStr: ( local_022C ) - :A hash character, 2D hex = "-" 59AC1B: Lead3/fe FnInStr4Var :VB function, Find in string a variable 59AC1F: Lead1/f6 FStVar 59AC2C: Lead0/33 EqVarBool :Our check to see if we are losers or not 59AC2E: 1c BranchF: 59AC34 :Branch here to pass the 5th check 59AC31: 1e Branch: 59b0f4 :Beggar off loser crackerWell if we are to pass this 5th Registration Code check we better go back and put another hash into the Registration Code itself. Anywhere will do for now as this VB function only checks for the hash presence not its position. Remember the 2nd and 4th characters will be different again. Here's what my Registration Code looks like now and this will get us past 5 checks.
59AC34: 28 LitVarI2: ( local_020C ) 0x1 (1) 59AC39: f5 LitI4: 0x1 1 (....) 59AC44: 0a ImpAdCallFPR4: _rtcMidCharVar ...... Well we know this will get 1 character starting from the beginning of the string. Returns 1 from string 12 ......Where the hell did that string 12 come from. We know the returned variable from the string (1) but that 12 doesn't look familiar. Time to cut into the heart of the overbloated beast and disect the mysterious string. Whenever we get the MidCharBstr function there is always a call before it that loads the string the function will use. This is the first call to step into with our beloved softice.
:0f04308c 56 push esi :0f04308d ff750c push dword ptr [ebp+0c] :0f043090 e89787fcff call 0f00b82c :Step (F8) into this call :0f043095 83f8ff cmp eax, -01 :0f043098 0f8498a70300 jz 0f07d836 :0f04309e ff7514 push dword ptr [ebp+14] :0f0430a1 ff7510 push dword ptr [ebp+10] :0f0430a4 50 push eax :0f0430a5 e8068cfcff call MSVBVM50!rtcMidCharBstrAfter stepping into this call you just have to keep tracing until you get to the next call and step into that, and the next call and step into that. Three calls deep now so just keep tracing and you will come across:
59AC4E: 28 LitVarI2: ( local_023C ) 0x1 (1) 59AC53: f5 LitI4: 0x2 2 (....) 59AC5E: 0a ImpAdCallFPR4: _rtcMidCharVar ...... Get the second character from our string 12. Obviously returns 2 ...... 59AC68: a9 AddI2 ...... Adds the returned characters together. Result = 1 + 2 = 3 ...... 59AC8F: 0a ImpAdCallFPR4: _rtcMidCharVar ...... Gets the third character from our Registration Code. ...... 59AC97: 3a LitVarStr: ( local_0270 ) - :Its going to have to be a hash 59AC9D: Lead0/40 NeVarBool :Our 6th check 59ACA6: 1c BranchF: 59ACAC :Better take this branch 59ACA9: 1e Branch: 59b0f4 :Not bad to get this far :but you're still outta here LAMERWell to sum all that up we have the length of our Registration Code, adjusted to suit removed characters. From this returned length the first decimal value is added to the second decimal value. The result being where our second hash should be positioned. In our case this is 3 which means our second hash should be the 3rd character of the Registration Code. Remember as we add or remove characters from the Registration Code this hash position will change. Not so hard after all and now we have passed six checks. The next few lines of code in the disassembly just remove our second hash so our Registration Code now looks like this:
59ACF5: 04 FLdRfVar local_00C4 59ACF8: 04 FLdRfVar local_0124 59ACFB: 04 FLdRfVar local_0184 59ACFE: 04 FLdRfVar local_0094 59AD01: 10 ThisVCallHresult 54ed80 :Step into hereLooking at the disassembly of this call there are another five calls at the start of the subroutine. Looking at each one of these you see that each one of these subroutines are very short and the main functions are rtcMidCharVar, rtcRightCharVar, and plenty of loops. Looks like plenty of Registration Code manipulation in those five calls. Browsing through the four pages of disassembled code for this subroutine also turned up another EARNLIOSTH array being set up as we encountered before, and also more of rtcMidCharVar, rtcRightCharVar, and rtcLeftCharVar functions which leads us to believe that there is even more manipulation of our Registration Code. Tracing through all these loops and calls would be a time consuming nightmare with softice thus I decided to go back and use Smartcheck to see what it would turn up. Before this though I did notice one function call that needed immediate attention and obviously a revision of our Registration Code. Check out this first part of the subroutine:
54EA7E: Lead3/70 ForStepVar 54EA87: 10 ThisVCallHresult 51979c :Registration Code manipulation 54EA92: 10 ThisVCallHresult 524ca4 :in these five calls 54EA9D: 10 ThisVCallHresult 50fb38 54EAA5: 10 ThisVCallHresult 513458 54EAAD: 10 ThisVCallHresult 51979c 54EAB5: Lead3/7e NextStepVar: (continue) 54EA84 54EAC6: 3a LitVarStr: ( local_00D4 ) @ :Looks like we need an @ character 54EAD0: Lead3/fe FnInStr4Var :Find in string a @ character 54EAE0: Lead0/9c SubVar ...... ......Well we have dealt with this function only recently with our hashes. Find in string a variable and it looks like it's looking for the @ character. As yet we don't know why or where it should be placed but we just take a guess and see what happens. I decided to put it 2nd to last. No reason really in case you are wondering, just wanted to see what happens. Remember now the 2nd and 4th characters and 2nd hash position will change. My Registration Code now looks like this:
59ACFE: 04 FLdRfVar local_0094 59AD01: 10 ThisVCallHresult 54ed80 :Our Registration Code mangled in here 59AD14: Lead0/40 NeVarBool :Better check what is being compared 59AD16: 1c BranchF: 59AD5B :Our 7th check, we better branch 59AD27: 3a LitVarStr: ( local_022C ) Registration aborted. Bad password 59AD32: 0a ImpAdCallFPR4: _rtcMsgBox :You are still a loserTime to trace into the compare and see what we can see. One of the compare variables was:
212 + frmGCMDI_Load 2410 frmAbout_Activate 2419 + cmdAboutReg_Click 2482 + cmdRegCode_Click 2523 + Command1_Click 2813 + cmdOKCS_Click :Expand this thread, ie: click + signExpand the cmdOKCS_Click thread. This is the event when we pushed the OK button after entering our Registration Code. Scroll down to the command that removes the last hash and leaves us with our Registration Code that is about to be manipulated. It is about sequence number 2872 for me but can be different each time you run it through Smartcheck. Here's what you will see in your program results window:
2872 Right(VARIANT:String:"137-890a...",long:10) 2873 Mid(VARIANT:String:"137890ab...", long:2, VARIANT:Integer:1) 2874 Mid(VARIANT:String:"137890ab...", long:1, VARIANT:Integer:1) 2875 Mid(VARIANT:String:"317890ab...", long:4, VARIANT:Integer:1) 2876 Mid(VARIANT:String:"317890ab...", long:3, VARIANT:Integer:1)Registration Code now looks like this:
.... 2890 Mid(VARIANT:String:"318709ba...", long:5, VARIANT:Integer:1) 2891 Mid(VARIANT:String:"318709ba...", long:6, VARIANT:Integer:1) 2892 Mid(VARIANT:String:"318709ba...", long:7, VARIANT:Integer:1) 2893 Mid(VARIANT:String:"318709ba...", long:8, VARIANT:Integer:1) 2895 Right(VARIANT:String:"380bdc@e...",long:9) ....Taken the 2nd, 4th, 6th, 8th characters and moved them to the end of the code. Registration Code now looks like this:
.... 2905 Mid(VARIANT:String:"dc@ef179...", long:4, VARIANT:Integer:1) 2906 Mid(VARIANT:String:"dc@ef179...", long:3, VARIANT:Integer:1) 2907 Mid(VARIANT:String:"dc@ef179...", long:2, VARIANT:Integer:1) 2908 Mid(VARIANT:String:"dc@ef179...", long:1, VARIANT:Integer:1) ....Reverse the Registration Code. Registration Code now looks like this:
a511511@Pirate@Copy 5a1115@1iParetC@poy (Swap pairs) yop@CteraPi1@5111a5 (Reverse) 5ayop@CteraPi1@5111 (Last 2 add to front, reverse them) 1115@1iParetC@poya5 (Reverse) a51115@1iParetC@poy (Last 2 add to front) ao5y1115@1iParetC@p (Last 2 to character position 2,4) p@CteraPi1@5111y5oa (Reverse) 5oap@CteraPi1@5111y (Last 3 add to front) 51o1ayp@CteraPi1@51 (Last 3 to character position 2,4,6) 15@1iParetC@pya1o15 (Reverse) 1o1515@1iParetC@pya (Last 4 add to front) 1@op1y5a15@1iParetC (Last 4 to character position 2,4,6,8) @1poy1a5511@PirateC (Swap pairs)Now we just need to add our 2nd, 4th, 5th, 6th, and our two hash characters to make the inputed Registration Code. It now looks like this:
59AF40: Lead0/40 NeVarBool 59AF42: 1c BranchF: 59AF48 :Our 8th check, a free pass for us 59AF45: 1e Branch: 59b0f4 59AF48: 0a ImpAdCallFPR4: 57d16c :Last call before program is cracked 59AF4D: 05 ImpAdLdRf: 5d3870 59AF50: 3a LitVarStr: ( local_022C ) Name: :If you made it this far then :program fully cracked :You ain't no lamerSo whatever our last checks are must be in the ImpAdCallFPR4: 57d16c. Time for you to disassemble this call. Oh shit another 8 pages of code to trawl through. This really is too much, but don't dispair we are soooo close. The 1st, 2nd, 3rd, and 4th page are all date checks. Unless you totally have fucked up your settings on the puter you will pass all of these no worries at all with the main branch for these checks at 57cdbe.
57CDAC: Lead0/74 GtVarBool 57CDBE: 1c BranchF: 57CDF5 :We automatically take this branch 57CDD8: 3a LitVarStr: ( local_0184 ) Your PC's date/time settings appear...Next two checks we pass as well without any effort. You will see something like "before RegNo999 check". Remember our magic number from our Passwrod routine, 511511. Looks like this is being compared to 999999 to make sure it's not greater than that and also the next check seems to check if it is not less than zero. Didn't do much homework on these two compares again because we have passed them regardless so I could be somewhat right or wrong but I don't care. Which in turn leads us to our important piece of code where we failed our last check which brought our attention to that annoying message box.
57CEB2: 3a LitVarStr: ( local_0184 ) Before AppCode check 57CEBC: Lead0/ef ConcatVar 57CEC0: 3a LitVarStr: ( local_01A4 ) AC= 57CEC5: Lead0/ef ConcatVar 57CED4: Lead0/ef ConcatVar ...... Join the string Before AppCode check AC= with our last character that hasn't been checked in our Registration code. a511511@Pirate@Copy. String now looks like Before AppCode check AC=A. The character is uppercased somewhere in all the previous character manipulations so it doesn't matter if you enter lowercase or uppercase in the final Registration Code. ...... 57CEF0: 1b LitStr: K :What's our A character being compared to? 57CEF3: Lead0/3d NeStr ...... Our character A is loaded and compared with the character K ...... 57CEF8: 1c BranchF: 57CF34 :Take this jump and call yourself a cracker 57CF09: 3a LitVarStr: ( local_0184 ) Registration key is not valid for NYC... 57CF17: 3a LitVarStr: ( local_01A4 ) Please check with support@ffts.com.So we have a couple of strings joined together (Before AppCode check AC=) with the last character that hasn't been checked from our Registration Code. It is then checked with the character K. We have to change our 'a' to a 'k' in our Registration Code. Why the fuck would you join all these strings together only to take our character off it for the compare. Seems like a waste of code to me and the program grows bigger for nothing. Maybe the programmer can explain these small idiocities to me. Let's go back and alter our Registration Code so we can get past this check.
Proc: 52f82c 52F6DF: 3a LitVarStr: ( local_00D8 ) A 52F6E4: f5 LitI4: 0x7 7 (....) 52F6EC: Lead1/b0 Ary1StVarCopy :A = 7 52F6EE: 3a LitVarStr: ( local_00D8 ) B 52F6F3: f5 LitI4: 0x4 4 (....) 52F6FB: Lead1/b0 Ary1StVarCopy 52F6FD: 3a LitVarStr: ( local_00D8 ) C 52F702: f5 LitI4: 0x6 6 (....) 52F70A: Lead1/b0 Ary1StVarCopy 52F70C: 3a LitVarStr: ( local_00D8 ) D 52F711: f5 LitI4: 0x2 2 (....) 52F719: Lead1/b0 Ary1StVarCopy 52F71B: 3a LitVarStr: ( local_00D8 ) E 52F720: f5 LitI4: 0x1 1 (....) 52F728: Lead1/b0 Ary1StVarCopy 52F72A: 3a LitVarStr: ( local_00D8 ) F 52F72F: f5 LitI4: 0x3 3 (....) 52F737: Lead1/b0 Ary1StVarCopy 52F739: 3a LitVarStr: ( local_00D8 ) G 52F73E: f5 LitI4: 0x5 5 (....) 52F746: Lead1/b0 Ary1StVarCopy 52F748: 3a LitVarStr: ( local_00D8 ) H 52F74D: f5 LitI4: 0x9 9 (....) 52F755: Lead1/b0 Ary1StVarCopy 52F757: 3a LitVarStr: ( local_00D8 ) J 52F75C: f5 LitI4: 0x8 8 (....) 52F764: Lead1/b0 Ary1StVarCopy 52F766: 3a LitVarStr: ( local_00D8 ) K 52F76B: f5 LitI4: 0x0 0 (....) 52F773: Lead1/b0 Ary1StVarCopyYou have seen a similar instance to this before, you remember yes? Anyway it is just setting up an array so if any of the characters in our password equal, A, B, C, D, E, F, G, H, J, K then it will be assigned the correspnding number. Anything else will be ignored.
A = 7 B = 4 C = 6 D = 2 E = 1 F = 3 G = 5 H = 9 J = 8 K = 0 g i m m e e g i m m e e 5 1 1 5 1 1And thus we get our Password magic number 511511. Too easy aye.