Sunday, August 8, 2010

A beginners Guide to Cracking POST-2

CHAPTER 3: SOME BASIC CRACKING TECHNIQUES
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The first thing I want to do before going into some simple techniques is to
explain the purpose of one of the uuencoded cracking tools at the end of this
guide. And also to go over some general procedures you should perform before
actually loading a program you wish to crack into the debugger.

Nowadays a lot of programmers will compress the executable files of their
programs to save space and to make it difficult for people who don't know any
better to hack those files. There are a lot of losers out there who will get
ahold of a program and lacking any skill or talent of their own, will load the
program into a disk editor and hex edit their name into it. Or they will make
other similarly feeble modifications.

This is the reason I encrypt all of the cracks that I distribute. The routines
I write are not that hard to defeat, but I figure anyone with the skill to
crack them is far above having to hack their name into them...

Ok, back to the file, the name of the program is UNP and it is an executable
file expander. It's purpose is to remove the compression envelope from
executable programs. And it supports most of the compression routines
currently in use...

A lot of the compression routines will cause a debugger to lock up if you try
to step through the compressed file, especially PKLITE v1.15. And seeing as
how the file is compressed, if you load it into a disk editor it will just
look like a bunch of garbage and you'll not be able to find the bytes you want
to edit anyway.

UNP is very easy to use, just type UNP [filename] and if there is any type of
compression envelope that UNP understands on the file, UNP will remove it. You
can then load the file into a debugger and hack away...

But before you load a program into the debugger you should run the program a
few times and get a feel for it. You want to see how the protection is
implemented. Whether it's nag or delay screens and at what point in the
program they fist appear, or where in the program does the first mention of
being unregistered or an evaluation copy appear?

This is important. Because before the program displays the first mention of
being unregistered, it has to do the protection check. and this is where you
will usually want to concentrate. Also look for registered functions being
disabled, and sometimes date expirations. The program could also be looking
for a registration key.

In the case of commercial software what type of copy protection is used? Is it
a doc check, or does the program want you to input a serial number before it
will install itself? Once you see how and where the offending routines are
implemented you can begin to develop an overall strategy on the best approach
to circumvent them. It's also a good idea to read the docs, you can pick up a
lot of useful info from doc files.

There are basically three categories that shareware programs fall into... They
are begware, crippleware, and deadware.

The begware category is comprised of programs that have all the registered
features enabled but every time you run them they will display screens that
bug you to register. This is usually the easiest form of protection to remove
and it's the type I'll go over in the walk through.

The crippleware category is comprised of programs that in the unregistered
version have certain functions disabled, and maybe nag screens as well. This
type of protection can be more complex, but often times is just as easy to
defeat as a simple nag screen.

The deadware category is comprised of programs that are totally stripped of
the code for the registered features so there is really nothing to crack. A
good example of this is DOOM by ID software. You can get the shareware version
just about anywhere, however no matter how much you hack at it you cannot make
it into the commercial version cause it only contains the code for the first
episode.

The sample code fragments in this section are not taken from actual programs.
I just made them up off the top of my head while I was writting this guide,
and there are bound to be some errors in them. Please dont write me and tell
me this, I already know it.

Most forms of copy protection have one weak spot, and this is the spot you
will concentrate on. They have to perform the protection check and then make a
decision based on the results of that check. And that decision is usually a
conditional jump. If the check was good the program will go in one direction,
if it was bad it will go somewhere else.

So, you've run the program through a few times and you know at what point the
routines you want to modify first appear, you've also run UNP on it and have
removed any compression envelopes. Now you load the program into the debugger
and wonder what to do next...

What you want to do is to step through the code until something significant
happens like a nag screen gets displayed, or a doc check comes up or the
program tells you that the function you just tried to use is only available in
the registered version. When you reach that point you can then start to
evaluate what portion of code to begin studying.

Let's say you have a program that displays a nag screen and you want to remove
it. You step through the program until the nag screen pops up, you now think
you know the location of the instructions that are causing it to be displayed.
So you reload the program and trace back to a point a few instructions before
the call to the nag screen, and this is what you see:

09D8:0140 CMP BYTE PTR [A76C],00
09D8:0145 JNZ 014B
09D8:0148 CALL 0C50
09D8:014B MOV AH,18

Now, let's assume that the memory location referenced by the first line of
code does indeed contain 00 and that it is the default value placed in there
by the programmer to indicate that the program is unregistered.

