VB6-Pcode Reversing
Cracking a VB6-Pcode Crackme
Visual Basic
Visual Basic
3 October 1999
by CyberBlade
Courtesy of Reverser's page of reverse engineering
slightly edited
by disavowed
Woa! It was indeed about time that somebody cared to write a good essay about the most important visual basic functions (and breakpoints) for reversing purposes!
As disavowed (ex-St0rmer) writes: "As for now, put on your thinking caps, load msvbvm60.dll into Symbol Loader, and learn, learn, learn!".
Note also that this is one of those rare essays made in 'team work' between cracker and protector, therefore you'll have the advantage of being able to "feel" both sides of the coin...
CyberBlade affirms that he is "not good at writing tutorials", but, after readig this, I state that such an assertion is patently untrue.
C'mon, vbasic buffs, read this and enjoy!
Ah yes, you'll find inside the archive pgccrkme.zip the following files: the crackme, the "keygen" and a list of VB6 exports (.txt) CyberBlade used as a reference...
There is a crack, a crack in everything That's how the light gets in
( )Beginner (x)Intermediate ( )Advanced ( )Expert

It's about time someone wrote a VB6-Pcode tutorial!! I'm proud to have a crackme I made as the target of this fine tutorial. (And to settle the confusion, I changed my name from 'St0rmer' to 'disavowed' after having made this crackme)

CyberBlade does an outstanding job of analyzing msvbvm60.dll (the VB6 "virtual machine" (not quite as cool as it sounds)) and also the crackme itself, and how to follow exactly what's going on inside the program by following the pcode.

Some great points made by CyberBlade:
Watch for 'xor eax, eax' in msvbvm60.dll, it means the jmp coming up will be a vb function! (F8!)
Play around inside the vb functions with softice! Find out what's going where, how the stack is being utilized (esp), and how return values are derived.
Want to really REVERSE vb crap? Then reverse oleaut32.dll, where all the "real" calculations are being made.

Not only did CyberBlade go to the trouble of writing this tutorial, he was also nice enough to include a brute-force keygen including source code. With some effort, an actual "non-brute-force" keygen could be made.

As our knowledge of VB reversing increases, our fear of seeing vb dll's in imports will hopefully diminish. Next project: a true VB5/VB6 decompiler? As for now, put on your thinking caps, load msvbvm60.dll into Symbol Loader, and learn, learn, learn! :)

- disavowed (disavow@optonline.net)
VB6-Pcode Reversing
Cracking a VB6-Pcode Crackme
Written by CyberBlade

I'm not good at writing tutorials, but ACiD BuRN asked me to write one, so if you don't like it ACiD BuRN is to blame =P

As I told you, this is a VB6 CrackMe compiled to P-Code. Many people think that it is very hard to crack such CrackMes, but it isn't. All you need is a bit training and the knowledge of common VB breakpoints. I suggest you print this text to have it handy while debugging with Soft-Ice.

The serial consists at least of 4 characters, the Name must have at least 6 characters.

Tools required
NuMega SmartCheck, Softice and (W32dasm 8.9)


Perform the following steps to reverse the protection mechanism:

Start the CrackMe and complete the Registartion Info:

I entered --> Name: CyberBlade | Code: 6786789

Now press "Ctrl + d" to get into Softice and set a breakpoint on hmemcpy. I assume you know how to do that : ) If not go and ask somebody in the street. Then return to the CrackMe and push the "Check" - Button.....B00M....What happened ?! We're in Softice : ). Now you can (believe it or not) remove the breakpoint (bc *). Push F12 exactly 7 times, till you get into the core of MSVBVM60.DLL.

Now you can set some breakpoints. Having taken a closer look at the results of SmartCheck I found the following breakpoints:

Breakpoints marked with a * are those I always set when cracking a VB program.

After having set all these breakpoints we can proceed...to the deep codewoods...Don't be afraid, CyberBlade is with you hehehe =P

Turn the "floating point window" on, coz you will need it later. (wf)

It's important to know that you don't have to step into any calls except those that invoke the functions (mostly "call ebx") and the "call eax" which takes you back to PGC Crackme. Trace through the code (F10). Don't bother to understand any asm-instruction before reaching an area with a series of XOR instructions (each one is followed by a "jmp dword ptr") making up a "XOR pattern".







movsx eax, word ptr [esi]
push dword ptr [eax+ebp]
xor eax, eax
mov al, byte ptr [esi+02]
add esi, 00000003
jmp dword ptr [4*eax+66106D14]

movsx eax, word ptr [esi]
push dword ptr [eax+ebp]
mov dword ptr [eax+ebp], 00000000
xor eax, eax
mov al, byte ptr [esi+02]
add esi, 00000003
jmp dword ptr [4*eax+66106D14]

