P-Code Compiled Visual Basic 5
Now You're Cooking Ver 5.11

  July 2002 

by Gimmee

New Zealand
I don't want anything thats just my nickname
 
There is a crack, a crack in everything. That's how the light gets in

Rating
( )Beginner (x)Intermediate ( )Advanced ( )Expert
 

 

Reversing a P-Code Compiled Registration Algorithm
from yet another Visual Basic program
Written by Gimmee

Target's URL/FTP
http://www.ffts.com/
HomePage of "Now You're Cooking"

Introduction

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.

About the Target

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.

Tools Required
Softice 4.05
Smartcheck 6.03
Hex Workshop 2.54
Brain

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.


Find some of the tools here:

crackpl tools
VB debugger website

Part 1: About P-Code Compiled Visual Basic

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 something
Let'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 stack

In 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 routine
Any 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 stack
Well 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 here
You 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 variable
You 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 routine
Don'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.

The second method is the one I usually use. That is to step into the function and step over all instructions until you get to the next visual basic call related to the original calling function. Its easy to spot so you should never miss it for each function. Stepping through the code for a few instructions you will come to MSVBVM50!rtcLeftBstr. Step over this (F10) and the returning call updates the eax register with a pointer to what values have been returned from the function. An easy dump of the eax register (d eax) shows exactly what we need in our data window of softice.

The third method is if I'm not to sure where a value has come from or if I just want to get to the heart of the beast I will use our scapel called softice and cut into the heart of this overbloated beast called visual basic to see what is going on. You will soon get to know the relevant code for each function and you can make speedy work tracing through pages and pages of irrelevant instructions until you get to the 3 or 4 lines of asm where the real work is carried out to complete the called function. I won't show the heart of each function as this tutorial will be turned into a novel and I am still too lazy to document them myself here. Shit I hate it when tutorial writers say that, "Just tell me and show me god damn it all". Hmmm now I know why the tut writers say that.

Now returning from the call eax we see our next red flag which is the xor eax, eax instruction. We know this means we are getting ready to load our next opcode into the al register. Next two instructions in our disassemby are:
59a7f1:  04  FldRfVar		local_020C		:Load variable or push onto stack
59a7f4:  04  FldRfVar		local_0094		:Same again
You 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 variable
The 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 here
Now 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.

You should have traced through the above and returned to the original call and be at the xor eax, eax instruction. Getting ready for our next opcode then. Let's look at the next instruction of our disassembly:
59a7fb:  28  LitVarI2:		(local_022C) 0x4 (4)	:Constant 4 pushed onto stack
So 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 routine	
Pretty 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 something
You 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 routine
You'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 jmp
We 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 0f102565
The 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 variable
It 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 here
So 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.

Well that covers a very small part of the disassembled file and how to understand better the debugging of p-code in softice. The next part of the tutorial will be how to reverse the registration code algorithm so install Now You're Cooking if you haven't already done so and lets reverse.

Part 2: Reversing the Registration Algorithm

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_Click
Command1_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:

1: EXP=c:\windows\system\msvbvm50.dll
2: EXP=c:\windows\system\oleaut32.dll

Start up NYC again, enter our Password (gimmeegimmee), click OK, enter our Registration Code (1234567890). Sometimes softice doesn't break on our set breakpoints in VB unless we are in the MSVBVM50.dll code before we set them. To do this and to get to the very beginning of our code routine after we click the OK button, pop up softice (F5) and set a break point on hmemcpy (bpx hmemcpy). Exit out of softice and click the OK button. You should break straight back into softice. F12 7 or 8 times until you land back in the VB virtual engine. Look at the botton of the code window in softice for something like MSVBVM50!ENGINE+1834. Now you can start stepping over all the instructions until you get to the familiar code instructions of loading an opcode into the al register. This will be the start of the subroutine that handles the check on the Registration Code we entered. Anywhere here you can also set your breakpoints on memory address, bpm 59a784, as an example. Now I'm going to use the Exdec disassembly to explain what is going on and only go back to softice code when the disassembled code doesn't show enough detail. If you use Exdec and print out this part of the routine there will be about 12 pages of code without even printing out all the calls to other subroutines so I will only go through the relevant parts to save time. Where I have cut parts of the code out it will be shown as below:
59A781: 3e FLdZeroAd               local_01FC		
......
Code removed, will use space to comment on previous instructions
......
59A78A: 0a ImpAdCallFPR4:          _rtcTrimVar