The first line of code is checking the value contained in the memory location
to see if it is 00 or not. If the location does contain 00, the compare
instruction will cause the Zero flag to be set. If the location contains any
other value than 00, the Zero flag will be cleared.

The second line of code makes the decision on how to proceed based on the
results of the compare instruction. The JNZ instruction will make the jump to
the fourth line of code if the zero flag is cleared. This will bypass the call
to the nag screen on the third line. If the zero flag is set, no jump will
occur and the call will be made.

The third line of code contains the call to the nag screen. If it is executed
the nag screen will be displayed. The fourth line of code is just the next
instruction in the program.

Once you have found and analyzed this piece of code within the program, you
can now decide on how to bypass the call on the third line. There is no single
way to do this. I can think of a half dozen different ways to patch the
program so it will not make the call. But there is a best way...

First, you could just replace the JNZ 014B with JMP 014B. This is an
unconditional jump and it will bypass the call on the third line no matter
what the memory location that the first line of code is referencing contains.

You could also change it to read JZ 014B so that the jump will be made if the
location contains 00, and not the other way around. You could even change the
CMP BYTE PTR [A76C],00 instruction to JMP 014B.

Or you could just NOP out the call on the third line altogether seeing as how
it's a local call. By a local call I mean that the code contained within the
call resides in the same code segment as the call instruction itself.

This is an intersegment call. You will see other calls that reference lines of
code outside of the current code segment. These are intrasegment calls, and
have to be handled differently. They will look something like CALL 0934:0AC5,
or CALL FAR 0002. I'll go over how to handle intrasegment calls later on.

NOP is short for no op-code, and it is a valid instruction that the
microprocessor understands. It is only one byte in length, and the call
instruction is three bytes in length. So if you wanted to nop out the call
instruction you would have to enter the NOP instruction three times in order
to replace it. And if you replaced the CMP BYTE PTR [A76C],00 with JMP 014B,
you would have to pad it out with a few nop's as well.

The compare instruction is 5 bytes and the jump instruction is only 2 bytes,
so you would have to add 3 nops in order to equal the length of the original
compare instruction. Otherwise you would throw off the address of every
instruction after it in the program and end up with a bunch of unintelligible
garbage. Not to mention a major system crash...

When the NOP instruction is encountered no operations will take place and the
CS:IP will then be incremented to the next instruction to be executed. A lot
of compilers leave nop's in the code all the time and it's a great instruction
you can use to wipe out entire lines of code with.

The above methods of bypassing the call are called 'dirty' cracks in that they
have only modified the end result of the protection check and have done
nothing to alter the actual protection check itself.

All the techniques I showed you above are only implemented after the check is
made. They will bypass the nag screen, but what if the program also has
registered features that are disabled or displays another nag screen upon
exit? The above methods only remove the original nag screen and don't address
the reason the screen is being displayed in the first place.

A much cleaner way to crack the above piece of code would modify the cause and
not the effect. And could be written like this:

original code new code

09D8:0140 CMP BYTE PTR [A76C],00 09D8:0140 MOV BYTE PTR [A76C],01
09D8:0145 JNZ 014B 09D8:0145 JMP 014B
09D8:0148 CALL 0C50 09D8:0148 CALL 0C50
09D8:014B MOV AH,18 09D8:014B MOV AH,18

Remember that the protection check is basing it's actions on the value
contained in the memory location that the first line of code is checking. The
original code displayed the nag screen if the value of that location was 00,
meaning it was unregistered. So that means a value of 01 indicates a
registered copy. It could be the other way around as well, it just depends on
how the programmer worded the source code. But we know in this case that
00=false so 01=true. These are Boolean expressions and most compilers use the
AX register to return these values.

By changing the first line from CMP BYT PTR [A76C],00 to MOV BYTE PTR
[A76C],01 the program no longer performs the protection check. Instead, it
places the correct value in the memory location to indicate a registered copy.
Now if the program checks that memory location again later on it will think
that it is registered and activate all of it's disabled features, or not
display a second nag screen upon it's exit if it has one.

I changed the second line of code to an unconditional jump because the compare
instruction on the first line no longer exists, and the conditional jump on
the second line may still access the call to the nag screen on the third line
if the Z flag was already set before the old compare instruction was
encountered.

