In this essay I'll try to cover the most specific problems that a NEWBEE
till SEMY-ADVANCED cracker will encounter at the beginning of his career.
My text includes simple nag-screen
(-dialogs) removing, limit-protection cracking, fishing serialz, and some elements of
key- generator programming.
I'll try to submit you my opinion about what "really" cracking should be. I think, since limit protections of any kind should be defended in any way you fancy, cracking a online-registration target should always include at least one "true" Serial Number matching your key. Most of the cracks roaming the Web are just quick-and-dirty rejump-ed schemes... and you can never be sure of their efficiency as the targets are actually NOT registered at all! You can avoid some ugly surprise later only if you see your target answering with the message:
Thank You for ...(...that you gave me your money!)
after the registering prompt AND your own User Name (handle) inside the
Help-About dialog... "as it should be".
Furthermore, to reverse the algorithm itself is one of the higher arts of our trade, and it is a useful exercise indeed.
The reader is supposed to be familiar with our main tools - SoftIce and W32Dasm - at some beginners level too, I'll not teach you here how to load exports or how to set breakpoints: there are many other good essays covering these themes.
I have collected some targets that I have cracked sincelast August. I hope I established some graduation too, so you'll be prepared for the more difficult jobs at the end.
And... excuse my horrible english too...
And now lets start, putting our targets one after another.
MVP Backgammon, game - nag screen, annoying Editor
MVP Bridge, game - same as above + "triple" limit protection
Hedit, very fast HexEditor - fishing serial number
Hex Workshop + Win-eXpose I/O - THE most stupid protections - new feature not published yet!
IconEdit Pro (;-) - a Visual Basic 5 approach
Gif Construction Set - and we make a password-generator!
SoftIce for W95 from NuMega - THE debugger
W32dasm v89 - try to fetch it, it is just around your nose! - disassembler
A good Win32 reference tool, may I suggest the Borlands C++v5 help files?
Since +He went over to his dead listing approach it seemed that the community begun to underestimate the live-one with our beloved SoftIce. I'll show you the dangers such aproach still hides, since many incredible features are tainted by the dead listing, like the one in HexWorkshop that comes below. I wish to say here, that the live approach is at least so important as the dead one, and that you should always check your targets using both approaches. Sometimes this will spare you a lot of work.
NOTE: for both MVP Backgammon and Bridge we could also "apply" the Borland Resource Workshop approach successfully, but if you want to see how these targets maintain nags, I suggest you to follow me, it will not be so boring and you'll learn something new.
MVP Backgammon; Game from MVP Software;
to obtain from www.mvpsoft.com/strategy.htm
This is a very pretty game, although quite simple, and as the name suggest it is a implementation of this antique amusement. The program is shareware, not protected at all, yet with some annoying features that focus our attention:
When you start the game, right after the (fully warrantable) MVP-logo, a dialog pops up with two elements - a edit field with a quite long text inside, disabusing us about what we should do as next, if we - undoubtfully - like this exclusive product (we have to run immediately to the store/bank), and a button - OK - , that we press after we've thoroughly read the explanations above - and only now the game starts. The whole thing appears again when you want to stop enjoying it, you must then press OK again to scratch the backgammon from your desktop. But that is not all!
After finishing the game, like a dead spirit greeting you from the other side, your very own editor launches and presents to you a stupid Order Form, by which you could buy this bothersome game for, believe it or not, just 40 bucks
Jolly guys, this MVP Soft programmers!
Since we don't like when OTHERS waste OUR resources for such a crap (and anyway just and solely for learning purposes) we gonna change all this. This time the dead approach will be enough, but when you wish to test against side-effects, you must use SoftIce.
Now you disassembled the target and we are going together to crack it.
In this example we will try to bypass the annoying pieces of code, thus
jumping over the relevant calls to this dialog and over the place where
our Editor is launched.
Where should we begin?
The Nag Screen is a Dialog with the title "Registering MVP Backgammon". Since dialogs are mainly created via DIALOGBOX() and CREATEDIALOG() windo$e functions, it is a good idea to look at the imported functions of our program to check for the above two. This is done in Wdasm when you select Imports from the Functions menu, or simply press the ImpFn button, duh
Scrolling down we see:
.......................... USER.CREATEWINDOW USER.DEFWINDOWPROC USER.DESTROYCURSOR USER.DESTROYWINDOW USER.DIALOGBOX <========================= THIS HERE! USER.DISPATCHMESSAGE ..........................
(there will be many more functions).
When we doubleclick the DIALOGBOX item, Wdasm locates the first reference to this function in the listing. To see the other references we just keep doubleclicking the item.
There are lots of DIALOGBOX references in the listing, it's almost impossible to trace them all, how can we get "ours"?
Looking at the Dialog Information in the listing (start of the disassembly) we can see:
+++++++++++++++++ DIALOG INFORMATION ++++++++++++++++++ ....... Name: DialogID_2382, # of Controls=002, Caption:"Registering MVP Backgammon" 001 - ControlID:2383, Control Class:"" Control Text:"" 002 - ControlID:0001, Control Class:"" Control Text:"&OK" ...........
So, now look at Dialog Reference (pressing the DLGRef button) and find the one with the DialogID_2382. Doubleclick it for Wdasm to locate it in our Source Code - and we are lucky: only one occurrence!
.................. * Possible Reference to Dialog: DialogID_2382, CONTROL_ID:2383, "" | :0019.0FB7 688323 push 2383 :0019.0FBA 66FF36001E push word ptr [1E00] :0019.0FBF 9AFFFF0000 call USER.SETDLGITEMTEXT ...................
Seems to be on the right track... Now look around - above and below - and... just a chunk code before that:
............ Exported fn(): REGDLGPROC(const HWND__*,uint,u,long) - Ord:0017h :0019.0F90 8CD0 mov ax, ss :0019.0F92 90 nop <============ ?????? (see below) :0019.0F93 45 inc bp :0019.0F94 55 push bp :0019.0F95 8BEC mov bp, sp :0019.0F97 1E push ds ..............
What the hell does it mean? Why should this function be exported? Is
it called from somewhere "outside" our program? Possible from
a perfid DLL, hiding a weird protection scheme?
The answer is simple if you are familiar with windo$e programming: We just fetched the DIALOG() procedure for the nag. A dialog procedure under windo$e makes your dialog "go round"; YOU wrote it and it is kind-of-"called back" from windo$e after you created the dialog; but it must not therefore be also exported. Probably it is called from another DLL, it is up to you if you want to proof this.
How does windo$e know about which dialog procedure belongs to a dialog? The programmer tells this. Once you are about to create a dialog you call p.e. the Dialogbox()-windo$e procedure and one of its parameters points to the dialog procedure you intend to use for the specific dialog... Dig it?
Now back to our listing.
As we already know, this is the dialog procedure responsible for the behavior of the nag dialog called "Registering MVP Backgammon" which we are going to "fix". What when we crack immediately this routine, thus gathering from windo$e the ability to create the nag dialog? You see, it wouldn't be a good idea, since this function does only the half of the job on our dialog; and - notice this well! - it outcrops AFTER the dialog has been created, NOT before this! You can try this out as I did, p.e. change :
change :0019.0F90 8CD0 mov ax, ss to CB retf (also retf 000A, if you want)
and look at the mess you created...
So we have to use another approach. I've already mentioned, that this function has to be "reported" to the OS in order to be properly "called back" when it's time for the program to deal with the user's input. When we find the place where this happens, then we'll be closer to the naging mechanism as well.
But how can we do this?
Well, there must be a pointer to this crap somewhere pushed as parameter to the dialogbox(), createdialog() or so (see above!); and since we DO KNOW its address -:0019.0F90 - ...click?...
Now lets search! In W32dasm we simply make a Find Text with 0F90 and the very first hit looks like this:
...................... * Referenced by a Jump at Address:0019.10F5(U) | :0019.112C 68DD11 push SEG ADDR of Segment 0019 <===HALLO, :0019.112F 68900F push 0F90 <================ HEEEEREEE! :0019.1132 FF76F4 push word ptr [bp-0C] :0019.1135 9AFFFF0000 call KERNEL.MAKEPROCINSTANCE :0019.113A 8956FC mov [bp-04], dx :0019.113D 8946FA mov [bp-06], ax :0019.1140 FF76F4 push word ptr [bp-0C] :0019.1143 666882230000 push 00002382 :0019.1149 FF76F6 push word ptr [bp-0A] :0019.114C 52 push dx :0019.114D 50 push ax :0019.114E 9AFFFF0000 call USER.DIALOGBOX :0019.1153 8946F8 mov [bp-08], ax :0019.1156 66FF76FA push word ptr [bp-06] :0019.115A 9AFFFF0000 call KERNEL.FREEPROCINSTANCE :0019.115F 66833E001E00 cmp dword ptr [1E00], 00000000 :0019.1165 7419 je 1180 :0019.1167 66C706001E00000000 mov dword ptr [1E00], 00000000 :0019.1170 FF76F0 push word ptr [bp-10] :0019.1173 9AFFFF0000 call KERNEL.GLOBALUNLOCK :0019.1178 FF76F0 push word ptr [bp-10] :0019.117B 9AFFFF0000 call KERNEL.FREERESOURCE ................................
Everyone is here! Good old W32dasm did it this time!
This is almost self explained but here I'll say it again:
MAKEPROCINSTANCE() with the address of our dialog procedure as parameter is needed for system reasons, it returns a handle for the function at 0019.0F90 and this handle is passed then to the DIALOGBOX(), which on turn establishes then the well known nag dialog. After the crime is commited FREEPROCINSTANCE() "unregisters" the dialog procedure since windo$e no more needs it. The last two calls are doing "clean up" jobs.
Congratulations! You just have seen how 16-bit windo$e programs handle our enemys - the nag screen dialogs.
Immediatly after that this finctions rets. We don't need to bother for the function at 0019.0F90, since it is no more relevant to the nag scheme at all. When we crack properly the one that creates the nag, the dialog procedure itself will never come on turn.
Tracing back in the last piece of code we soon notice, that it is entered at:
........... :0019.1081 8CD0 mov ax, ss :0019.1083 90 nop <===== ???????? :0019.1084 45 inc bp :0019.1085 55 push bp :0019.1086 8BEC mov bp, sp :0019.1088 1E push ds ...........
and after a lot of ressource-relevant calls it comes to the above DIALOGBOX() call. There is also a retf at:
.......... :0019.1128 1F pop ds :0019.1129 5D pop bp :0019.112A 4D dec bp :0019.112B CB retf<================ HERE! .........
but we don't bother for it, since it is just on "emergency exit"
for the program in case it didn't find its resources.
And don't forget what we are about to do! Let's crack!
There is one radical way I know when you want to bypass a routine: Just ret it at the very beginning. You must be sure of course that no other use of this routine is made (but for the nag); and there is a more "subtle" detail: the stack's clean up (after parameters have been passed to it). Thus sometimes you must experiment a little, but in this case we can try it out:
change :0019.1081 8CD0 mov ax, ss to :0019.1081 CBxx retf (xx means don't care)
You will need for this one of the Hexeditors we'll crack in this essay,
be aware, the right offset of the instructions is in the status bar of
W32dasm. (Don't forget the backup copy of the target victim!).
So we made it, saved it and know lets see: Launch the Backgammon. Waw! Aren't we wizards?! No more nags at the beginning, no more nags at the end of the game.
We were right.
A little NOP-digression: As you maybe noticed, there are some strange, fully needless nop instructions in the code of some functions, near the entry point. In most cases there are for alignment there, but in some old windo$e versions they are replaced from the OS with other instructions, thus gaining access to the privat data segments of a program. This stupid practice is obsolet in the 32bit world and no more present but you should learn to take hands off from patching NOPs for a very good reason, as +ORC always said: since they could be overwritten in some cases.
Now... the stupid editor remains! We have to fix this too, just for
learning purpose of course... this is not the kind of target that you crack
because you NEED it, this is the kind of target that you crack and then
throw away almost immediately.
There is one quite popular way to start any process from your program - when you call the WINEXEC() function.
To be sure, we take a look at the Imported Functions of our backgammon - press the button "ImpFn" in the toolbar. Then scroll the window untill you reach:
..................... KERNEL.WAITEVENT KERNEL.WINEXEC <==============HERE! KERNEL.WRITEPRIVATEPROFILESTRING .................
Aha, WINEXEC() is there! Now doubleclick on it, Wdasm locates the reference in its window, doubleclick once more - and we are lucky again, no more occurances.
...................... :0003.1C9B 6A05 push 0005 :0003.1C9D 9AFFFF0000 call KERNEL.WINEXEC ;<================= :0003.1CA2 5F pop di :0003.1CA3 5E pop si :0003.1CA4 8D66FE lea sp, [bp-02] :0003.1CA7 1F pop ds :0003.1CA8 5D pop bp :0003.1CA9 4D dec bp :0003.1CAA CB retf ...................
You guess what comes next? Right! We scroll upside to see where this piece of code is entered, wow, quite a lot of commands!
................. :0003.1BEA 8CD0 mov ax, ss ; <===== HERE! :0003.1BEC 90 nop :0003.1BED 45 inc bp :0003.1BEE 55 push bp :0003.1BEF 8BEC mov bp, sp ..................
Can we do it the same manner as with the dialog? I say yes, since there aren't any relevant system calls before WINEXEC(), the GETMODULEFILENAME serves for ?????????????????. But when we want to be sure, as I already said, we should have to set a breakpoint inside this code perhaps at:
:0003.1BEA 8CD0 mov ax, ss
and then we "walk" trough the game, wating for softice to pop up. If this isn't the case, we can go on and crack:
replace :0003.1BEA 8CD0 mov ax, ss with :0003.1BEA CBxx retf
Now start the program. Quit it. That was all!
MVP Bridge; Game from MVP Software
to obtain from: http://www.mvpsoft.com/cardgame.htm
This is another nice product from our friends in MVP, this time a S-class one, if you believe the home-made reclame. In addition to the already known nags (bugs ;-) - dialog before running and quitting, this target starts your own editor as well, whithout asking you, and the game is also limit protected! When you start the game, a nice nag dialog informs you that you have a 30 days trial period or either a 20 times "quiver" for using it, "...whatever comes last." Pretty generous dimensioned from MVP.
I did play this game just a few times and its best feature is the background and "event" music it plays during the rubbers, unfortunately it suffers from some algorithmic weakness (a common MVP feature BTW ;-)
Sometimes I used it when I was general frustrated from life - "This time I'm playing to win...".
I would never have come to the idea to crack this target if not just for learning purposes (you must pay 40 US$ to "gain" this game, a crap that you probably could better program yourself)
I went to work on it. Right after the first attempt it looked somehow fishy to me, like wasting my own resources without permissions... and I in general punish such attempts.
First -of course- I desinstalled (deleted) the game in order to install it once more - no chance. After some I/O trapping I found a file called Clock.tim in the windo$e directory, which is "consulted" by the program everytime it starts. This was already "housebreaking" but after a meaningful text:
You have exceeded the playing limit!... etcetera
The thought that somebody leaves traces and marks on my own harddisk, without
any warning, just to ensure
his (fully) hypothetical profit of 40 bucks is for me like a slap on my face.
Dear MVP boys
-when you will read this- there are perhaps millions of software developers
all over the world and I'm testing thousands of different shareware
applications every year.
Just imagine the mess, when evryone leaves even a very tiny file inside MY OWN windo$e folder (32KB under Win95 minimum!) or some half-concealed keys in MY OWN registry! Should I buy a new disk just to keep your (futile and pathetical) "secrets" inside the old one? This is only one reason more to crack all your damn protections "black and blue", as the +Masters use to say.
I began working on this when the limit was already over. It is certainly better to start deprotecting when the target still works (a "fresh" target) but in this case is quite interesting to analize its behavior after that, once the target is already stale... and in order to learn something new, we better work this way!
To simulate the situation you could start the game 20 times repeatedly, but since I won't harass you with such trivial tasks here you can find a mvpcount.REG file, reflecting the state of the "hidden" counter after 15 launches, you add it to your registry by simply clicking it twice. Don't forget to delete the CLOCK.TIM in your Window$ directory!
You need also to turn your clock one month forward.
Now you can enjoy the game 5 more times until the protection snaps. When you start the program at this point, you see just a message box as:
You have exceeded the playing limit. ...bla-bla-bla.....
And after pressing OK comes first the nag dialog and then the editor
Lets disassemble the thing (Mvpbr.exe). Pressing ImpFn we then look for our DIALOGBOX(). Surprise - we see it along with the similar one DIALOGBOXINDIRECT(), but only at one place in the listing, one after another! This means, that ALL dialogs in this game - and there are many! - come as parameters passed to the function containing both dialogbox() procedures - and the DIALOGBOX() is called at only one place, yet with different parameters for the different dialogs. This a clever approach, since it first:
- spares exports/ load time
and second - and more important for us
- lets us search for our dialogs on a "higher" level and more difficult!
What we should do?
A first approach is to find where in the body of our game the message box with the above text is issued, in the hope that we will land somewhere in the protection scheme as well. In order to do it we can search for the string "exceeded" and for references to it. And here I made a mistake that costs me lot of time and work: I tried the Wdasm Find Text and found nothing, while pretty easy located the word with the Hexeditor in the Mvpbr.exe. Why?
We must never forget what +ORC says:
First analize the target! How is it structured? What kind of windo$e program is it?
In this case it is a 16bit program with 14 different segments, as we can see at the beginning of the disassembly:
............ +++++++++++++++++++ SEGMENT INFO ++++++++++++++++++++++++ Number of Code/Data Segments = 14 CSEG001 File Offset: 00001000 Size:B7D7 Flags:0x1D50 -> CODE, MOVEABLE CSEG002 File Offset: 0000CA00 Size:B68E Flags:0x1D50 -> CODE, MOVEABLE CSEG003 File Offset: 00018200 Size:C4C6 Flags:0x1D50 -> CODE, MOVEABLE CSEG004 File Offset: 00024800 Size:E794 Flags:0x1D50 -> CODE, MOVEABLE CSEG005 File Offset: 00033200 Size:E4E3 Flags:0x1D50 -> CODE, MOVEABLE CSEG006 File Offset: 00041C00 Size:A868 Flags:0x1D50 -> CODE, MOVEABLE CSEG007 File Offset: 0004CC00 Size:C33C Flags:0x1D50 -> CODE, MOVEABLE DSEG008 File Offset: 00000000 Size:0000 Flags:0x0C51 -> DATA, MOVEABLE DSEG009 File Offset: 00000000 Size:0000 Flags:0x0C51 -> DATA, MOVEABLE DSEG010 File Offset: 00000000 Size:0000 Flags:0x0C51 -> DATA, MOVEABLE DSEG011 File Offset: 00000000 Size:0000 Flags:0x0C51 -> DATA, MOVEABLE DSEG012 File Offset: 00000000 Size:0000 Flags:0x0C51 -> DATA, MOVEABLE DSEG013 File Offset: 00000000 Size:0000 Flags:0x0C51 -> DATA, MOVEABLE DSEG014 File Offset: 00059400 Size:92D0 Flags:0x0D51 -> DATA, MOVEABLE .................
Neither Find Text, nor StrRef can help us, we have to look directly
in the data segments!
There are 7 of them, but only 1 contains actualy data. We can see this by pressing the HexData button/ menu item.
Browsing trough the segments we soon find in
"Data Segment 14.... Page 1 of 5" following:
:0014.0138 6D 00 59 6F 75 20 68 61 m.You ha <=========== HERE! :0014.0140 76 65 20 65 78 63 65 65 ve excee :0014.0148 64 65 64 20 74 68 65 20 ded the :0014.0150 70 6C 61 79 69 6E 67 20 playing :0014.0158 6C 69 6D 69 74 2E 20 20 limit. :0014.0160 53 65 65 20 74 68 65 20 See the :0014.0168 66 6F 6C 6C 6F 77 69 6E followin :0014.0170 67 20 6F 72 64 65 72 20 g order :0014.0178 69 6E 66 6F 20 66 6F 72 info for :0014.0180 20 64 65 74 61 69 6C 73 details :0014.0188 2E 00 6E 6F 74 65 70 61 ..notepa <=========== AND THIS ONE TOO! :0014.0190 64 20 6F 72 64 65 72 2E d order. :0014.0198 66 72 6D 00 77 62 00 25 frm.wb.% :0014.01A0 64 00 44 32 00 54 31 00 d.D2.T1. :0014.01A8 53 79 73 74 65 6D 20 69 System i :0014.01B0 6E 73 74 61 6C 6C 61 74 nstallat :0014.01B8 69 6F 6E 20 65 72 72 6F ion erro
What do you mean? The error message, followed immediately by the command
line for launching the editor! Hot!
But how do we proceed further?
Look, to display a message, any windo$e procedure needs a pointer to that string passed as parameter to the procedure. And what looks a pointer like? It is just the offset of the string within some data segment. Since we see above that "our" offset is 013A, we do following search:
Find Text mov ax, 013A (there must be ONE space between the comma and the 013A
Note, that only Find Text 013A would do
it too, but there are to much irrelevent occurences of it.
Now Wdasm searches and what? Believe it or not, but there is only one mov ax, 013A in the whole program:
............. :0001.07C9 3D1E00 cmp ax, 001E :0001.07CC 7303 jnb 07D1 :0001.07CE E98800 jmp 0859 :0001.07D1 B83A01 mov ax, 013A <========== HERE! :0001.07D4 8CDA mov dx, ds :0001.07D6 52 push dx ..............
Is that what we need? we can try it! Right above the mov there is a jump-around construction, lets change it:
from :0001.07CC 7303 jnb 07D1 to :0001.07CC 7300 jnb 07CE
thus forcing program flow to the mysteriose 0859 location.
Load the mvpbr.exe in the hexeditor, go to location 000017CC (you take it from the status line of the Wdasm!), patch it and save. Now run the game and what? It starts with the nag set to "Time expired" but no more limits there!
Wasn't it easy?
This is not the final crack, as there are many more checks and reefs in our target. At this place I will explain how the protection works to see how many things we still have to do.
The file clock.tim is not so relevant for the protection. You can delete it, but Mvpbr creates it every time anew. It serves only for comparison with the other data.
The main source for the protection is of course the registry. The program creates a new key under HKEY_CLASSES_ROOT named Accessoires. Then it adds to it a subkey Clock, which in turn contains tree subkeys: D1, D2 and T1. These subkeys contain string values, accordingly to the following schema:
D1 represents the date of the last-but-one start of the program;
D2 represents the date of the very last start;
T1 represents the total times you used it.
So even if you "turn back the time", the program recognize
it and refuses starting.
And now look: What happens, when you, WITHOUT any intents turn the clock back and try the game (this happens automaticaly when you are playing late night on the day when the or summer-winter time change commits) - you will be never able to start normally this program again, even when in the "allowed" limit!
Is such protection juristic legitim at all?
With this in mind we come back to cracking. Now, where the game works again, we are going to crack first the whole protection, and then we will remove tha nags in order to save our disk space (and for learning purpose, of course!)
We can of course simply delete the Accessoires key in the registry, or, for more fun just copy the value of D2 into D1, but since they are created every time anew and the registry becames more and more a carbage colector we should try these keys to be not created at all.
If you look closer at the HexData dump above, you can see three other messages complaining some "errors":
System error - conflicting dates... at
offset 00A9 - this occures when the date in the Clock.tim does not correspond
to the one in the Registry;
System date error... at offset 00F9 - when you turn your system time back, and
System installation error... at offset 01A8 - if the registry operation fails
We can easy locate the references to the strings using the same approach
Find Text mov ax, String_Offset
p.e. searching for mov ax, 00A9 will find this:
....... :0001.0658 8B86C2FC mov ax, [bp+FCC2] :0001.065C 8B96C4FC mov dx, [bp+FCC4] :0001.0660 3986CEFC cmp [bp+FCCE], ax :0001.0664 7403 je 0669 :0001.0666 E93000 jmp 0699 :0001.0669 3996D0FC cmp [bp+FCD0], dx :0001.066D 7403 je 0672 :0001.066F E92700 jmp 0699 :0001.0672 8B869EFC mov ax, [bp+FC9E] :0001.0676 8B96A0FC mov dx, [bp+FCA0] :0001.067A 3986CAFC cmp [bp+FCCA], ax :0001.067E 7403 je 0683 :0001.0680 E91600 jmp 06990001.067E(C) :0001.0683 3996CCFC cmp [bp+FCCC], dx :0001.0687 7403 je 068C :0001.0689 E90D00 jmp 0699 :0001.068C 8B86B2FD mov ax, [bp+FDB2] :0001.0690 3986C0FC cmp [bp+FCC0], ax :0001.0694 7503 jne 0699 :0001.0696 E97500 jmp 070E :0001.0699 B8A900 mov ax, 00A9 <====== HERE! :0001.069C 8CDA mov dx, ds :0001.069E 52 push dx :0001.069F 50 push ax ................
There is still one occurrence apiece, so you can't do anything wrong.
The locations are close to each other and everyone has a short overhead
with a jump-around chain, followed by a WINEXEC() call. This means, the
program checks everytime for the current condition, and, if "not good"
puts the appropriate message, otherwise it jumps to the next check.
Do we need to crack ALL these checks separately in order to become our program run without any nags? You can try it, of course, and it will work (be aware to patch the right jump/conditional jump, it is always the FIRST one of a chain; sometimes you should patch ALL the jumps!) but this will be the hell of a job; furthermore our goal should be to crack it so, that no more registry keys will be added/no files created!
This is done generally by jumping over the culprite code to the location where the "really" game begins. So we need TWO locations: the first where we should jump from, and the second where we're jumping to.
How do we find them?
The latter one is easy. Browsing through the code around the checks we soon find the calls that handles the registry itself:
..................... :0001.0470 9AFFFF0000 call SHELL.REGQUERYVALUE ....................... :0001.04AC 9A71040000 call SHELL.REGQUERYVALUE .......................
Just after the LAST registry call at:
................. :0001.0B6F 9A79090000 call SHELL.REGCLOSEKEY :0001.0B74 B80302 mov ax, 0203 <===< HERE :0001.0B77 8CDA mov dx, ds ...................
we may suppose that the deverse checks are finished and is a good place
for jumping to. You can call it Zen, if you want, however I have tryed
it and it works. Jumping to it from somewhere above the checks puts one
through the "back door" directly in the "restricted area"!
Now we have our "entry point". What leaves is the location, where to place the jump itself.
There are lots of registry calls in that code chunk, creating, seting, and reading variose keys. But where exactly are the relevant keys created? The string "Accessiores" doesn't appear in the disassembly or in the HexData, nor can you find it even with a hex-search in the exe or in the DLLs. The same with the "clock.tim".
Look at this beautiful piece of code, just before the first registry-related call:
...................... :0001.03C2 C68618FF41 mov byte ptr [bp-00E8], 41 :0001.03C7 C68619FF63 mov byte ptr [bp-00E7], 63 :0001.03CC C6861AFF63 mov byte ptr [bp-00E6], 63 :0001.03D1 C6861BFF65 mov byte ptr [bp-00E5], 65 :0001.03D6 C6861CFF73 mov byte ptr [bp-00E4], 73 :0001.03DB C6861DFF73 mov byte ptr [bp-00E3], 73 :0001.03E0 C6861EFF6F mov byte ptr [bp-00E2], 6F :0001.03E5 C6861FFF72 mov byte ptr [bp-00E1], 72 :0001.03EA C68620FF69 mov byte ptr [bp-00E0], 69 :0001.03EF C68621FF65 mov byte ptr [bp-00DF], 65 :0001.03F4 C68622FF73 mov byte ptr [bp-00DE], 73 :0001.03F9 C68623FF5C mov byte ptr [bp-00DD], 5C :0001.03FE C68624FF43 mov byte ptr [bp-00DC], 43 :0001.0403 C68625FF6C mov byte ptr [bp-00DB], 6C :0001.0408 C68626FF6F mov byte ptr [bp-00DA], 6F :0001.040D C68627FF63 mov byte ptr [bp-00D9], 63 :0001.0412 C68628FF6B mov byte ptr [bp-00D8], 6B :0001.0417 C68629FF00 mov byte ptr [bp-00D7], 00 .....................
Don't you see anything? This is the same old trick the protectionists try to foolish us with since the very first days! Now look again, after I've "commented" it for you, together with the other relevant place:
..................... :0001.03C2 C68618FF41 mov byte ptr [bp-00E8], 41; A :0001.03C7 C68619FF63 mov byte ptr [bp-00E7], 63; c :0001.03CC C6861AFF63 mov byte ptr [bp-00E6], 63; c :0001.03D1 C6861BFF65 mov byte ptr [bp-00E5], 65; e :0001.03D6 C6861CFF73 mov byte ptr [bp-00E4], 73; s :0001.03DB C6861DFF73 mov byte ptr [bp-00E3], 73; s :0001.03E0 C6861EFF6F mov byte ptr [bp-00E2], 6F; o :0001.03E5 C6861FFF72 mov byte ptr [bp-00E1], 72; r :0001.03EA C68620FF69 mov byte ptr [bp-00E0], 69; i :0001.03EF C68621FF65 mov byte ptr [bp-00DF], 65; e :0001.03F4 C68622FF73 mov byte ptr [bp-00DE], 73; s :0001.03F9 C68623FF5C mov byte ptr [bp-00DD], 5C; \ :0001.03FE C68624FF43 mov byte ptr [bp-00DC], 43; C :0001.0403 C68625FF6C mov byte ptr [bp-00DB], 6C; l :0001.0408 C68626FF6F mov byte ptr [bp-00DA], 6F; o :0001.040D C68627FF63 mov byte ptr [bp-00D9], 63; c :0001.0412 C68628FF6B mov byte ptr [bp-00D8], 6B; k :0001.0417 C68629FF00 mov byte ptr [bp-00D7], 00; <== terminates it ....................
..................... :0001.00CC 8346E001 add word ptr [bp-20], 0001 :0001.00D0 8D9EEAFC lea bx, [bp+FCEA] :0001.00D4 03D8 add bx, ax :0001.00D6 C6075C mov byte ptr [bx], 5C; \ <===== :0001.00D9 8B46E0 mov ax, [bp-20] :0001.00DC 8346E001 add word ptr [bp-20], 0001 :0001.00E0 8D9EEAFC ; lea bx, [bp+FCEA] :0001.00E4 03D8 add bx, ax :0001.00E6 C60743 mov byte ptr [bx], 43; C <===== :0001.00E9 8B46E0 mov ax, [bp-20] :0001.00EC 8346E001 add word ptr [bp-20], 0001 :0001.00F0 8D9EEAFC lea bx, [bp+FCEA] :0001.00F4 03D8 add bx, ax :0001.00F6 C6074C mov byte ptr [bx], 4C; L <===== :0001.00F9 8B46E0 bsp; mov ax, [bp-20] :0001.00FC 8346E001 add word ptr [bp-20], 0001 :0001.0100 8D9EEAFC lea bx, [bp+FCEA] :0001.0104 03D8 p; add bx, ax :0001.0106 C6074F mov byte ptr [bx], 4F; O <===== :0001.0109 8B46E0 mov ax, [bp-20] :0001.010C 8346E001 add word ptr [bp-20], 0001 :0001.0110 8D9EEAFC lea bx, [bp+FCEA] :0001.0114 03D8 add bx, ax :0001.0116 C60743 mov byte ptr [bx], 43; C <===== :0001.0119 8B46E0 mov ax, [bp-20] :0001.011C 8346E001 add word ptr [bp-20], 0001 :0001.0120 8D9EEAFC lea bx, [bp+FCEA] :0001.0124 03D8 add bx, ax :0001.0126 C6074B mov byte ptr [bx], 4B; K <===== :0001.0129 8B46E0 mov ax, [bp-20] :0001.012C 8346E001 add word ptr [bp-20], 0001 :0001.0130 8D9EEAFC lea bx, [bp+FCEA] :0001.0134 03D8 add bx, ax :0001.0136 C6072E mov byte ptr [bx], 2E; . <===== :0001.0139 8B46E0 mov ax, [bp-20] :0001.013C 8346E001 add word ptr [bp-20], 0001 :0001.0140 8D9EEAFC lea bx, [bp+FCEA] :0001.0144 03D8 add bx, ax :0001.0146 C60754 mov byte ptr [bx], 54; T <===== :0001.0149 8B46E0 mov ax, [bp-20] :0001.014C 8346E001 add word ptr [bp-20], 0001 :0001.0150 8D9EEAFC lea bx, [bp+FCEA] :0001.0154 3D8 add bx, ax :0001.0156 C60749 mov byte ptr [bx], 49; I <===== :0001.0159 8B46E0 mov ax, [bp-20] :0001.015C 8346E001 add word ptr [bp-20], 0001 :0001.0160 8D9EEAFC lea bx, [bp+FCEA] :0001.0164 03D8 add bx, ax :0001.0166 C6074D mov byte ptr [bx], 4D; M <===== :0001.0169 8B46E0 mov ax, [bp-20]; <====== Here is the index! :0001.016C 8346E001 add word ptr [bp-20], 0001 :0001.0170 8D9EEAFC lea bx, [bp+FCEA]; <======Here is the string! :0001.0174 03D8 add bx, ax; <====== Calculates the right position... :0001.0176 C60700 mov byte ptr [bx], 00; <= ...and puts there the current char ...................
You should always be aware, when you see such massive byte - constant loads in a target! BTW, since Wdasm still missing a feature as ASCII- dump the constant's manipulation, you can pretty easy "see" such hidden string- buildings with SoftIce - one more advantage of the live approach.
We lhave to:
find a place for our jump before the file is created AND
calculate the offset.
BTW, [bp+FCEA] is indeed [bp-0321] - have you made your homeworks?(;-P) - but it is still not the final address of "CLOCK.TIM". When the file is about to be created it needs three more letters (guess which) for the INT 21 call (yes, this target uses DOS-fashion calls to manipulate this file!), so we have to search for [bp-31F], which looks like [bp+FCDE] in our disassembly.
We find it at :
................... :0001.03A2 FFB6E0FC push word ptr [bp+FCE0] :0001.03A6 FFB6DEFC push word ptr [bp+FCDE]; <======= HERE! :0001.03AA 9AD8005403 call 0007.00D8 ...................
So our location should be the 0001.03A2.
There is still another problem. The program creates one more file in its directory, called Temp.tst. It is no relevant at all and serves to check whether the game resides on a CD or on the hard disk, but when we place the jump on the above address, this file - automaticaly erased otherwise - remains too.
So we make a final Text Find with:
mov ax, 0026
where 0026 is the offset of the string "\Temp.tst" in the data segment (DataHex!)
.................. :0014.0020 43 3A 00 43 3A 00 5C 54 C:.C:.\T <==== HERE! :0014.0028 45 4D 50 2E 54 53 54 00 EMP.TST. :0014.0030 54 45 4D 50 2E 54 53 54 TEMP.TST ..................
What comes at least is what we needed:
............ :0001.02A9 B82600 mov ax, 0026; <==== HERE COMES the JUMP!!! :0001.02AC 8CDA mov dx, ds :0001.02AE 52 push dx ...........
Now the offset.
In the windo$e world most of the jumps are compiled as E9h, what is a relative jump with a 16b offset, which means it needs 3 bytes in the binary. The offset then is calculated as follows:
offset = (jump_destination - jump_address) - 3
In our case:
offset = (0B74 - 02A9) - 3 = 08C8h
but it must be compiled so:
E9 C8 08
due to the weird way the Intel processors store data in memory - the LSB (Low Significant Byte) in the lower addres, the MSB (Most Significant Byte) in the higher.
Now we can crack: Open the Mvpbr.exe in your HexEditor, go to the offset 000012A9h (you've seen it on the task bar of Wdasm!) in Segment 0001 and
change: B82600 mov ax, 0026 to: E9C808 jmp 0B74
We did it!
As a nice side effect from this, the "entry" nag screen disapears too! But the final one and the editor with the order form still remains. You have owned already the knowledge nessessary to commit this last crack, but there is still something I want to show you.
We start as usual at the beginning of the W32Dasm listing, looking for the dialog named "Order info" - this is the text presented in the window's caption of the dialog. Here is it:
.................. Name: DialogID_0107, # of Controls=002, Caption:"Order Info" 001 - ControlID:0001, Control Class:"" Control Text:"OK" 002 - ControlID:00D9, Control Class:"" Control Text:"" .................
Now press the DlgRef button, find the one with ID 0107 and doubleclick it. Only one reference in our listing:
................... :0002.5F92 C8020000 enter 0002, 00 :0002.5F96 56 push si :0002.5F97 57 push di * Possible Reference to Dialog: DialogID_0107 | :0002.5F98 680701 push 0107; <============ HERE! :0002.5F9B 6A00 push 0000 :0002.5F9D 6A00 push 0000 :0002.5F9F FF7608 push word ptr [bp+08] :0002.5FA2 FF7606 push word ptr [bp+06] :0002.5FA5 9AD224685F call 0006.24D2 :0002.5FAA C45E06 les bx, [bp+06] :0002.5FAD 26C707889E mov word ptr es:[bx], 9E88 :0002.5FB2 26C74702865F mov word ptr es:[bx+02], SEG ADDR of Segment 0007 :0002.5FB8 C45E06 les bx, [bp+06] :0002.5FBB 8B4610 mov ax, [bp+10] :0002.5FBE 26894728 mov es:[bx+28], ax :0002.5FC2 C45E06 les bx, [bp+06] :0002.5FC5 8B460E mov ax, [bp+0E] :0002.5FC8 2689472A mov es:[bx+2A], ax :0002.5FCC 8B460A mov ax, [bp+0A] :0002.5FCF 8B560C mov dx, [bp+0C] :0002.5FD2 C45E06 les bx, [bp+06] :0002.5FD5 2689472C mov es:[bx+2C], ax :0002.5FD9 FF7608 push word ptr [bp+08] :0002.5FDC FF7606 push word ptr [bp+06] :0002.5FDF 9AAA25A85F call 0006.25AA; <======= WE NEED THIS ONE :0002.5FE4 8B4606 mov ax, [bp+06] :0002.5FE7 8B5608 mov dx, [bp+08] :0002.5FEA E90000 jmp 5FED :0002.5FED 5F pop di :0002.5FEE 5E pop si :0002.5FEF C9 leave :0002.5FF0 CA1000 retf 0010 .........................
This target uses enter for the procedure frame, but it doesn't realy
matter for us. Note that retf 0010 suggests, that the procedure (in this
case) have recieved 10h prameters (inclusive CS:IP!) and at its end it
makes clean-up of the stack.
Now we can easy crack it by placing retf 0010 at 0002.5f92 instead of enter. But in this case it won't work!
You see, somwhere in the body of this function they set up a jump-table for later use; when we bypass it, the program crashes when it quits!
An ugly, really ugly trick!
So we must look closer. The second call leads us direct to the dialog routines - you can try it if you will, I don't want to bother you with more code here. Relevant is the fact that we can crack the above:
change :0002.5FD9 FF7608 push word ptr [bp+08] to :0002.5FD9 E90800 jmp 5FE4
Since the editor starts immediately after that it's a good idea to find the place where this function is called from in the hope to eliminate the WINEXEC() too. We do a Text Find (backwards) with call 0002.5F92 as retf tells us that it must be a far call. What You Find is What You Needed:
............... :0001.30A5 9A908F4730 call 0007.8F90 :0001.30AD FF7606 push word ptr [bp+06] :0001.30B0 6A01 push 0001 :0001.30B2 6AFF push FFFF :0001.30B4 6AFF push FFFF| :0001.30B6 6AFF push FFFF :0001.30B8 8D8676FF lea ax, [bp+FF76] :0001.30BC 8CD2 mov dx, ss :0001.30BE 52 push dx :0001.30BF 50 push ax :0001.30C0 9A925FBA2B call 0002.5F92; <==== culprit routine :0001.30C5 B82008 mov ax, 0820; <======== FROM HERE... :0001.30C8 8CDA mov dx, ds :0001.30CA 52 push dx :0001.30CB 50 push ax :0001.30CC 6A01 push 0001 :0001.30CE 9ACD090000 call KERNEL.WINEXEC; <= ! :0001.30D3 C45E06 les bx, [bp+06]; <======= ...INTO HERE ! ...........................
There are many other calls to the same routine, but this is not interesting coze all of them dwells in our bypassed piece of code (from 0b74 to 02a9). We fall back once more on our aproach and patch:
change :0001.30C5 B82008 mov ax, 0820 to :0001.30AD E90B00 jmp 30D3
Now lets try it - and it works fine!
That was all for this target. Pretty much, what? Now sipp your favorite cocktail and prepare yourself for the funny part of this essay - fishing serialz.
Hedit v2.1.11; Hexeditor from Yuri SoftWare
to obtain from: Everywhere on the Web or direct from http://www.yurisw.com/hedit/
This is a small and very fast (unlike the monstrose Hex Workshop) HexEditor. Allthough with limited functionality it still offers the most important for us features. If you have only 16MB RAM and have to deal with large files (what I suppose you are doing!) you'll soon learn to appreciate it.
It is time limited and refuses to save changes (during the trial period) to the files too, but is protected with a very banal scheme. I would never bother Fravia's page with such ridiculous thing but since this essay concentrates on the newbee's needs I'll explain this crack here as a foretaste of what comes next.
Little online-registering digression:
In fact there two commonly methods to register a shareware online (I mean: on your computer): Choosing the option "registration" from somwhere in the menus of a target you are prompted to input either a
-serial number (like Hex Workshop) or
-a user name, followed with the apropriate "User Number" - kind of the Login/Password pairs known from Unix and other multiuser OS.
The protection then checks for validity of the users input and acts depending of whether you typed the right values or not. The chek itself uses one of the two approaches:
-It manipulates the UserName item in a more or less "secret" way - this means it ENCODES it. The rezult then represents the Magic Key which the program expects as the second input - the user number (or password, sometimes it is just another string) - to open to you the gates of the unlimited use.
-Sometimes however the user number is encoded too - as by Gif Construnction Set - in order to NOT let us see the right number while debugging the target.
Needless to say, that while the first approach means no protection at all, the second is a really challenge to the cracker genius.
Our Yuri software has implemented the first method ;-)
Let see - load SoftIce and start the target. From the menu item Help in Hedit we select About Hedit and a dialog box pops up with a meaningless text and two buttons. We press the Register one and an other box presents to us the registration form which in this case consists only of two text-edit ields as:
I used for this example UncleVan and 123456789, but you can place your name there. Be aware to use longer names, since some schemes require more than few chars for their manipulations.
As number (or password) use always a string with DIFFERENT characters in it, this can be very useful if you are about to trace some longer encoding algorithm.
As you should already know fro +ORCs lessons, they use most of the time the GETWINDOWTEXT() function to get the user input. If you are not sure you can try the GETDIALOGITEMTEXT() too.
At this point you press Ctrl-D. The softice screen should appear. Here you have to bpx the above function. Type first addr to see the name of the target - Hedit in this case. Then change to its addres context with:
This ensure that you are in the right address space to set the breakpoints. Now type:
Press Ctrl-D again. You return to windo$e. Here you press OK in the registration dialog. SoftIce pops up immediately, but this time we must press G to continue executing Hedit, since this was only the UserName-read. Hedit reads first both fields and then calculates the right number. When SoftIce pops again you are in the very beginning if the GETWINDOWTEXT() procedure, so you have to press F12 twice (or issue twice p ret ) to come back to Hedit, right where the function was called:
............. :00427406 FF15B80D4500 Call [user!getwindowtext] :0042740C 8D4518 lea eax, dword ptr [ebp+18] :0042740F 50 push eax :00427410 8D45E0 lea eax, dword ptr [ebp-20] :00427413 FF7510 push [ebp+10] :00427416 50 push eax :00427417 E837000000 call 00427453; <==== it's not that! :0042741C 85C0ets" inside the