Hopefully 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 check
We 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.

Trace into the NeVarBool command until you get to the following code in softice:
: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.

Extract starts here: The coprocessor uses 8 registers, which are arranged similar to the normal stack. Every register is 80 bits long and its contents is a real number. Then the copro uses a so called "status word register". It is very similar to the well known flag register. Here is a short graphical description:
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
I
Let 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.
fld real8 ptr [ebx]
This command loads a double real from ds:ebx into the register at which the TOS points. Before this operation the TOS is decremented. So all registers are moved one position down and the last register (in our example Reg2) comes to position 0. The double real is saved in Reg2.
Fst real8 ptr [ebx]
This command is the contrary to Fld. It saves a double real at address ds:ebx. The TOS is not changed though.
Fstp real8 ptr [ebx]
This is the same as Fst but the TOS is incremented (the p stands for "pop"). After this operation the TOS points at the next register. Mostly after the saving of a number we don't need this number in a register again.
Fadd, Fsub, Fmul, Fdiv
It is completely the same as Add, Sub, Mul, Div. But the condition is that one operand must be the TOS (TOS-->ST(0)).
For example: Fadd ST(3), ST(0).
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).
So the content of Reg7 is 11 after this operation. The TOS is not changed.
Faddp, Fsubp, Fmulp, Fdivp are the same like Fadd, Fsub, Fmul, Fdiv but the TOS is incremented.
Fcom, Fcomp.
Fcom is equivalent to Cmp. But it has only one operand. Fcom compares thhis operand with the TOS and changes the condition code. You know the condition code is Bit 14 and Bit 10-8 of the status word register. Fcomp pops (increment) the TOS after the compare.
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
1
Operands not comparable
Fcompp
Fcompp has no operand. It compares ST(0) with ST(1). The TOS is incremented by two.
Fild word ptr [ebx]
Fild converts an integer in a real number and loads it in ST(0).
Fist, Fistp
Fist converts a real number from ST(0) to a word(!) integer and saves the integer at a memory location.
Fistp converts a real number from ST(0) to a dword(!) integer at a memory location and increment the TOS.

I thinks that's enough. If you want more then get a good book on coprocessor instructions. There is much more to explain. OK now back to our target. Extract ends here:

Lets now work through our coprosessor instructions:
: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.

Now we know that one part of our serial (42) is being compared to 23 but we don't know where this 23 came from. Better go find it then we will pass our first check. Remember there was one place we had not checked leading up to our compare.
......
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 ExitProc               
Now 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=1A7
So 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:

1332567890

Cool, go back to the compare in softice and you shall see that we are on the right track as the compare is correct and we take the BranchF: 59a8a3 which leads us on to more checks. Anytime we change our Registration Code it means that we will have to calculate a new 2nd and 4th value. At the start I just kept a breakpoint at the original compare in softice and had a look what the new value was, being loaded into ST0 register. After awhile this became tiresome so I coded a small routine in VB that did it for me. From now on I will just tell you that it has changed as we modify our Registration Code and insert the new values in the 2nd and 4th places respectively. You shouldn't be confused now when these numbers not so mysteriously change.

Onto the next part of our disassembled code. Pretty much the same as before, _rtcMidCharVar, ConcatVar, and SubVar seem to be plentiful. You should be getting a handle on how to quickly trace through these functions by now.
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 ConcatVar      
i'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 = 0
All 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 cracker
Step 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 2
Hmmmmm 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.