Don't think that all programs are this easy, they're not. I just
over-simplified this example for instructional purposes. And I really wouldn't
patch the code like that, although the last method should work fine for all
registered features to be enabled. Remember I told you there was a best way to
crack this?

What I would actually do is to trace further back into the program and find
the line of code that sets up the memory location referenced by line one of
the code for the protection check in the first place and modify it there. This
is an example of a 'clean' crack.

I just did it in the above manner to try and show you the difference between
clean and dirty cracks without totally confusing you. And to give you a
general idea on how to creatively modify existing code.

If you are using soft ice as your debugger, an easy way to find the
instruction that sets up the memory location for the protection check is to
set a breakpoint on the location when it gets 00 written to it. The syntax
would be BPM XXXX:XXXX W EQ 00, where XXXX:XXXX is the address of the memory
location referenced by the compare instruction on line 1.

Now when the program wrote 00 to that memory location, soft ice will pop up
and the CS:IP will be sitting at the next instruction after the one that wrote
00 to the memory location. You will now be able to evaluate the code around
the instruction that writes to the memory location and decide on how to
proceed.

This also could just be a general purpose location that the program uses for
generic references (especially if it's in the stack segment), and it could
write 00 to it several times throughout the course of the program for a
variety of different functions. You should let the program run normally after
soft ice broke in to see if it will trigger the breakpoint again. If it
doesn't you know that the location is only used for the protection check. But
if the breakpoint gets triggered several more times, you will have to figure
out which set of instructions are being used to set up for the protection
check before proceeding.

The above examples were based on shareware programs. Now I'll go over a few
techniques to remove copy protection from commercial games that have doc
checks in them as the methods are slightly different...

shareware programs are usually coded so that they check a variable in memory
before deciding if they are registered or not and how to proceed. Commercial
games with doc checks take a different approach as they check nothing before
calling the copy protection. It always gets called every time you play the
game no matter what. As a result, the doc check routine is usually easier to
find, and there are basically two types of doc checks... The passive check,
and the active check.

The passive doc check is easier to defeat than the active. In the passive doc
check, the program will issue a call to the copy protection routine. And if it
is unsuccessful will either abort the program, or loop back to the beginning
of the routine and give you a few more tries before aborting. The entire
protection routine will be included in a single call, so merely nopping out
or bypassing the call will be sufficient to remove the copy protection.

A few good examples of this are Spear of Destiny by ID, and the Incredible
Machine by Sierra. Yes I know that they are old, but if you happen to have a
copy of either one laying around they are excellent examples of passive doc
checks to practice on.

Look at the following piece of code:

0277:01B5 MOV [AF56],AX
0277:01B8 PUSH BX
0277:01B9 PUSH CX
0277:01BA CALL 0234
0277:01BD POP CX
0277:01BE POP BX
0277:01BF JMP 0354

The first three lines of code are just setting up for the call, the call on
the fourth line is the protection check itself. It will display the input
window asking for a word from the manual, will perform the protection check,
and will display an error message if you input the wrong word. It can also
optionally give you a few more tries if you type in the wrong word.

If you fail the protection check, the program will abort without ever having
returned from the call. The fifth, sixth, and seventh lines are the next
instructions to be executed if the protection check was successful and the
program returns from the call.

This type of protection is trivial to defeat, all you have to do is the
following:

original code new code

0277:01B5 MOV [AF56],AX 0277:01B5 MOV [AF56],AX
0277:01B8 PUSH BX 0277:01B8 PUSH BX
0277:01B9 PUSH CX 0277:01B9 PUSH CX
0277:01BA CALL 0234 0277:01BA NOP
0277:01BD POP CX 0277:01BB NOP
0277:01BE POP BX 0277:01BC NOP
0277:01BF JMP 0354 0277:01BD POP CX
0277:01BE POP BX
0277:01BF JMP 0354

Simply nopping out the call to the protection routine will be sufficient to
crack this type of doc check. No window asking for input will appear, and the
program will continue on as if you had entered the correct word from the
manual. Remember that I told you that the NOP instruction is only one byte in
length, so you have to enter as many nop's as it takes to equal the length of
the code you are modifying.

The active doc check is more complex. The program will issue the check and
unlike the passive protection, will set a variable in memory somewhere and
reference it later on in the program.

You can crack this type of protection somewhat using the methods for the
passive check and it might run fine for a while. But if you didn't crack it
right, later on when the next episode gets loaded or you reach a crucial point
in the game, the program will reference a memory location and bring up the
copy protection again, or abort. This type of protection is more akin to how
most shareware programs operate and MUST be done with a CLEAN crack.

Look at the following piece of code:

0234:0B54 MOV CX,0003 ;Sets up to give you three tries
0234:0B57 DEC CX ;deducts one for every time through the loop
0234:0B58 JCXZ 031A ;when CX=0000, program will abort
0234:0B60 PUSH CX ;just setting up for the call
0234:0B61 PUSH DS ; " "
0234:0B62 PUSH ES ; " "
0234:0B63 CALL 035F:112D ;call to input window and validation routine
0234:0B68 OR AL,AL ;seeing if check was successful
0234:0B6A JNZ 0B6E ;yes, continue on with the program
0234:0B6C JMP 0B57 ;no, set up for another try
0234:0B6E CALL 8133 ;next line in the program if check was good

The above code is the outer loop of the protection routine. Look at the call
on the seventh line and the compare instruction on the eighth line. When the
call to the input routine or in the case of shareware, the check routine is
paired with a compare instruction in this manner, You can bet that the program
set a memory variable somewhere inside the call. Especially suspicious is the
unconditional jump on line 10 that jumps backwards in the code.

This won't always be the case as no two programs are alike, and simply
changing line 9 of the code from JNZ 0B6E to JMP 0B6E to force the program to
run even if you fail the doc check may allow the program to run just fine.
Let's say that this is how you patched the program and it runs. Great, your
work is done... But what if before the first level loads, or at some other
point within the program the input window pops up again asking for a word from
the manual?

You realize that you should have patched it right in the first place as you
now have to go back in there and fix it. This is why so many groups have to
release crack fixes, they patch the program in a hurried manner and don't even
run it all the way through to see if it's going to work.

Ok, back to the problem at hand... The above method of patching the program
didn't work, so you now have to load the program back into the debugger and
trace into the call on line seven to see whats going on in there. And you
can't NOP this kind of call out either, this is an intrasegment call.

Certain things in programs get assigned dynamic memory locations, and
intrasegment calls are one of those things. When the program gets executed,
the code segment, data segment, extra segment, and stack segment get assigned
their respective addresses based on the memory map of your computer.

And when a program does a FAR call (a call to a segment of memory outside the
current code segment), The program goes to the address that was assigned to
that segment at run time. The CS, DS, ES, and SS will be different on every
computer for the same program.

And seeing as how these addresses don't get assigned until run time, the
actual bytes for the addresses of far calls don't exist in the program file as
it resides on your disk. That's why you can't just NOP a CALL FAR instruction
out.

However, the bytes for calls that are within the same segment of code as the
calling instructions themselves will be contained within the file as it
resides on disk. And that is because even though the program doesn't get the
addresses for the actual segments until run time, the offsets within those
segments will always be the same.

Back to the example, let's say you've traced into the call on line seven and
this is what you see:


035F:112D MOV [324F],BX ;
035F:1131 CMP BYTE PTR [BX+06],03 ; just some error checking
035F:1135 JNZ 0339 ;

035F:1137 CALL F157 ; call to the input window that
; asks you to type a word in from
;the manual

035F:113A MOV DI,[0332] ; this routine is comparing the
035F:113D MOV ES,DX ; word you typed in to a word
035F:1140 MOV DS,BX ; in memory that the program is
035F:1144 MOV SI,[0144] ; referencing. As long as the
035F:1148 MOV CX,[0097] ; bytes match the loop will
035F:114C REPE CMPSB ; continue.

035F:114F JCXZ 1154 ; This is the routine that sets
035F:1151 JMP 1161 ; the memory variable. 01 will be
035F:1154 MOV AX,0001 ; placed in it if you typed in
035F:1159 MOV [0978],AX ; the correct word. 00 will be
035F:115E JMP 116B ; placed in it if you typed in
035F:1161 MOV AX,0000 ; the wrong word.
035F:1166 MOV [0978],AX ;

035F:116B POP ES ; setup to return from call
035F:116C POP DS ; " "
035F:116D POP CX ; " "
035F:116E RETF ; return from call


Again, this code is over simplified as I figured all of the code would be
overwhelming and really is not needed to get my point across. And as I've
stated before, every program will be different anyway, so the actual code
wouldn't help you. Instead, I want to give you a general overview on what to
look out for.

So, what do you think is the best way to patch the above piece of code? Take a
few minutes to study the code and formulate some ideas before reading on. Then
compare your methods to mine. And remember, as with any code there is no
single way. But as always, there is a best way... I'll go over few of them one
at a time, starting with the dirtiest and finishing up with the cleanest.

The dirtiest crack for this piece of code also happens to be the method you
will use to nop out intrasegment calls. It really isn't nopping out, but
seeing as how you can't nop it out, just let the program make the call and
change the first line of the code within the call to RETF. This will return
from the call without ever having executed any of the code contained within
it.

In the case of registers needing to be restored as in the above code, change
the first line of code to jump to the part of the routine that restores the
registers for the return. However, in the above example if you use this method
and just return from the call without executing any of the code, you will also
have to patch the outer loop as well.

Remember that this call only displays the input window and sets the memory
variable. The outer loop of the routine makes the decision on how to proceed
based on the results of the call.

To do this, you would change line one of the call from MOV [324F],BX to JMP
116B. This will restore the registers and return from the call without ever
having executed any of the code within the call. But seeing as none of the
code got executed, you'll have to patch line 9 of the outer loop from JNZ 0B6E
to JMP 0B6E as you now need an unconditional jump to force the program to
continue. This doesn't address the problem of the memory variable though, and
the program won't be completely cracked. That's why if you did it like this
you would end up releasing a fix.

A cleaner crack would be to change line 11 of the call from JCXZ 1154 to JMP
1154. Now when the window pops up and asks for a word, it will set the correct
memory variable and the program will run no matter what word you type in. This
method is still not desirable because the end user will get the input window
and have to type something every time they play the game.

The cleanest way to crack this, and the way I would do it is to change line 4
of the call from CALL F157 to JMP 1154. This method will totally bypass the
input window, place the correct variable in memory and return from the call
without the end user ever having seen even a hint of copy protection.

With this method, the outer loop does not need to be patched cause the program
now thinks that it displayed the input window and the correct word was typed
in. Now when the program checks that memory variable later on, it will think
that you successfully passed the original check and skip the second protection
check.

There is also an added benefit to the last method... Some games will bring up
the protection check between each and every level of the game even though you
type the correct word in every time. But if you've completely killed the
routine as in the last example, you'll never be bothered by it again no matter
how many times the program tries to bring it up.

Please be aware of the fact that these are not the only methods that
programmers will use in copy protection schemes. These are just the basics and
there are several variations on these routines. The only way to be able to
know what any given routine is doing at any time is to master assembly
language.

Before we move onto the walk though, there is one other technique I want to go
over with you. And that is how to get out of a loop. You will get stuck in
loops constantly during the course of debugging a program and knowing how to
get out of them will save you a lot of time and frustration. You will find
that programs contain loops within loops within loops etc... Some loops can
execute hundreds of times before the program will advance, especially ones
that draw screens.

When you realize that you are stuck in a loop, execute the loop several times
and keep an eye on the highest address the loop reaches before jumping
backwards within the code. Once you have found the end of the loop, write down
the address of the jump that re-executes the loop, and then look for
conditional jumps inside the loop that will put you past the address of that
backwards jump. You will want to set a breakpoint on the address this
instruction jumps to and then let the program run normally. The HERE KEY is
excellent for this type of situation.

If you guessed right, control will be returned to the debugger when the
program reaches that instruction. If you guessed wrong, you will lose control
of the program and will have reload it and try again. This is where writing
down the address comes in handy, just reload the program and then issue the GO
TO command and supply it the address of the backwards jump that you wrote
down.

The program will run until it reaches that address and control will then be
returned to the debugger. This will save you from having to trace all the way
through the code again in order to reach the point where you lost control of
the program in the first place. You could just use sticky breakpoints instead,
but what you will end up with is a half dozen or so breakpoints in as many
different locations in the code, and it's very easy to loose track as to which
breakpoint is which.

That's why I use non-sticky breakpoints and write down the address I'm
currently at before executing suspicious looking calls and jumps. My desk is
usually scattered with scraps of paper filled with notes and addresses. I only
use sticky breakpoints for specific situations. It's much easier to just
reload the program and use the GO TO command to get back to the point in the
program where I lost control.

No comments:

Post a Comment