Cracking a VB6-Pcode Crackme |
Visual Basic | |
by disavowed | ||
fra_00xx 98xxxx handle 1100 NA PC | 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... | |
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".
:66105620 :66105623 :66105626 :66105628 :6610562B :6610562E :66105635 :66105651 |
0FBF06 FF3428 33C0 8A4602 83C603 FF2485146D1066 0FBF06 0FBF06 |
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] movsx eax, word ptr [esi] |
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))
Loop
______
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)
Else
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 : )
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 : (