6.5 compared with 0

Well now we know what our second compare value is. As yet I still don't know where it came from but decided to try something too easy first. What happens if we just make our 5th and 6th characters of our Registration Code 0,0. Then we would be comparing 0.0 with 0. As yet we don't know if changing the Registration code would then change the second compare value. Only one thing to do and that's to try it. Remember the 2nd and 4th value will need to be recalculated (if you truely have forgotten why click here), thus the new registration code to try is this:

1231007890

Go back to the compare and yes 0.0 is compared with 0 so this enables us to pass the second test. I still don't know where the 0 comes from and I went back and entered all sorts of combinations of letters and numbers and it always was 0. As you will see this means our 5th and 6th variable of our Registration Code will always be 0. Maybe the author could explain why he has a page and a half of code doing all number of things when in the end the result is always going to equal 0 and thus it makes our job on this second test of the program just too easy.

Passing the second test moves us down to page 6 of our 12 page routine. Onto the next part of the routine. Check out the following code snippet:
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 loser
Ahhhhh 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.

17350078-90

Next piece of code and we see that a 4th check is coming up. The following snippet of code is what we see:
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 loser
Ohhh 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:

1435007890a-bcdef

Fourth check has been passed now with our modified Registration Code. Our next snippet of code has more manipulation of our Registration Code but it is still all straight forward. Basically all that has happened is the hash we had at the 6th position from the end of our code is removed. Wonder why that is?
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 cracker
Well 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.

193900-7890a-bcdef

God damn it I think this programmer might be paranoid some lamer is going to crack his program. Frankly i'm getting bored with the whole thing. I better take a break because I'm starting to rant and rave. It's 1.36 in the morn, I'm tired, thirsty and hungry and this whole tutorial is turning into what I didn't want it to be, a fucken novel.

Looking down the disassembly there seems to be another half a page of some sort of Registration Code manipulation before we course upon another check to see if we are lamers or not. A couple of instructions before the branch is another hash, it's being pushed onto the stack or something. Anyway going from what we have just been through with the previous hash checks a pretty good guess is that this half page of code is used to make sure the second hash is in the correct position as well. Here's what we have:
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!rtcMidCharBstr
After 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:

CALL [OLEAUT32!VariantChangeTypeEx]

Looks like a variant is going to have its type changed. Step into that call and we end up at our next calling routine.

CALL OLEAUT32!VarBstrFromI4

If you want you can carry on deeper and deeper into the dark codewoods. What an overbloated mess you see as you step through this pea soup. Anyway before stepping over the CALL OLEAUT32!VarBstrFromI4 take a look at the ecx register, it holds 0C hex. Thats 12 decimal as well. After the call the ecx register holds the memory address to a pointer which finally leads to the returned result which is a string 12. No surprises there then. Now where did this 0C hex come from then. We could set a memory breakpoint on where the 0C hex was loaded from and trace back from there but alas I am lazy so think of lazy methods to find the answer. Our Registartion Code (13-7890abcdef) with the 2nd, 4th, 5th, 6th characters and the hash removed is 13 or 0D hex in length. Me thinks that maybe if we discard the second hash as well (only for our calculation, will not actually physically delete it from the Registration Code) this will leave our Registration Code with a length of 0C hex or 12. What I did then was go back to the registration routine and enter different length Registration Codes and checked the return value from the VarBstrFromI4 call. All of the results were as me thinks. Our Registration Code length minus the 2nd, 4th, 5th, 6th, and the two hash characters. Now with that solved we can continue:
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 LAMER
Well 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:

137890abcdef

Well what's next? In the disassembly you can see alot of variables being pushed before a call then a few instructions below this call another check which shows if we fail and don't take the jump we get the "Registration aborted. Bad password or" error message. I would say something important is happening in here so best we have a preview of it.
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 here
Looking 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:

1336007-890ab-cde@f

