"Linkbot Pro 4.0 Beta 1"
||Code Reversing For Beginners
Program Name: linkbot4.exe
Program Type: Web utility
Program Location: Here
Program Size: 4.33 Mb
Softice - Win'95 Debugger
W32Dasm - Win'95 Disassembler
Easy ( X ) Medium ( ) Hard ( ) Pro ( )
|Solving the puzzle
LINKBOT PRO: YOUR WEBRAGE CONTROL SYSTEM
Linkbot Pro is a complete Website testing solution that can automatically scan a
Website for more than fifty potential problems, generate graphical reports detailing
what errors need to be fixed, and provide all the tools Web development teams need
to ensure the accessibility, functionality and usability of their Websites.
About this protection system
The beta 1 is a trial version and will expire the 16th of february.
Even though this program is a trial version the registering function still works... so this
is a kind of double crack me ;-)
This main focus of this tutorial is on the registration function.
To register go to the Help menu and choose
The instructions here says: When you purchase Linkbot, Tetranet will provide
you with a serial and PIN numbre that will unlock the software.
I used the following registering information:
User Name: NiXe NiXe
I don't think this this information is saved at all...
PART 1 - The Registration Screen
Try to run Linkbot Pro 4.0 and enter something in the Linkbot Registration screen.
If you get the message "Please enter an integer between 1 and 100000000" then do just that;-)
Notice the text in the messagebox that pops up: "Sorry, your PIN number is not valid for the
given serial number. Please try again". This gives us a clue about how the PIN number is
generated - it's probably calculated from the serial! Write down the bad guy message.
Create a deadlisting in W32Dasm. Among the imported dll (Dynamic Link Library) files you will
find MFC42.DLL. This tell us that the program is written in Microsoft Visual C++ using Microsoft
Foundation Classes (MFC). We better include this MFC42.DLL in the SoftIce.Ini file! Include MSVCRT.DLL too.
Now find the "Sorry, your PIN number is not valid for the
given serial number. Please try again" message. Study the surrounding code. Try to search
for the bad guy string again. Hey, the bad guy string appears many different places! This makes analysing
the deadlisting a bit more difficult because we don't know which bad guy string we got when
entering our invalid registration information.
I think it's a very good idea to disassemble, analyse and print the interesting places of a
program before starting to debug with SoftIce. But in this case we need to do a little bit of debugging
in SoftIce to find serial/PIN check.
Load Linkbot into SoftIce and go to the registration screen. Enter some registration information
and press Ctrl+d to enter SoftIce. Set a breakpoint at hmemcpy (bpx hmemcpy). Press
Ctrl+d to continue and press the OK button.
Now SoftIce breaks just before hmemcpy. Step into the hmemcpy with F10 until
you have excuted two repz movsb instructions (that's about 31 F10's). Now ds:esi points to
just after where the string was being copied from and es:edi points just after where the string was copied to.
In order to see what was copied simply do a d es:edi-4 where 4 is the number of characters
you'll see - if you only get the last part of your string you can increase 4 to the length
of your string.
The first data we see is our serial. Lets us follow the serial and monitor where it is read. Clear old breakpoints
(bc *) and set a new breakpoint at read/write access to our serial (bpr es:edi-4 es:edi rw).
If you do not remember the last 'rw' SoftIce only breaks on writes.
Press Ctrl+d and SoftIce will break in MFC42!ORD_08f1+0074. Step with F10 past MSVCRT!strtol. Now
EAX contains our serial as a number! A few lines below eax is moved to a new memory location 'MOV [ECX],EAX'.
Execute the 'MOV [ECX],EAX and clear old breakpoints (bc *) and set a new breakpoint at this new memory
location of our serial (bpr ecx ecx+3 rw) and press Ctrl+d again. Now we break here:
:004596E3 E854BA0300 Call 0049513C
:004596E8 8B03 mov eax, dword ptr [ebx] ; WE BREAK HERE
:004596EA 6800E1F505 push 05F5E100 ; 100000000
:004596EF 6A01 push 00000001 ; 1
:004596F1 50 push eax ; save serial on stack
:004596F2 56 push esi
:004596F3 E862BA0300 Call 0049515A ; This must be the function that checks
; if our serial is between 1 and 100000000!
Here we have some values being pushed before a call. To see the decimal value of the numbers in SoftIce just write
? 05F5E100 show the decimal value 100000000. Hex 1 is decimal 1. This must be the check to
see if the serial is between 1 and 100000000. Remember the message "Please enter an integer between 1 and 100000000"?
This is not really of interest so press Ctrl+d again. Now we break here:
:00459786 8B4E64 mov ecx, dword ptr [esi+64] ; 99999999 - our pin
:00459789 8B5660 mov edx, dword ptr [esi+60] ; 88888888 - our serial - WE BREAK HERE
:0045978C 51 push ecx ; save pin on stack
:0045978D 52 push edx ; save serial on stack
:0045978E 8D8818010000 lea ecx, dword ptr [eax+00000118] ; 5295176
:00459794 E89743FCFF call 0041DB30 ; call to interesting routine
:00459799 83F80A cmp eax, 0000000A ; compare on eax
:0045979C 7510 jne 004597AE
* Possible Reference to String Resource ID=00255: ""
:0045979E 6AFF push FFFFFFFF
:004597A0 6A00 push 00000000
* Possible Reference to String Resource ID=00140: "Sorry, your PIN number is not valid for the given serial num"
:004597A2 688C000000 push 0000008C
* Reference To: MFC42.Ordinal:04AF, Ord:04AFh
:004597A7 E8E4B00300 Call 00494890
:004597AC 5E pop esi
:004597AD C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 0045979C(C)
:004597AE 83F80E cmp eax, 0000000E
:004597B1 7510 jne 004597C3
:004597B3 6AFF push FFFFFFFF
:004597B5 6A00 push 00000000
* Possible Reference to String Resource ID=00272: "The serial number and PIN combination you are using is from "
:004597B7 6810010000 push 00000110
:004597BC E8CFB00300 Call 00494890
:004597C1 5E pop esi
:004597C2 C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Address:004597B1(C)
:004597C3 83F80B cmp eax, 0000000B
:004597C6 7510 jne 004597D8
This pice of code is much more interesting. Here we have a call which has our entered serial and pin as parameters!
After the calls we have a lot of compares on eax and some bad guy messages! We better debug this call to 0041DB30.
Here is the complete routine at 0041DB30 with the comments I added while tracing in SoftIce with F10:
* Referenced by a CALL at Addresses: 0041D66F ,00459794
:0041DB30 53 push ebx
:0041DB31 8B5C2408 mov ebx, dword ptr [esp+08] ; 8888 - our serial
:0041DB35 55 push ebp
:0041DB36 56 push esi
:0041DB37 57 push edi
:0041DB38 8BF9 mov edi, ecx
:0041DB3A 53 push ebx ; save serial on stack
:0041DB3B E860030000 call 0041DEA0 ; check to see if serial is 1997, 1998,
; 2353, 13579 or 131313
:0041DB40 84C0 test al, al
:0041DB42 740C je 0041DB50
:0041DB44 B80B000000 mov eax, 0000000B ; B = old serial return code
:0041DB49 5F pop edi
:0041DB4A 5E pop esi
:0041DB4B 5D pop ebp
:0041DB4C 5B pop ebx
:0041DB4D C20800 ret 0008
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 0041DB42(C)
:0041DB50 53 push ebx
:0041DB51 8BCF mov ecx, edi
:0041DB53 E8480A0000 call 0041E5A0 ; is serial 1900?
:0041DB58 84C0 test al, al
:0041DB5A 740C je 0041DB68
:0041DB5C B80E000000 mov eax, 0000000E ; return code = E
:0041DB61 5F pop edi
:0041DB62 5E pop esi
:0041DB63 5D pop ebp
:0041DB64 5B pop ebx
:0041DB65 C20800 ret 0008
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 0041DB5A(C)
:0041DB68 8B6C2418 mov ebp, dword ptr [esp+18] ; our PIN
:0041DB6C 81FB40080000 cmp ebx, 00000840 ; is our serial = 2112
:0041DB72 7514 jne 0041DB88
:0041DB74 81FD8E150B00 cmp ebp, 000B158E ; is our PIN 726414
:0041DB7A 750C jne 0041DB88
:0041DB7C B80D000000 mov eax, 0000000D ; return code = D
:0041DB81 5F pop edi
:0041DB82 5E pop esi
:0041DB83 5D pop ebp
:0041DB84 5B pop ebx
:0041DB85 C20800 ret 0008
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:0041DB72(C), 0041DB7A(C)
:0041DB88 8BC5 mov eax, ebp ; PIN
:0041DB8A 53 push ebx ; push serial
:0041DB8B 99 cdq
:0041DB8C 33C2 xor eax, edx ; some calcualtions on PIN
:0041DB8E 8BCF mov ecx, edi
:0041DB90 2BC2 sub eax, edx
:0041DB92 83E001 and eax, 00000001
:0041DB95 33C2 xor eax, edx
:0041DB97 8BF0 mov esi, eax
:0041DB99 2BF2 sub esi, edx
:0041DB9B F7DE neg esi
:0041DB9D 1BF6 sbb esi, esi
:0041DB9F 46 inc esi
:0041DBA0 E84B020000 call 0041DDF0 ; Generate real PIN
:0041DBA5 8BC8 mov ecx, eax
:0041DBA7 85F6 test esi, esi
:0041DBA9 99 cdq
:0041DBAA 7427 je 0041DBD3
:0041DBAC 33C2 xor eax, edx
:0041DBAE 2BC2 sub eax, edx
:0041DBB0 83E001 and eax, 00000001
:0041DBB3 33C2 xor eax, edx
:0041DBB5 2BC2 sub eax, edx
:0041DBB7 7440 je 0041DBF9 ; if PIN is a non-equal number jump to PIN compare
:0041DBB9 51 push ecx
:0041DBBA 8BCF mov ecx, edi
:0041DBBC E82F020000 call 0041DDF0
:0041DBC1 8BC8 mov ecx, eax
:0041DBC3 99 cdq
:0041DBC4 33C2 xor eax, edx
:0041DBC6 2BC2 sub eax, edx
:0041DBC8 83E001 and eax, 00000001
:0041DBCB 33C2 xor eax, edx
:0041DBCD 2BC2 sub eax, edx
:0041DBCF 7428 je 0041DBF9
:0041DBD1 EB25 jmp 0041DBF8
* Referenced by a (U)nconditional or (C)onditional Jump at Address:0041DBAA(C)
:0041DBD3 33C2 xor eax, edx
:0041DBD5 2BC2 sub eax, edx
:0041DBD7 83E001 and eax, 00000001
:0041DBDA 33C2 xor eax, edx
:0041DBDC 2BC2 sub eax, edx
:0041DBDE 7519 jne 0041DBF9
:0041DBE0 51 push ecx
:0041DBE1 8BCF mov ecx, edi
:0041DBE3 E808020000 call 0041DDF0 ; Generate real PIN again
:0041DBE8 8BC8 mov ecx, eax
:0041DBEA 99 cdq
:0041DBEB 33C2 xor eax, edx
:0041DBED 2BC2 sub eax, edx
:0041DBEF 83E001 and eax, 00000001
:0041DBF2 33C2 xor eax, edx
:0041DBF4 2BC2 sub eax, edx
:0041DBF6 7501 jne 0041DBF9 ; if not PIN is an equal number jump to PIN compare
* Referenced by a (U)nconditional or (C)onditional Jump at Address:0041DBD1(U)
:0041DBF8 41 inc ecx ; Add 1 to real PIN
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
:0041DBB7(C), :0041DBCF(C), :0041DBDE(C), :0041DBF6(C) ; PIN compare
:0041DBF9 3BCD cmp ecx, ebp ; Compare REAL PIN with our fake PIN
:0041DBFB 7503 jne 0041DC00
:0041DBFD 89771C mov dword ptr [edi+1C], esi
* Referenced by a (U)nconditional or (C)onditional Jump at Address:0041DBFB(C)
:0041DC00 8BC1 mov eax, ecx
:0041DC02 5F pop edi
:0041DC03 2BC5 sub eax, ebp
:0041DC05 5E pop esi
:0041DC06 F7D8 neg eax
:0041DC08 1BC0 sbb eax, eax
:0041DC0A 5D pop ebp
:0041DC0B 24FE and al, FE
:0041DC0D 5B pop ebx
:0041DC0E 83C00C add eax, 0000000C
:0041DC11 C20800 ret 0008
Now just exchange your fake PIN with the real PIN and press the OK button again!
Ehh.. hmmn. When you rerun the program you are not registered:-( I hope this is due to fact
that this is a trial version which expires the 16th february...?
PART 2 - The Expiration Check
The real protection of this program is the expiration. When the program expires you get the message
"This copy of Linkbot 4.0 Beta 1 has expired..." in a messagebox!
Set the computer's date to anything afthe the 16th february and fire up SoftIce again.
Set a breakpoint at MessageBoxA. Now SoftIce break just before
the expired messagebox appears. Press F12 until you are back into LINKBOT. Here I stepped
with F10 until I met a ret instruction. Two F10s later I saw this:
:0041F093 8BCE mov ecx, esi
:0041F095 E8E61E0000 call 00420F80 ; Display expired messagebox
:0041F09A 85C0 test eax, eax
:0041F09C 7526 jne 0041F0C4
After the call eax is zero. I tried to reverse the result of test eax,eax (r fl z). Hey,
now the program continues insted of quitting back to windows - but we still get the expired
messagebox. Now I sat the clock back to a date before the 16th
february and debugged the above code again. This time the call returned with eax=1.
How can we allways return with eax=1 and avoid the expired messagebox? I have an idea to a patch:
:0041F093 8BCE mov ecx, esi ; before the fix
:0041F095 E8E61E0000 call 00420F80
:0041F09A 85C0 test eax, eax
:0041F09C 7526 jne 0041F0C4
:0041F093 8BCE mov ecx, esi ; after this fix
:0041F095 B801000000 mov eax,1
:0041F09A 85C0 test eax, eax
:0041F09C 7526 jne 0041F0C4
He he, it works. We do not call the function the display the expiration messagebox. Eax is
allways 1 when the test eax,eax executes so the program allways continues as a non expired version.
I used the W32Dasm deadlisting very little for this crack.
Including MFC42.DLL in the winice.dat was not very useful either as the functions in MFC42 is called
something like MFC42!ORD_08f1...
To crack this program you must spend quite some time in SoftIce... but hey, that's fun!
This program compares the PINs unencrypted (as numeric values, not as strings). If the PINs had been
encrypted the program would have been harder to crack.
Greetings/thanks to The Sandman, Razzia, Volatility, Eternal Bliss, and all other tutorial writers!
I wont even bother explaining you that you should BUY this target program if you intend to use it for a longer period than the allowed one.