movsx eax, word ptr [esi]
fld dword ptr [eax+ebp]
xor eax, eax
mov al, byte ptr [esi+02]
add esi, 00000003
jmp dword ptr [4*eax+66106D14]


Having reached the above location you can put breakpoints on the addresses 66105620; 66105635 and 66105651 to mark them for the run. It's always this pattern which leads you to the functions.

The jump at location 6610562E will take you to location 66106BC6 and as I mentioned before there's a call to a function of VB6:

* Reference To: MSVBVM60.__vbaLenBstr
:66106BC6 E853A1F5FF call 66060D1E

Use F8 to trace into the __vbaLenBstr function:

Exported fn(): __vbaLenBstr - Ord:0147h
:66060D1E 8B442404 mov eax, dword ptr [esp+04]
:66060D22 85C0 test eax, eax
:66060D24 7405 je 66060D2B
:66060D26 8B40FC mov eax, dword ptr [eax-04]
:66060D29 D1E8 shr eax, 1
:66060D2B C20400 ret 0004

Trace to location 66060D22 and type "d eax" and you will see the Name entered in the data window, in my case C.y.b.e.r.B.l.a.d.e (W.i.d.e.C.h.a.r.a.c.t.e.r.f.o.r.m.a.t). Leave that function, and type "? eax". A number specifiing the length of the entered name, in my case "10", will be displayed.

Keep on tracing (F10) till you get to the "XOR pattern" again. Again it takes you to the function __vbaLenBstr.This time it returns the length of the Code entered, in my case 6786789 with a length of "7".

Now continue through the deep codewoods, notice that you will pass through a "XOR pattern" that follows the one mentioned above. But this time no important function is called (__vbaI2var), so keep on tracing......You reach again the above mentioned "XOR pattern" that calls a function. This time it is calling "__vbaStrCat". It is used to concatenate two strings. In this case "CyberBlade & 6786789" results in "CyberBlade6786789":

Exported fn(): __vbaStrCat - Ord:0195h
:66060B5F 55 push ebp
:66060B60 8BEC mov ebp, esp
:66060B62 8D4508 lea eax, dword ptr [ebp+08]
:66060B65 50 push eax
:66060B66 FF7508 push [ebp+08]
:66060B69 FF750C push [ebp+0C]
:66060B6C FF15E8061166 call dword ptr [661106E8] <== This is a call to OleAut32 where the "real operations" are executed. Trace into this call to see how the strings are concatenated.
:66060B72 85C0 test eax, eax
:66060B74 0F8C2EEB0200 jl 6608F6A8
:66060B7A 8B4508 mov eax, dword ptr [ebp+08]
:66060B7D 5D pop ebp
:66060B7E C20800 ret 0008

I dunno why this is done. I didn't notice that this value is being used later. After I had successfully cracked the CrackMe, the author (St0rmer) told me that he had included some misleading dummy operations to confuse I don't know who, but not me ! : )
The above code sequence seems to be an example for this.

Knowing this you needn't worry about this function any longer. Ignore it and keep on cracking...

Again you pass the unimportant "XOR pattern". But then you get to the "XOR pattern" you've set your breakpoints on. The __vbaLenBstr function is called. It returns the length of "CyberBlade". Performing the same steps again it returns the length of our Code entered "6786789".

In the following I won't mention the "XOR patterns" any longer. I assume you've memorized it now. : ) I'll only show you the functions called by it:

__vbaStrCat ==> used to concatenate the strings "CyberBlade & 6786789" Why does this CrackMe do everything twice ?! Strange CrackMe !

Carry on, trace through the Code and you will get to another "XOR pattern". You might think: "Oh no ! not again !", but this is what P-Code is like... : )

:66106BF9 33C0 xor eax, eax
:66106BFB 8A06 mov al, byte ptr [esi]
:66106BFD 46 inc esi
:66106BFE FF2485146D1066 jmp dword ptr [4*eax+66106D14]
:66106C05 33C0 xor eax, eax
:66106C07 8A06 mov al, byte ptr [esi]
:66106C09 46 inc esi
:66106C0A FF248514711066 jmp dword ptr [4*eax+66107114]
:66106C11 33C0 xor eax, eax
:66106C13 8A06 mov al, byte ptr [esi]
:66106C15 46 inc esi
:66106C16 FF248514751066 jmp dword ptr [4*eax+66107514]
:66106C1D 33C0 xor eax, eax
:66106C1F 8A06 mov al, byte ptr [esi]
:66106C21 46 inc esi
:66106C22 FF248514791066 jmp dword ptr [4*eax+66107914]
:66106C29 33C0 xor eax, eax
:66106C2B 8A06 mov al, byte ptr [esi]
:66106C2D 46 inc esi
:66106C2E FF2485147D1066 jmp dword ptr [4*eax+66107D14]