With our new Registration Code set a breakpoint on 54EAD0: Lead3/fe FnInStr4Var and trace into the heart of the code so you can dump what our Registration Code looks like when it is being searched for the @ character. Remember we had five calls of Registration Code manipulation just before so goes to reason it will be all fucked up but this just enables us to confirm our suspicions. Yeah it sure looks messed up. This is what was being searched:

ab03cde@1f879

Certainly our Registration Code has been turned upside down and all over the place. Let the program run and we get the error message "Registration Aborted. Bad password or registration code". That means we have managed to get right through the subroutine that fucks up our Registration Code, back to our main registration routine but failed at the 7th compare.
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 loser
Time to trace into the compare and see what we can see. One of the compare variables was:

b03cde

Well that's easy, remember our manipulated Registration Code (ab03cde@1f879). Looks like it is comparing all the characters before the @ character but with the exception that we have dropped the first character (a) as well. And what is it being compared with?

511511

Holy cow I hope you remember where this number came from but it's a long tutorial already so if not click here. Yes that's our magic number from the Password we entered gimmeegimmee. What we need to do then is work out how the Registration Code is manipulated so we end up with 511511 before the @ character. It might have to look something like this:

a511511@1f879

Now I am going to cheat you all and just tell you what the story is with all the characters after the @ character. This is because I don't want to get to the end of the tutorial then have to go all the way back to the unmangling of the Registration Code with Smartcheck. Everything after the @ character will be used as our "registered to" name at the main screen of the program. I got right to the end of the regsistration process and the registered name was 1f879 from our code above. Also if you want to have a first name and a surname they must be separated by another @ character. That was just a bit of trial and error on my part. Now we have an idea of what our Registration Code should look like after it has been mangled. Here's what I wanted it to look like:

a511511@Pirate@Copy

To see how all this mangling takes place fire up Smartcheck and go through the whole registration process. Use the Registration Code from above, 1336007-890ab-cde@f, we know it is going to fail at the 7th check but that doesn't matter for now. We just want to see what results Smartcheck will reveal to us. Certainly alot more code than when we used it last time.
 212    +	frmGCMDI_Load
2410    	frmAbout_Activate
2419    +	cmdAboutReg_Click
2482    +	cmdRegCode_Click
2523    +	Command1_Click	
2813    +	cmdOKCS_Click			:Expand this thread, ie: click + sign
Expand 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:

318709badc@ef

This is a much easier way to see what is happening than stepping through the dark codewoods with Softice. It's so simple to see what is happening graphically right in front of our faces. Look, the function Mid is used to get the 1st two characters and swap them, then the next pair (3rd and 4th) and swap them, this done for the whole Registration Code. You can see the Registration Code change in the Details Pane as you go over each function. I will do a couple more then leave you to work through the rest.
....
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:

380bdc@ef179a

....
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:

b083a971fe@cd

So after you have gone through each step we get the Registration Code manipulation as follows:

(1) Swap pairs
(2) 2,4,6,8 character position to end of code
(3) 1,2,3,4 character position to end of code
(4) Reverse
(5) Swap pairs
(6) Swap pairs (This is silly, swapped pairs then swapped back again, really done nothing)
(7) 2,4,6 character position to end of code
(8) 1,2,3 character position to end of code
(9) Reverse
(10) Swap pairs
(11) Swap pairs (This is silly, swapped pairs then swapped back again, really done nothing)
(12) 2,4 character position to end of code
(13) 1,2 character position to end of code
(14) Reverse
(15) Swap pairs
(16) Swap pairs (This is silly, swapped pairs then swapped back again, really done nothing)
(17) 2,1 character position to end of code
(18) Reverse
(19) Swap pairs

I don't know what the story is with the silly swapping of pairs but we can cancel out the pair swapping that is shown as null and void above. Anyway theoretically if we did the above in reverse order that is step 19 to step 1 we should have our manipulated Registration Code. Only one thing for it and thats to test the above on a Registration Code. Remember the code that we came up with before, lets try it on that.
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:

@71300poy1a55-11@Pi-rateC

Enter it in and break in on the code for our 7th check to see if we are on the right track. Excellent, the 7th check is the same and we take the correct jump missing the "you are a lamer" message box, but end up with a new message box "Registration key is not valid for NYC (32-bit). For fucks sake what now, this is just never ending. We must be close though because looking at my printed disassembly we are on page 8 of 12 in the main registration routine. Looking at the disassembly we only have to get to the end of page 10 before we get past all the crap. Only 2 pages to go then. What I did now was use Smartcheck again with our newly modified Registration Code and let it run to see what the results were and how far past the 8th page we had gotten before the next check.

After our 7th check looking at our disassembled code we see more _rtcMidCharVar and _rtcLeftCharVar then _rtcGetPresentDate. In my Smartcheck program details window we find at event 4133 the VB function Now() which is the same as the _rtcGetPresentDate function. Got an idea where we are now? Scrolling down the program results window we see our registered name being worked on again, then our Registration Code has something done to it, heaps of Mid functions. After this we see Month, Day functions then more work done on our registered name to finally it finishes with three Chr functions then our messagebox. Looks like we have actually got quite some distance but where are we on our disassembled code?

Looking at our disassembled code the next beggar off loser check is at 59af42. This is almost at the end of our main routine so shit we must be close. Set a breakpoint here then. Stepping through with softice see what happens. We will end up taking the jump on our 8th check which is what we wanted anyway. I didn't even bother seeing what this check does seeing as we passed it anyway but with all the previous code having to do with the registered name manipulation I think it might have something to do with the checking of the @ in our name or something like that to confirm a valid registration name. Regardless our 8th check was a no brainer for us. So what next then:
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 lamer
So 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.

@71400poy1k55-11@Pi-rateC

Rerun Now You're Cooking with our revised Registration Code. Finally we get the message box we are looking for:

Is this registration info correct?

Name:
Pirate Copy

Registration Number:
511511

Registration Code:
@71400poy1k55-11@Pi-rateC

If everything is right click on yes and we get the "Registration Complete" message box. Click on OK button and we are dumped back into the main program with our new registration details at the bottom window pane. The last pin just fell in our locked front door, no need to kick it in after all, stealth is what we want. Finally we have produced a valid Registration Code and also have a pretty good understanding on how it was produced. One last small detail though. How was the magic number made from our password of gimmeegimmee? You can skip this if you like but I am guessing if you managed to make it this far it won't take much for you to do this one last bit of code. Here is the heart of the procedure where it takes place:
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 Ary1StVarCopy    
You 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 1
And thus we get our Password magic number 511511. Too easy aye.


Final Ravings

A long tutorial but I hope p-code cracking was made a little clearer. If I have erred on any of the information I have put forward feel free to edit this tutorial as needed but please send me the updated version. I downloaded the latest version of Now You're Cooking 5.51 and tried our Password and Registration Code which was sucessful as well. I asked Gary Hauser about this and his response was "Truth is, I could spend my life (and my next one) trying to make my software to slow down experienced crackers, but it is not worth my time trying (you know that)". Thus he has no intention of updating the registration algo in the near future. I'm sure Gary will be back though with something to challenge us at a later date. So please use this tutorial and his program as intended, to further your knowledge on p-code compiled programs, not to be a lamer and rip Gary off. I usually only write one tutorial each year so this is it for the year 2002. The July snow has arrived and my body is almost repaired for another season of snowboarding so no more tuts until next year or my body falls apart again and leaves me sitting in front of the puter with time on my hands.

Hi to my alter ego 'The Crow'

Send all correspondance and any comments both positive and negative to "thecrowatnz@hotmail.com"

adios

Gimmee (I don't want anything that's just my nickname)

Ob Duh
I wont even bother explaining to you why you should BUY this target program if you intend to use it. Please don't be a lamer and STEAL this software.