You can set breakpoints on the important jmps, so u won't miss them next time.

Okay, this "XOR pattern", well rather the jump will take you to the part of MSVBVM60.DLL where the functions for mathematical operations like Division, Multiplication, And, Subtraction are called:

:66105F52 EB26 jmp 66105F7A
:66105F54 8D1D8B841066 lea ebx, dword ptr [6610848B] (__vbaVarSub)
:66105F5A EB1E jmp 66105F7A
:66105F5C 8D1DC4251066 lea ebx, dword ptr [661025C4] (__vbaVarMul)
:66105F62 EB16 jmp 66105F7A
:66105F64 8D1D42881066 lea ebx, dword ptr [66108842] (__vbaVarDiv)
:66105F6A EB0E jmp 66105F7A
:66105F6C 8D1DE5251066 lea ebx, dword ptr [661025E5] (__vbaVarIdiv)
:66105F72 EB06 jmp 66105F7A
:66105F74 8D1D842D1066 lea ebx, dword ptr [66102D84] (__vbaVarAnd)

:66105F7A 0FBF3E movsx edi, word ptr [esi]
:66105F7D 03FD add edi, ebp
:66105F7F 57 push edi
:66105F80 FFD3 call ebx <== trace into this call, if you want to see how the mathematical operations are executed (it's the "call ebx" I mentioned before)

If you trace into the call (F8) you will find yourself in the "__vbaVarMul" function:


Exported fn(): __vbaVarMul - Ord:01DDh
:6610848B FF742404 push [esp+04]
:6610848F FF74240C push [esp+0C]
:66108493 FF742414 push [esp+14]
:66108497 FF152C001166 call dword ptr [6611002C] (OleAut32!VarMul) <== This is a call to OleAut32, where the real operations are executed.
:6610849D 85C0 test eax, eax
:6610849F 0F8CE4420000 jl 6610C789
:661084A5 8B442404 mov eax, dword ptr [esp+04]
:661084A9 C20C00 ret 000C

Trace into the call, at location 653BDE6A you will see that how "2" is multiplicated with "0".

.--==[ Always keep in mind: Trace through the Msvbvm60 code, don't bother to understand anything, but wake up, when you pass any of the Xor patterns I mentionend, there will mostly be jmps to locations where the functions are called ! ]==--.

Okay, same thing as before, I will only show you the functions that are called...

__vbaLenBstr takes length of serial ' Len("6786789") ==> result: 7
__vbaVarSub Subtraction 7 - 0 ==> result: 0
OleAut32!VarCmp compare 7 with 0
__vbaLenBstr takes length of Name ' Len("CyberBlade") ==> result: 10
__vbaVarSub 10 - 1
__vbaLenBstr takes length of Name ==> result: 10
__vbaVarSub 10 - 2
__vbaLenBstr takes length of Name ==> result: 10
__vbaVarSub 10 - 3
__vbaLenBstr takes length of Name ==> result: 10
__vbaVarSub 10 - 4
__vbaLenBstr takes length of Name ==> result: 10
__vbaVarSub 10 - 5
__vbaLenBstr takes length of Name ==> result: 10
__vbaVarSub 10 - 6
__vbaLenBstr takes length of Name ==> result: 10
__vbaVarSub 10 - 7
__vbaLenBstr takes length of Name ==> result: 10
__vbaVarSub 10 - 8
__vbaLenBstr takes length of Name ==> result: 10
__vbaVarSub 10 - 9
__vbaLenBstr takes length of Name ==> result: 10
__vbaVarSub 10 - 10

**Note: I have only listed the important function (I didn't mention __vbaI2Var, __vbaVarAdd, sometimes OleAut32!VarCmp)

No explanations will be given to those senseless function. But reversing the functions above is a good exercise anyway : P

Trace on and be careful, not to miss the "call eax" which will take you back to PGC CrackMe. There you will see jmps to many functions used by the CrackMe. You can trace over the call to the function rtcMidCharVar. Now push "d eax" and you will see the last character of your Name (whatever you have entered as "Name") in the data window. Follow the thread and you will see how the AscII value of this character is taken. To give you a better feeling of the manipulations that are performed on the Name entered I'll list them here:

1. Read the last character of the Name ==> "e"
2. Take the AscII value ==> 101
3. Add 6 to 101 ==> 107
4. (107)3 ==> 1225043
5. Divide this number by 10. 1225043 / 10 ==> 122504,3
6. Subtract 6 from the number. 122504,3 - 6 ==> 122498,3
7. Round the result Round(122498,3) ==>122498

**Note: Sorry for shortening this part so much, but the tutorial otherwise would exceeds 11 pages : ). If something isn't understood, don't hesitate to contact me via e-mail.

These steps are performed on every character of the Name, remember to use the "floating point window", while debugging the mathematical functions.
Performing these steps on every single character of the Name, beginning with the last will lead to the following result:

CyberBlade 122498
CyberBlade 119096
CyberBlade 109267
CyberBlade 148148
CyberBlade 37219
CyberBlade 172794
CyberBlade 122498
CyberBlade 112480
CyberBlade 204832
CyberBlade 38896


The original VB-Code, that performs these operations:

dim KeyName(1 to 50)

For x = 1 To Len(NameText.Text)
KeyName(x) = Asc(NameText.Text)
KeyName(x) = Key(x) = Key(x) + 6
KeyName(x) = Key(x) ^ 3
KeyName(x) = Key(x) / 10
KeyName(x) = Key(x) - 6
KeyName(x) = Round(Key(x))
Next x


Here the manipulations on the Serial:

1. Take the AscII values of the key, beginning with the last one and stopping one before the first character. ==> 57
2. Use the Cosine function on the AscII value ==> 0,89986682...
3. Multiply 0,89986682... with 600 ==> 539,9200961..
4. result = result + (PositionOfCharacterInString - 1)4 ==> 1835,9200961...
5. Round the result Round (1835,9200961...) ==> 1836
6. Take the absolute value of the number Abs(1836) ==> 1836


6786789 1836
6786789 1137
6786789 269
6786789 417
6786789 528
6786789 14


The original VB-Code, that performs these operations:

x = Len(NameText.Text)

Do While x > 1
x = x - 1
KeySerial(x) = Asc(Mid(NameText, x + 1, 1))
KeySerial(x) = Cos(KeySerial(x))
KeySerial(x) = KeySerial(x) * 600
KeySerial(x) = KeySerial(x) + x^4
KeySerial(x) = Round(KeySerial(x))
KeySerial(x) = Abs(KeySerial(x))


Now these values are reduced to one:


ResultSerial = 0 - 14 + 528 - 417 + 269 - 1137 + 1836 = 1065

ResultSerial = 1065 - 6

ResultSerial = 1059

The original VB-Code, that performs these operations:

Plus = True

For x = 1 To (Len(CodeText) - 1)
Plus = Not Plus
If Plus = True Then
ResultSerial = ResultSerial + KeySerial(x)
ResultSerial = ResultSerial - KeySerial(x)
End If
Next x

ResultSerial = ResultSerial - 6


Now, after the CrackMe has calculated the final result of the Serial generated by the entered Code, it calculates the final result of the Number generated by the entered Name:

ResultName = 37219
ResultName = 37219 / 1000
ResultName = 37


The original VB-Code, that performs these operations:

ResultName = KeyName(Round(Len(Text1.Text) / 2))
ResultName = Round(ResultName / 1000)

While debugging all these operations I thought this CrackMe would never end... But then...
...these two values were compared. Yeahh !!!

If ResultName = ResultSerial (37 = 1059) means a correct Code was entered. Oh my god ! Now we have to reverse all these operations in order to code a KeyGen. It would be easier to code a BruteForcer for this CrackMe. In fact this is, what I did. You can find the Source Code (VB6 : ) ) in the Zip-file. It only needs 20sec to find a correct serial. But this isn't a very elegant, so if you think you're good you can try to code a KeyGen and send it to me : )

Final Notes

If I made mistakes and explained things wrongly then please mail me and I will correct my mistakes. (Feel free to correct it yourself and send me the new version :-) )You can also mail me if there is anything you are not clear about. Any comment is appreciated. Don't hesitate to send your remarks to: CyberBlade@gmx.net

GREETS FLY OUT TO: --==[ ACiD BuRN, AfKayAs, Bjanes, DnNuke, Eternal Bliss, Fisch, Gizmo, Joseph, MiZ, St0rmer, Torn@do, Tusk, Vladimir, Volatility, ^The Uplifter^ and The uncle ]==--

Sorry, if I forgot someone : (

You are deep inside reverser's page of reverse engineering, choose your way out:

redhomepage redlinks redsearch_forms red+ORC redhow to protect redacademy database
redreality cracking redhow to search redjavascript wars
redtools redanonymity academy redcocktails redantismut CGI-scripts redmail_reverser
redIs reverse engineering legal?