1617 lines
84 KiB
Plaintext
1617 lines
84 KiB
Plaintext
A Beginners Guide to Cracking
|
|
|
|
|
|
|
|
Chapter 1 Overview
|
|
|
|
Chapter 2 Some tips on how to use the debugger
|
|
|
|
Chapter 3 Some basic cracking techniques
|
|
|
|
Chapter 4 Walk through of an easy crack
|
|
|
|
Chapter 5 How to use the disk editor
|
|
|
|
Chapter 6 Other cracking tools
|
|
|
|
Chapter 7 Source code to a simple byte patcher
|
|
|
|
Chapter 8 Conclusion
|
|
|
|
|
|
Programs included with this guide
|
|
|
|
|
|
UNP411.ZIP An important Cracking tool
|
|
|
|
SS33S.ZIP Another importnat Tool
|
|
|
|
BUDGET.ZIP Program to crack for the walk through
|
|
|
|
|
|
|
|
|
|
CHAPTER 1 OVERVIEW
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
|
|
You might be wondering what type of programming skills you need to become a
|
|
cracker. Knowing a higher level language such as Basic, Pascal, or C++ will
|
|
help you somewhat in that you will have an understanding of what's involved in
|
|
the process of writing a program and how certain aspects of a program
|
|
function. If you don't have any programming skills at all, you have a long
|
|
road ahead of you. But even if you can program in a high level language, in
|
|
order to crack you have to know assembly...
|
|
|
|
It really doesn't matter what language a program was written in in order to
|
|
crack it, because all programs do the same thing. And that is issue commands
|
|
to the microprocessor. And all programs when broken down to their simplest
|
|
form are nothing more than a collection of 80XXX instructions and program
|
|
specific data. This is the level of assembly language. In assembly you have
|
|
total control of the system. This is also the level that the debugger operates
|
|
at.
|
|
|
|
You don't have to become a master at assembly to crack a program, but it
|
|
helps. You do need to learn some rudimentary principles, and you absolutely
|
|
have to become familiar with the registers of the cpu and how the 8088
|
|
instruction set uses them. There is no way around this.
|
|
|
|
How proficient you are at assembly will determine how good of a cracker you
|
|
become. You can get by on learning a few basic instructions, how to use a
|
|
debugger, and one or two simple techniques. This will allow you to remove a
|
|
few shareware nag screens, and maybe you'll luck out and remove the copy
|
|
protection from a game or two, but that's it.
|
|
|
|
As soon as a programmer throws some anti-debugging code into a program or
|
|
starts revectoring interrupts, you'll be whining for someone to post a crack
|
|
for this or that... And you can forget about ever learning to crack windows
|
|
programs.
|
|
|
|
It's much much easier to learn to crack in DOS than windows. DOS is the
|
|
easiest environment to debug in. This guide will focus on DOS programs as
|
|
cracking windows apps is a little bit overwhelming unless you are already an
|
|
experienced cracker. And if you are, your wasting your time by reading this.
|
|
This manual is geared towards the raw beginner who has no clue as to where to
|
|
start and needs a little hand holding in order to get going.
|
|
|
|
There are several good beginners manuals out there, but most of them assume a
|
|
person has at least some experience in cracking or knows how to use the
|
|
different tools of the cracker, and the raw beginner usually becomes
|
|
frustrated with them very quickly because they don't understand the concepts
|
|
contained in them.
|
|
|
|
I wrote this guide as sort of a primer for the beginner to read before reading
|
|
the more comprehensive guides. I tried to keep it as simple as possible and
|
|
left a great deal of information out so as not to overwhelm anyone with too
|
|
much information at once. Hopefully after reading this guide it will be easier
|
|
for the beginner to understand the concepts of the more arcane guides out
|
|
there. So if you are reading this and it seems a little bit remedial,
|
|
remember, at one time you didn't know what a debugger was used for either.
|
|
|
|
Now in case your not familiar with the debugger and disk editor and what their
|
|
different roles in cracking are, I'll give a brief explanation of each. As
|
|
these are the crackers most used tools.
|
|
|
|
The debugger is what you will use to actually crack the program. When you load
|
|
a program you wish to crack into the debugger, it will load the program and
|
|
stop at the first instruction to be executed within the code segment. Or, you
|
|
can also optionally break into an already running program and it will halt the
|
|
program at the instruction you broke into it at and await further input from
|
|
you. At this point, you are in control of the program.
|
|
|
|
You can then dynamically interact with the program and run it one line of code
|
|
at a time, and see exactly what the program is doing in real time as each line
|
|
of code is executed. You will also be able to re-assemble instructions (in
|
|
memory only), edit the contents of memory locations, manipulate the cpu's
|
|
registers, and see the effects your modifications have on the program as it's
|
|
running. This is also where all your system crashes will occur... There is a
|
|
lot of trial and error involved in cracking.
|
|
|
|
As stated above, the debugger will only modify the program while it's up and
|
|
running in memory. In order to make permanent changes, you need to load the
|
|
program file to be patched into the disk editor and permanently write the
|
|
changes you've made to disk. A detailed explanation of how to do this will be
|
|
made in chapter 5.
|
|
|
|
So, with this in mind, you need a few essential tools... The first one is a
|
|
good debugger. The original draft of this guide gave explicit instructions on
|
|
how to use my favorite debugger. After considerable deliberation, I decided to
|
|
re-write it and make the instructions more generic so you could apply them to
|
|
most any debugger. You will also need a disk editor, it doesn't matter which
|
|
one you use as long as it will load the program file, search for and edit the
|
|
bytes you want to change.
|
|
|
|
I uuencoded a few cracking tools that you will find indespensible and placed
|
|
them at the end of this guide. I won't go into the use of the cracking tools
|
|
right now. But believe me, you absolutely need one of them, and the other one
|
|
will save you a lot of effort. I also uuencoded the program that we will crack
|
|
in the walk through and included it in this guide as well.
|
|
|
|
As you get better, you'll have to write programs that will implement your
|
|
patches if you decide to distribute them. The patches themselves don't have to
|
|
be written in assembly.
|
|
|
|
The source code I included in this manual for the byte patcher is the first
|
|
patcher program I ever wrote, and is extremely simple. It's written in
|
|
assembly because that's the only language I know how to program in. but if you
|
|
are already proficient in a higher level language, it should be trivial for
|
|
you to duplicate it's methods in your preferred language.
|
|
|
|
|
|
|
|
|
|
CHAPTER 2 SOME TIPS ON HOW TO USE THE DEBUGGER
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
|
|
Ok, before I begin, I'd just like to stress how important it is that you know
|
|
at least some assembly before trying to continue. If you don't, you will get
|
|
lost pretty quick from here on out. Comprehension of the base 16 (hexadecimal)
|
|
number system is also required.
|
|
|
|
I'm not about to give a remedial course on assembly or hex math, that would
|
|
take too long and I'd probably leave too many questions un-answered. Besides,
|
|
there is enough information on them available from a myriad of other sources.
|
|
|
|
So, from now on in this guide, I'm assuming you have a fair working knowledge
|
|
of assembly and hexadecimal. If I say something you don't understand or you
|
|
cannot grasp some concept, look it up somewhere...
|
|
|
|
I've tried to make this section as generic as possible. I used general
|
|
descriptions when explaining HOTKEYS and COMMANDS as different debuggers will
|
|
use different keys and command syntax to implement these functions.
|
|
|
|
You should be able to translate these instructions to the actual key strokes
|
|
and commands that your debugger uses... If you don't know how to use a
|
|
debugger, PAY ATTENTION!!! If you already know how to use a debugger you can
|
|
skip this section as it is only a general overview of different windows and
|
|
functions designed for the absolute beginner who has no clue as to what he is
|
|
looking at.
|
|
|
|
The reason I included this section is because most manuals for debuggers tell
|
|
you how to use the various features of the debugger, but they don't give any
|
|
insight on how to apply those features, as they assume the person reading them
|
|
already knows how to debug a program.
|
|
|
|
First, I'll give an overview on the different windows that most debuggers use.
|
|
|
|
|
|
REGISTER WINDOW:
|
|
|
|
The register window contains the general purpose and flags registers of the
|
|
cpu. You will notice that the general purpose registers contain hexadecimal
|
|
values. These values are just what happened to be in there when you brought up
|
|
the debugger. you will also notice that some of the flags are highlighted
|
|
while some are not. Usually, the highlighted flags are the ones that are SET.
|
|
While the ones that are not highlighted are CLEARED. The layout of this window
|
|
will vary from debugger to debugger, but they all basically are the same.
|
|
|
|
From this window you will be able to manipulate the contents of the cpu's
|
|
registers. some debuggers accomplish this by clicking on the register to
|
|
modify with the mouse and then entering a new value. Other more powerful
|
|
debuggers use a command line interface, you'll have to discover how your
|
|
debugger goes about this yourself.
|
|
|
|
You can change the values of the registers while debugging a program in order
|
|
to change the behavior of the running program. Say you come across a JNZ
|
|
instruction (jump if not zero), that instruction makes the decision on whether
|
|
or not to make the jump based on the state of the (Z)ero flag. You can modify
|
|
the condition of the (Z)ero flag in order to alter the flow of the programs
|
|
code.
|
|
|
|
By the same token, you can modify the general purpose registers in the same
|
|
manner. Say the AX register contains 0000, and the program bases it's actions
|
|
on that value, modifying the AX register to contain a new value will also have
|
|
the effect of modifing the flow of the code. After you become comfortable with
|
|
using a debugger you'll begin to appreciate just how powerful this window is,
|
|
and you'll aslo discover soon enough just how totally it can screw your
|
|
system.
|
|
|
|
|
|
DATA WINDOW:
|
|
|
|
The data window will display data as it exists in memory. From this window you
|
|
can usually display, search, edit, fill, and clear entire ranges of memory.
|
|
The two most common commands for this window are display and edit. The search
|
|
command is also useful in cracking. But for the level of debugging I'll be
|
|
teaching you in this guide, we won't make much use of this window. You have a
|
|
lot to learn before this window becomes an asset to you.
|
|
|
|
|
|
CODE WINDOW:
|
|
|
|
The code window is the window in which you will interact with the running
|
|
program. This is the most complex window, and it is where the bulk of
|
|
debugging occurs. I'll just go over some keystrokes and a few commands here,
|
|
as the majority of learning how to use this window will come when I show you
|
|
how to crack a program.
|
|
|
|
The layout of the window is pretty simple, the group of 8 numbers with the
|
|
colon in the middle of them to the far left of the window is the
|
|
address:offset of that line of code. Each line of code in this window is an
|
|
instruction that the program will issue to the microprocessor, and the
|
|
parameters for that instruction. The registers that contain the address for
|
|
the current instruction waiting to be executed are the CS:IP registers (code
|
|
segment and instruction pointer).
|
|
|
|
You will also notice a group of hex numbers to the right of the addresses,
|
|
this group of numbers is the hexadecimal equivalent of the mnemonic
|
|
instructions (pronounced new-mon-ik). The next group of words and numbers to
|
|
the right of the hex numbers are the mnemonic instructions themselves.
|
|
|
|
HOTKEYS AND COMMANDS:
|
|
|
|
Now we'll move onto the HOTKEYS. I won't go into all of them, only the most
|
|
useful ones, same for the commands.
|
|
|
|
The RESTORE USER SCREEN KEY: This key will toggle the display between the
|
|
debugger and the program you are debugging without actually returning control
|
|
to the program itself. it's useful to check what the program is doing from
|
|
time to time, especially after stepping over a CALL.
|
|
|
|
The HERE KEY: This key is the non-sticky breakpoint key. To use it, Place the
|
|
cursor on a line of code and hit it. The program will then run until it
|
|
reaches that line. When (and if) the program reaches that line, program
|
|
execution will halt, control will be returned to the debugger and the
|
|
breakpoint will be removed.
|
|
|
|
The TRACE KEY: This key will execute one line of code at a time and will trace
|
|
into all calls loops and interrupts.
|
|
|
|
The BREAKPOINT KEY: This is the sticky breakpoint key. This will enable a
|
|
permanent (sticky) breakpoint on the line of code that the cursor is on. When
|
|
a sticky breakpoint is enabled, program execution will halt and control will
|
|
be returned to the debugger every time that line of code is encountered within
|
|
the running program until you manually remove it.
|
|
|
|
The SINGLE STEP KEY: The most used key on the keyboard. This key will execute
|
|
one line of code at a time but will not trace into calls loops or interrupts.
|
|
When you step over a call interrupt or loop with this key, all the code
|
|
contained within the sub-routine is executed before control is returned to the
|
|
debugger. If the program never returns from the sub-routine, you will lose
|
|
control and the program will execute as normal.
|
|
|
|
The RUN KEY: This key will return control to the program being debugged and
|
|
allow it to execute as normal. Control will not be returned to the debugger
|
|
unless a breakpoint that you've set is encountered.
|
|
|
|
Now for a few commands. The GO TO command functions like the HERE key in that
|
|
it will insert a non-sticky breakpoint at the specified address.
|
|
|
|
When you enter this command the debugger will return control to the program
|
|
until the line of code you specified in the GO TO command is reached. When
|
|
(and if) the CS:IP registers equal the address you typed in, the program will
|
|
halt, control will be returned to the debugger and the breakpoint will be
|
|
removed.
|
|
|
|
You might be wondering why you would want to type all this in when you can
|
|
just hit the HERE KEY instead. The answer is this; the HERE KEY is great if
|
|
you want to set a local breakpoint. By a local breakpoint I mean that the
|
|
breakpoint you want to set is somewhat close to your current location in the
|
|
program.
|
|
|
|
But what if you want to set a breakpoint on a line of code that isn't in the
|
|
current code segment? You wouldn't want to use the HERE KEY cause the address
|
|
is no where near the point you are at in the program. This, among other uses
|
|
is where the GO TO command comes in.
|
|
|
|
The ASSEMBLE command is the command you will use to re-write the programs
|
|
instructions. This command will allow you to assemble new instructions
|
|
beginning at the address you type in, or at the current CS:IP. The
|
|
instructions you enter will replace (in memory only) the existing program code
|
|
at the address you specified. This is another method you will use to alter the
|
|
running program to behave as you wish and not as the programmer intended it
|
|
to.
|
|
|
|
EXAMPLE: Lets say that there is a line of code that reads JNZ 04FC, and we
|
|
want to change it to read JMP 04FC. You would issue the ASSEMBLE command and
|
|
specify the address of the code you wish to change, then type in JMP 04FC.
|
|
Now the line of code in the code window who's address you specified in the
|
|
ASSEMBLE command will be overwritten with the code you typed in. Some
|
|
debuggers automatically default to the address contained in the CS:IP for this
|
|
command.
|
|
|
|
There are a whole host of other commands available in this window depending on
|
|
what debugger you are using, including commands to set breakpoints on
|
|
interrupts, memory locations, commands that list and clear breakpoints,
|
|
commands to un-assemble instructions etc etc...
|
|
|
|
Well, that's pretty much it on debuggers without going into explicit
|
|
instructions for specific debuggers. The only other thing I can tell you is
|
|
that the more you use it, the easier it'll get. Don't expect to become
|
|
familiar with it right away. As with anything, practice makes perfect. It's
|
|
taken me 5 years and thousands of hours of debugging to reach the level I'm at
|
|
now. And I still learn something new, or re-learn something I forgot on just
|
|
about every program I crack.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
CHAPTER 4 WALK THROUGH OF AN EASY CRACK
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
|
|
First of all, I want to go over some of the criteria I used in choosing the
|
|
program I used for the walk through. An important factor was the programs
|
|
size. I want to keep this manual as small as possible, and I chose the program
|
|
that is included in this guide because among other things it is the smallest
|
|
one I could find that best illustrated the example of a simple nag screen.
|
|
|
|
Whether or not the program was one that you would actually find useful was not
|
|
a consideration, as you should eventually be able to crack just about any
|
|
program you wish if your serious about cracking. If you come across a program
|
|
that has you stumped, leave it alone for a while and then try again after
|
|
you've cracked something else. You may find that whatever you were having
|
|
problems with is now easier to understand.
|
|
|
|
Before we start I want to go over one other thing. When you load a program
|
|
into a debugger, the debugger will load the program and halt at the very first
|
|
instruction to be executed within the program. You can also at this point let
|
|
the program run normally and then break back into it at a later point.
|
|
|
|
When you use the second method it will halt the program at the current
|
|
instruction and return control to the debugger, but you may not end up in the
|
|
program itself. You could have broken into the program while it was in the
|
|
middle of executing either a DOS or BIOS interrupt, and the code you are in
|
|
belongs to either DOS or BIOS and not the program you are debugging.
|
|
|
|
You can tell by looking at the addresses of the instructions in the code
|
|
window where you are, low segment addresses indicate you are in DOS, and
|
|
addresses that start with FXXX indicate a BIOS routine.
|
|
|
|
If you break into the program while it is in one of these interrupt routines
|
|
you will have to trace your way back into the programs code, this will usually
|
|
be indicated by an IRET (interrupt return) instruction. When you do get back
|
|
to the program code, you will then have to trace your way back to the top of
|
|
the call that issued the interrupt you broke into. Then you may also have to
|
|
trace back to the top of that call, and to the top of that call, etc etc,
|
|
until you reach the top level of the program. After you've done this a few
|
|
times you'll begin to recognize when you've gotten back to the main flow of
|
|
the program...
|
|
|
|
On the other hand, when you load a program into the debugger and begin
|
|
stepping through the code from the very first instruction to be executed
|
|
within the program, you have the best picture on the overall flow of the
|
|
program as you are sitting on top of everything.
|
|
|
|
But some programs don't access the copy protection until they are further
|
|
along in the code. In this case, it's best to let the program run normally and
|
|
then break into it at a later point. Otherwise, you will have a ton of code to
|
|
trace through before the protection routine is accessed, and this can become
|
|
quite tedious. Which method you choose will be determined after you've run the
|
|
program through a few times and decide how and where you want to break into
|
|
it.
|
|
|
|
One last thing, DOS will always load a program into the same memory range
|
|
provided that no other programs are run in the interim. It's important that
|
|
you always boot with the same config files and don't run any other memory
|
|
resident programs between cracking sessions.
|
|
|
|
If you load a program into the debugger and start tracing, then quit. And
|
|
before The next time you load that same program into the debugger, you boot
|
|
with a different config or load a memory resident program that you didn't have
|
|
loaded the first time you started cracking that program, the segment addresses
|
|
will change and the addresses you wrote down will be useless. This is because
|
|
the memory map of your computer will change.
|
|
|
|
When I boot with my debugging config (when I use my DOS debugger, Windows
|
|
manages memory differently and these steps are not needed), the only things I
|
|
load are a mouse driver, 4DOS, my debugger and ansi.sys (needed for cracking
|
|
bbs doors). This way I'm assured that the program I want to crack gets loaded
|
|
into the same memory region every time I run it, providing I don't run any
|
|
other memory resident programs before loading the program to be cracked into
|
|
the debugger.
|
|
|
|
Take soft ice as an example, if you load a program into it using LDR.EXE and
|
|
begin debugging, then later on you decide to just execute the program and
|
|
break into it without first loading it with LDR.EXE, the segment addresses
|
|
will change. That's because LDR.EXE is a program and using it will throw the
|
|
segment addresses off by one word as opposed to just breaking into an already
|
|
running program without first loading it with LDR.EXE.
|
|
|
|
The program we will crack is budget minder, it is an extremely simple crack
|
|
(it took me about 2 minutes to crack it) and is ideal for the lesson on how to
|
|
remove nag screens from otherwise fully functional programs. It also deals
|
|
with intrasegment calls, so it serves a dual purpose. That's another reason I
|
|
chose it for the lesson.
|
|
|
|
From now on, when I say step, step through, or step over, I want you to use
|
|
the SINGLE STEP key. When I say trace, I want you to use the TRACE key once
|
|
and only once!!!! The TRACE key is a highly specialized key and is not
|
|
intended to be used multiple times like the SINGLE STEP key. If you don't
|
|
follow these instructions, your gonna get lost...
|
|
|
|
OK, once you've run budget minder a few times you will notice that it displays
|
|
a nag screen before the main program is executed. You will also notice that
|
|
this nag screen is the only type of protection that the program has. It
|
|
doesn't contain any features that are disabled, nor does it display an
|
|
additional nag screen upon exit.
|
|
|
|
It's okay to apply a dirty crack to this program as all you want to do is kill
|
|
the nag screen, so you have a little more leeway on how to patch it. And if
|
|
you want to try different methods of patching it than the ones I give, it
|
|
should still work fine.
|
|
|
|
That was the most important factor in my decision to use this program for the
|
|
lesson. I wanted to walk you through a program so you would become comfortable
|
|
with it's flow, and I also wanted the program to be easy enough so that once
|
|
you became familiar with it, there was enough room for you to experiment and
|
|
try out your own methods.
|
|
|
|
In this case, it's best to load the program into the debugger and start
|
|
stepping through it right away. The protection is implemented very close to
|
|
the beginning of the program, and this method of loading the program will put
|
|
you right on top of everything.
|
|
|
|
Allowing the program to run and breaking into it later on will not serve any
|
|
useful purpose. You'll just end up having to trace your way back to the top.
|
|
Besides, the nag screen comes up so fast you'll probably miss it if you try
|
|
the second method anyway.
|
|
|
|
Before you load it into the debugger, run UNP on BUDGET.EXE... AHA! The file
|
|
was compressed with EXEPACK. It's now ready to debug as you've removed the
|
|
compression envelope. Just for the hell of it, run UNP on it again. I've come
|
|
across a few programs that have had multiple compression routines on them. If
|
|
it shows up negative, your set to go.
|
|
|
|
Now load BUDGET.EXE into the debugger, the program will be sitting at the
|
|
first instruction to be executed awaiting your next command... Use the SINGLE
|
|
STEP key to start stepping through the code and keep an eye on the
|
|
instructions as you are stepping through them.
|
|
|
|
Shortly you will come to a couple of calls, before you step over the first
|
|
one, write down it's address. Now step over the first call with the SINGLE
|
|
STEP key. Nothing happened, so you have to continue stepping through the code.
|
|
But if something did happen when you stepped over this call like the nag
|
|
screen being displayed or if you lost control of the program, you could just
|
|
reload the program and issue the GO TO command to get back to that point using
|
|
the address you wrote down.
|
|
|
|
Step over the second call, nothing again. Ok, keep stepping through the code
|
|
and keep an eye on the instructions. You will encounter a third call about 6
|
|
instructions or so after the second call, step over it with the SINGLE STEP
|
|
key... Bingo, you have found the call to the nag screen. Hit a key to exit the
|
|
nag screen and you will now be sitting in the main program screen.
|
|
|
|
But you no longer have control of the program. Remember I said you would loose
|
|
control if you step over a call loop or interrupt and the program never
|
|
returns from it? Hopefully you wrote down the address of that last call before
|
|
you executed it. Now you can just quit out of the program and reload it. Then,
|
|
once it's reloaded, issue the GO TO command to get back to the call without
|
|
having to trace your way back there. So go ahead and do this before reading
|
|
on...
|
|
|
|
Ok, we are all back at the third call. It's address will be CS:0161, remember
|
|
that the segment adresses will always be different for every computer, but the
|
|
offsets will be the same. So from now on I'll write the addresses in that
|
|
manner...
|
|
|
|
We know that the last time we executed this call, the program never returned
|
|
from it. So now we are going to have to trace into it for a closer look. Trace
|
|
into the call with the TRACE key, don't use the SINGLE STEP key this time or
|
|
you'll loose control again.
|
|
|
|
You will now be inside the code for that call, start stepping through it again
|
|
with the SINGLE STEP key, you will see some calls. Better write down your
|
|
address before you step over them.
|
|
|
|
Step over the first two calls, nothing... Use the RESTORE USER SCREEN key to
|
|
toggle the display between the debugger and the program. Still a blank screen,
|
|
so nothing important has happened yet. Now toggle the RESTORE USER SCREEN key
|
|
to get the debugger screen back and continue stepping through the code.
|
|
|
|
You will see another call and some more code, just step through them until you
|
|
reach the RETF instruction and stop there. Toggle the display with the RESTORE
|
|
USER SCREEN key, the screen is still blank...
|
|
|
|
But we executed all of the code within the call and are ready to return
|
|
without anything happening. The nag screen didn't get displayed nor did we
|
|
loose control and end up in the main program, How come?
|
|
|
|
Step over the RETF instruction with the SINGLE STEP key and you'll see why...
|
|
The address that we return to is not the next instruction after the original
|
|
call. Part of the code within the call we traced into revectored the return
|
|
address for the original call and sent us to an entirely different location
|
|
within the program.
|
|
|
|
This is why we lost control when we first stepped over the call, the debugger
|
|
was expecting the program to return to the next instruction after the original
|
|
call, but it never did...
|
|
|
|
So the instruction that we returned to was not the original line of code that
|
|
was expected, instead we are at another far call. If you haven't gotten lost
|
|
you should be at CS:0030 CALL CS:28BC.
|
|
|
|
Write down the address of the CS:IP and then step over this call with the
|
|
SINGLE STEP key, there is that annoying nag screen again. Hit a key to exit
|
|
the nag screen and control will be returned to the debugger. This time the
|
|
program returned from the call and you are in control again. So you now know
|
|
that this call is the one that displays the nag screen and it is the one you
|
|
want to kill.
|
|
|
|
Hit the RUN key and let the program run, now quit out of it from the main
|
|
program screen and reload it into the debugger. Use the GO TO command and
|
|
supply it the address for the call to the nag screen.
|
|
|
|
Ok, now lets see if the program will run or not if we don't execute the call
|
|
to the nag screen. The call is at CS:0030 and the next instruction after the
|
|
call is at address CS:0035... A quick way to jump past this call without
|
|
executing it is to just increment the instruction pointer register to the next
|
|
instruction.
|
|
|
|
In this case we want to manipulate the IP register, and we want to set it to
|
|
point to the instruction at CS:0035 instead of the instruction it is currently
|
|
pointing to at CS:0030. You are going to have to figure out the command on how
|
|
to do this with the debugger you are using yourself.
|
|
|
|
If you are using turbo debugger, place the mouse cursor on the line of code at
|
|
CS:0035 and right click the mouse. A window will pop up, then left click on
|
|
new IP, or increment IP. If you are using soft ice, type rip=0035 and hit
|
|
enter. Any other debugger, I have no clue...
|
|
|
|
Now that we've moved the IP past the call to the nag screen let's see if the
|
|
program is going to run. Hit the RUN key, this time the nag screen doesn't
|
|
come up, instead you are brought right into the main program screen.
|
|
|
|
It looks like getting rid of that call is going to do the trick. Now that we
|
|
know the program will run without making that call, it's time to decide on how
|
|
to patch the program so the call is never made again.
|
|
|
|
Think back to the original call we traced into for a minute, that call was the
|
|
one that revectored the return address and brought us to the call to the nag
|
|
screen. Therefore, it's reasonable to assume that that call is the protection
|
|
check, and it might be a good idea to have another look at it.
|
|
|
|
Before we do that there is one other thing I want to show you, and that's how
|
|
to allow the program to make the call to the nag screen and return from the
|
|
call without executing any of the code contained within it.
|
|
|
|
This isn't the method we will use to patch this program, but it's an important
|
|
concept to grasp as you'll end up doing it sooner or later on some other
|
|
program anyway. Remember that this is a far call and you can't just nop it
|
|
out.
|
|
|
|
Quit the program, reload it, and get to the address of the call to the nag
|
|
screen. Last time through we just incremented the IP to bypass it. Now we will
|
|
trace into it to see what it is doing.
|
|
|
|
Hit the TRACE key and trace into the call. Now start stepping through it with
|
|
the SINGLE STEP key, don't bother writing any addresses down for now. There
|
|
are several dozen calls in this routine along with shitloads of other code.
|
|
|
|
Toggle the display with the RESTORE USER SCREEN key after you step over a few
|
|
of the calls and you will see that the program is in the process of drawing
|
|
the nag screen.
|
|
|
|
Keep stepping through it and you'll see more and more of the screen being
|
|
drawn as the code progresses. This is getting boring, so stop stepping through
|
|
the code and start scrolling the code window down with the down arrow key and
|
|
watch the code. If you are using soft ice, the F6 key toggles the cursor
|
|
between the code and command windows, and the cursor must be in the code
|
|
window in order to scroll it.
|
|
|
|
What you are looking for is the RETF instruction as this is the end of the
|
|
call. Keep scrolling, I told you this call had a ton of code in it. When you
|
|
do find the RETF instruction write down it's address, it is CS:2B0E in case
|
|
your having trouble finding it. Ok, you've got the address of the RETF far
|
|
instruction written down so now just let the program run, quit out of it,
|
|
reload it, and get back to the call for the nag screen.
|
|
|
|
You should now be sitting at the call to the nag screen, trace into it and
|
|
stop. The first instruction of the call is MOV CX,0016 and this is where the
|
|
CS:IP should be pointing to. What we want to do now is to jump to the RETF
|
|
instruction and bypass all of the code within the call itself. So let's
|
|
re-assemble the MOV CX,0016 instruction and replace it with a new one.
|
|
|
|
First, make sure you are at this instruction, if you've traced passed it your
|
|
gonna have to reload the program and get back to it... OK, we are all sitting
|
|
at the MOV CX,0016 instruction and it's address is contained in the CS:IP
|
|
registers.
|
|
|
|
Now ASSEMBLE JMP 2B0E (the offset address of the RETF instruction) and specify
|
|
the address of the CS:IP. The MOV CX,0016 instruction will be replaced with
|
|
JMP 2B0E. And seeing as how both of these instructions are the same length we
|
|
didn't have to pad it out with any nop's.
|
|
|
|
Now hit the RUN key, you are brought into the main program and the nag screen
|
|
didn't get displayed! We allowed the program to make the call, but we didn't
|
|
allow any of the code within the call to be executed. And as far as the
|
|
program is concerned, it made the call and the nag screen was displayed.
|
|
|
|
Now let's go back and take another look at the call that we suspect is the one
|
|
that contains the protection check itself. Reload the program and go to the
|
|
original call that revectored the return address, now trace into it. I've
|
|
traced into the calls that are contained in here and they are setting up the
|
|
addresses for the RETF instruction at the end of this call among other things.
|
|
You don't need to trace into them as you might not understand what's going on,
|
|
but if you feel up to it, go right ahead.
|
|
|
|
What I want to concentrate on are the last four lines of code in the call as
|
|
they are the ones that finally set up the address to return to. Step through
|
|
the code until you are at CS:00A8 and take a look:
|
|
|
|
CS:00A8 8B04 MOV AX,[SI] DS:SI=0000
|
|
CS:00AA 053000 ADD AX,0030
|
|
CS:00AD 50 PUSH AX
|
|
CS:00AE CB RETF
|
|
|
|
The first instruction is loading the AX register with the contents of the
|
|
memory location that the SI register is pointing to. And you can see by
|
|
looking at the memory location that the DS:SI pair is pointing to that it
|
|
contains 0000. (this is where the display command and data window come in
|
|
handy).
|
|
|
|
The second instruction is adding 0030 to the contents of the AX register.
|
|
|
|
The third instruction is placing the contents of the AX register onto the top
|
|
of the stack.
|
|
|
|
The fourth instruction is returning from the call, and where do you think that
|
|
the RETF instruction gets the address for the return? Yep, you guessed it, it
|
|
gets it off the top of the stack. Funny that the instruction right before it
|
|
just placed something there isn't it?
|
|
|
|
Also funny is that it happens to be the address of the nag screen. Look at
|
|
what is being added to the AX register on the second line of code. Boy that
|
|
sure looks like the offset address to the nag screen to me.
|
|
|
|
Remember that the next instruction after the nag screen is CS:0035, now look
|
|
at the first line of code. The contents of the memory location it's
|
|
referencing contains 0000, and I'll bet that if your copy was registered it
|
|
would contain 0005 instead.
|
|
|
|
Why? because if the first instruction placed 0005 in the AX register, when the
|
|
second line of code added 0030 to it, you would end up with 0035 which happens
|
|
to be the address of the next line of code after the nag screen.
|
|
|
|
Then the third instruction would place 0035 on the stack and that is where the
|
|
RETF instruction would go to. If this were the case, the nag screen would
|
|
never get displayed...
|
|
|
|
Well, what do you think we should do? We could trace further back in the
|
|
program and try to find the instructions that place 0000 in that memory
|
|
location and modify them to place 0005 in there instead, but this process is
|
|
somewhat involved and I don't want to throw too much at you at once.
|
|
|
|
Instead, I have an easier solution. Seeing as how the memory location will
|
|
always contain 0000, why don't we just change the ADD AX,0030 instruction to
|
|
ADD AX,0035? This should get the correct address placed on the stack for the
|
|
RETF instruction to bypass the nag screen...
|
|
|
|
Let's try it and see how it works. SINGLE STEP through the code until the
|
|
CS:IP is at the instruction ADD AX,0030. Now, ASSEMBLE the instruction to read
|
|
ADD AX,0035 and hit the RUN key. We are placed in the main program screen
|
|
without any stinkin' nag screen getting displayed!
|
|
|
|
Congratulations! you have just cracked your first program :) Try other methods
|
|
of patching the program besides the ones I went over. The next chapter will
|
|
deal with how to make the changes you've made permanent.
|
|
|
|
|
|
|
|
CHAPTER 5 HOW TO USE THE DISK EDITOR
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
|
|
Ok, we cracked budget minder in the debugger and know it's going to work. Now
|
|
we need to make those changes permanent. The first thing we have to do before
|
|
we load the file into the disk editor is to create a search string.
|
|
|
|
So we are going to have to reload budget.exe into the debugger and trace back
|
|
to the location where we want to make the patch in order to get the hex bytes
|
|
of the instructions we want to search the disk file for.
|
|
|
|
Load budget.exe back into the debugger and trace back to the last four
|
|
instructions of the original call that revectored the return address. You
|
|
should be looking at this:
|
|
|
|
CS:00A8 8B04 MOV AX,[SI]
|
|
CS:00AA 053000 ADD AX,0030
|
|
CS:00AD 50 PUSH AX
|
|
CS:00AE CB RETF
|
|
|
|
The group of numbers to the right of the addresses are the hexadecimal
|
|
representations of the mnemonic instructions. These are the bytes that we will
|
|
use for our search string. So write them down beginning from top left to
|
|
bottom right so you end up with this: 8B0405300050CB
|
|
|
|
This is the byte pattern that we will search for when we load the file into
|
|
the disk editor. We have a search string, but we also need to make a patch
|
|
string as well. In order to do this, we will have to assemble the new
|
|
instructions in memory, and then write down the changes we've made to the
|
|
code.
|
|
|
|
So ASSEMBLE ADD AX,35 and specify the address for the old ADD AX,0030
|
|
instruction. The new code should look like this:
|
|
|
|
CS:00A8 8B04 MOV AX,[SI]
|
|
CS:00AA 053500 ADD AX,0035
|
|
CS:00AD 50 PUSH AX
|
|
CS:00AE CB RETF
|
|
|
|
Notice that we only re-assembled the second line of code and that is the only
|
|
difference between the new code and the original code. So what I want you to
|
|
do is to write down the changes under the old code it replaced so it looks
|
|
like this:
|
|
|
|
8B0405300050CB <-- search string
|
|
^
|
|
5 <-- patch string
|
|
|
|
Now we are all set to load the file into the disk editor. We have a string to
|
|
search for and another one to replace it with. Load budget.exe into your disk
|
|
editor, select the search function, and input the search string.
|
|
|
|
NOTE: some disk editors default to an ASCII search so you may have to toggle
|
|
this to hex search instead. If your in the wrong mode, the disk editor will
|
|
not find the byte pattern your looking for.
|
|
|
|
Once the disk editor finds the byte pattern of the search string, just replace
|
|
the bytes of the old code with the bytes to the new code and save it to disk.
|
|
The program is now permanently cracked.
|
|
|
|
Sometimes however, the code you want to patch is generic enough that the
|
|
search string will pop up in several different locations throughout the file.
|
|
It's always a good idea to keep searching for the byte pattern after you've
|
|
found the first match. If the disk editor doesn't find any more matches your
|
|
all set.
|
|
|
|
If the string you are searching for is contained in more than one location and
|
|
you patch the wrong one the crack will not work, or you will end up with a
|
|
system crash when you run the program. In this case, you'll have to reload the
|
|
program back into the debugger and create a more unique search string by
|
|
including more instructions around the patch site in the search string.
|
|
|
|
One last thing, you cannot include instructions that reference dynamic memory
|
|
locations in the search string. These bytes are not contained in the disk
|
|
file. So keep this in mind when you are creating your search strings...
|
|
|
|
And the protection might not be included in the main executable either. If you
|
|
cannot find the search string in the main exe file, load the other program
|
|
files into the disk editor and search them as well, especially overlay files.
|
|
Fortunately for you, I've included a tool to help you do this.
|
|
|
|
|
|
|
|
|
|
CHAPTER 6 OTHER CRACKING TOOLS
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
|
|
|
|
In addtion to UNP, there are several other tools that you can utilize to make
|
|
your job easier. These tools were not designed with the cracker in mind, but
|
|
they can be adapted to serve our purposes rather than the ones which they were
|
|
written for.
|
|
|
|
UNP and other programs like it were written to remove the compression
|
|
envelopes from exectables so you would be able to scan those files with a
|
|
virus scanner among other things. If someone were to attach a virus to an exe
|
|
file and then compress it, the file for all intents and purposes would be
|
|
encrypted. Now when you downloaded that file and ran your virus scanner on it,
|
|
it might not find the virus.
|
|
|
|
But crackers found a different use for these types of programs. We use them to
|
|
remove the compression envelope so that we can find the byte strings we want
|
|
to search the files for. I'm sure most of the programmers who wrote these
|
|
programs never intended them for this purpose. There are some out there though
|
|
that were written by crackers with this exact purpose in mind.
|
|
|
|
Don't just rely on UNP as your only program to do this. No one program will be
|
|
able to remove evrything you come across. It's a good idea to start collecting
|
|
these types of programs so you have more than one alternative if you come
|
|
across a compressed file, and your favorite expander doesn't understand the
|
|
routines. Be aware though that some programs are actually encrypted and not
|
|
compressed. In this case the expander programs will prove useless.
|
|
|
|
Your only recourse in this instance is to reverse engineer the encryption
|
|
routine while the program is decrypting to memory, and modify your search
|
|
string to search for the encrypted version of the bytes. Or you could write a
|
|
tsr patcher that impliments your patch after the program is decrypted to
|
|
memory.
|
|
|
|
There is another category of programs you can adapt to your use and they work
|
|
in conjunction with the file expanders. These types of programs will scan
|
|
entire directories of files and pop up a window that displays which files are
|
|
compressed and what they are compressed with. They won't remove the
|
|
compression routines from the files themselves, but will only inform you which
|
|
files are compressed and which are not. UNP also includes a command line
|
|
switch to do this...
|
|
|
|
Now instead of blindly running UNP on several different program files to see
|
|
if they are compressed or not, you can see at a glance if you even need to run
|
|
it at all. And if you do, you'll know exactly which files to run it on. This
|
|
is another time saving type of program and there are several out there, you
|
|
just have to look for them.
|
|
|
|
Another type of program that you will find useful will scan entire
|
|
disks/directories/subdirectories of files for specific hex or ascii byte
|
|
patterns contained within those files, and this is the purpose of the second
|
|
uuencoded cracking tool contained in this guide.
|
|
|
|
One method I use to determine if a shareware program is registerable or not
|
|
before actually loading it into the debugger is to use this tool.
|
|
|
|
I usually will have it scan all the programs files and input the string REG.
|
|
This will show all files that contain the string unREGistered and REGistered.
|
|
If it returns a string that contains REGistered in a file other than the doc
|
|
files, I know the program can be made into the registered version. This is
|
|
just a quick check I do on programs that have certain features diabled to
|
|
determine if the program does contain the code for the registered version.
|
|
|
|
An added feature of this program is that after you've cracked a program and
|
|
have a byte string to search for, you can run this program in hex mode and
|
|
input your search string. Now it will search all of the programs files and
|
|
return the name of the file that contains your search string, then you can
|
|
just load that file into the disk editor and make the patch.
|
|
|
|
It will also let you know if your search string is contained in more than one
|
|
location within the file. Remember, if this is the case you'll have to reload
|
|
the program back into the debugger and create a larger search string by
|
|
including more instructions around the patch site.
|
|
|
|
The programs name is SS303 and it's very easy to use, just read the docs for
|
|
it...
|
|
|
|
These are the 'accessory' tools I use in cracking, there are several more out
|
|
there, I just don't have any use for them. And if you are dilligent, these are
|
|
all you'll really need as well.
|
|
|
|
|
|
CHAPTER 7 SOURCE CODE TO A SIMPLE BYTE PATCHER
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
|
|
As I've stated in the overview chapter, if you want to distribute your patches
|
|
you are going to have to write a patcher program. Simply releasing the patched
|
|
version of the program file is not desirable. For one thing it's illegal,
|
|
another consideration is size. Some files you patch will be 300K or more, and
|
|
this is quite a large crack to release. The patcher program included in this
|
|
guide is much much smaller, it will assemble to about 600 bytes or so,
|
|
depending on the size of your logo.
|
|
|
|
And what if you want the end user to be able to register the program in their
|
|
own name? A patched .exe or .ovr file will not allow this.
|
|
|
|
When you release a patch that you yourself wrote, you are not breaking any
|
|
laws. The program was written by you and is your intellectual property to do
|
|
with as you see fit, including making it available for public use. The person
|
|
breaking the law is the end user who will use it to illegally modify someone
|
|
elses intellectual property contrary to the licencing terms they agreed to
|
|
when they installed the program. Remember, it's not illegal to write and
|
|
distribute a crack, but it is illegal to apply a crack.
|
|
|
|
That's why all of the programs I've included in this guide are shareware
|
|
programs in the original archives as released by the authors and have not been
|
|
tampered with in any way. I'm not about to release a modified version of
|
|
someone elses copyrighted property. The only thing I am doing is supplying you
|
|
with the original archive and the information on how to modify it if you wish,
|
|
this is not illegal. If you decide to take the program and modify it that's
|
|
your problem, not mine...
|
|
|
|
This patcher routine is very simple, I wrote it about 5 years ago and it was
|
|
my very first patcher program. It is a brute force patcher in that it will not
|
|
do any error checking and blindly patch the program you specify with the byte
|
|
pattern you supply. This method has it's advantages and disavantages.
|
|
|
|
The disadvantage to this method is that seeing how the program does not
|
|
perform any error checking it will patch the file specified with the
|
|
replacement string even if it's not the correct version of the program. If the
|
|
filename is the same, the patch will be applied.
|
|
|
|
Let's say you crack a program called Ultimate Menu and the version number is
|
|
1.0, and the file you patch is called menu.exe. Now let's say a little while
|
|
later version 1.5 of the program comes out and someone who has your patch for
|
|
version 1.0 decides to run it on version 1.5 of the program.
|
|
|
|
This byte patcher will not check the new menu.exe for any changes before
|
|
making the patch, it will just patch the program in the location you specified
|
|
with the string you supplied even if the code you want to change is no longer
|
|
there. This could very well be the case if the programmer has significantly
|
|
re-written the code between versions, and what will end up happening is the
|
|
file will be corrupted and probably crash the system when it is run.
|
|
|
|
But this is also the advantage of my byte patcher. If the code to be replaced
|
|
is still in the same location in the new version, you'll not have to release a
|
|
new crack for each version of the program. Bear in mind that when I wrote this
|
|
program I was just starting out and didn't consider these possibilities. The
|
|
reason I included it in this guide was to give you an idea on how to write
|
|
your own patcher or to modify this one to suit your own purposes.
|
|
|
|
The patcher program that I use now is extremely complex and would just confuse
|
|
the hell out of you. Basically what I do is to make a backup of the original
|
|
file I am going to patch and then patch the original file. Then I run my
|
|
patcher program on the two files, it compares the differences between the
|
|
original file and the patched one and saves them to a data file. I then
|
|
assemble a patch using the data file.
|
|
|
|
What I end up with is a patch that will check the file you are running it on
|
|
to see if it is indeed the correct version before applying the patch. If it's
|
|
not, the patch won't be made. This method also allows me to make multiple
|
|
patches at different locations throughout the program. The byte patcher
|
|
included in this guide will only allow one string to be patched in one
|
|
location. But if you do a clean crack, that's all you'll usually need anyway.
|
|
|
|
Ok. here is the source code to the patcher program, I've commented as much as
|
|
I could throughout the code to make it more understandable. I also wrote it to
|
|
be generic enough so that you can re-use it over and over simply by plugging
|
|
in certain values and re-assembling it.
|
|
|
|
NOTE: the patch offsets are not the segment:offset adresses of the code as it
|
|
resides in memory, but the offset from the beginning of the disk file.
|
|
|
|
.model small
|
|
.code
|
|
ORG 100H
|
|
start: JMP begin
|
|
|
|
;******************************************************************************
|
|
; these are all the variables you set to crack a file,
|
|
; simply change the values and then assemble the program
|
|
;******************************************************************************
|
|
|
|
msb EQU 0000H ;the first part of the patch offset
|
|
lsb EQU 055AH ;the second part of the patch offset
|
|
cnt EQU 3H ;number of bytes in your patch
|
|
patch_data DB 'EB2E90',0 ;the byte string to be written
|
|
file_name DB 'go.pdm',0 ;the name of the file to be patched
|
|
|
|
logo DB 'Cracked by Uncle Joe',0AH,0DH
|
|
DB ' -=W.A.S.P. 92=- ',0AH,0DH
|
|
|
|
error1 DB 'FILE NOT FOUND',0AH,0DH
|
|
DB 'Make sure you have GO_CRACK.COM in the same',0AH,0DH
|
|
DB 'directory as GO.PDM',0AH,0DH
|
|
DB '$'
|
|
|
|
error2 DB 'A fatal error has occured',0AH,0DH
|
|
DB 'the crack was not applied',0AH,0DH
|
|
DB '$'
|
|
|
|
error3 DB 'GO.PDM has the read only attribute set',0AH,0DH
|
|
DB 'reset it before attempting to make the patch',0AH,0DH
|
|
DB '$'
|
|
|
|
handle DW 0
|
|
|
|
;******************************************************************************
|
|
; this procedure opens the file to be cracked
|
|
;******************************************************************************
|
|
|
|
open_it PROC near
|
|
MOV DX,offset file_name ;setup to open file to be
|
|
MOV AX,3D02H ;cracked
|
|
INT 21H
|
|
JNC done ;if successful, continue
|
|
|
|
CMP AX,05H
|
|
JZ read_only
|
|
MOV AH,09H ;else display error message
|
|
MOV DX,offset error1 ;and exit
|
|
INT 21H
|
|
JMP exit
|
|
read_only: MOV AH,09H
|
|
MOV DX,offset error3
|
|
INT 21H
|
|
JMP exit
|
|
|
|
done: MOV handle,AX ;store the file handle for
|
|
RET ;use later and return
|
|
open_it ENDP
|
|
|
|
;******************************************************************************
|
|
; this procedure sets the file pointer to the patch location
|
|
;******************************************************************************
|
|
|
|
move_it PROC near
|
|
MOV AH,42H ;setup to move the file
|
|
MOV AL,00H ;pointer to the patch site
|
|
MOV BX,handle ;load the file handle
|
|
MOV CX,msb ;the first part of offset
|
|
MOV DX,lsb ;and the second part
|
|
INT 21H ;move the pointer
|
|
JNC ok ;if successful, continue
|
|
|
|
MOV AH,09H
|
|
MOV DX,offset error2
|
|
INT 21H ;else print error message and
|
|
JMP exit ;exit
|
|
ok: RET
|
|
move_it ENDP
|
|
|
|
;******************************************************************************
|
|
; this procedure writes the crack to the file and closes it
|
|
;******************************************************************************
|
|
|
|
patch_it PROC near
|
|
MOV AH,40H ;setup to write the crack
|
|
MOV BX,handle ;load file handle
|
|
MOV CX,cnt ;load number of bytes to write
|
|
MOV DX,offset patch_data ;point DX to patch data
|
|
INT 21H ;make the patch
|
|
|
|
JNC close_it ;if successful, contintue
|
|
MOV AH,3EH
|
|
INT 21H
|
|
MOV AH,09H ;if not then something
|
|
MOV DX,offset error2 ;is wrong, disk may be write
|
|
INT 21H ;protected. If so, print error
|
|
JMP exit ;message and exit
|
|
|
|
close_it: MOV AH,3EH ;crack was successful
|
|
INT 21H ;close file and return
|
|
RET
|
|
patch_it ENDP
|
|
|
|
;******************************************************************************
|
|
; the main program
|
|
;******************************************************************************
|
|
|
|
begin PROC near
|
|
CALL open_it ;open file to be patched
|
|
CALL move_it ;move pointer to patch site
|
|
CALL patch_it ;make the patch and close file
|
|
MOV AH,09H
|
|
MOV DX,offset logo ;display logo
|
|
INT 21H
|
|
|
|
exit: MOV AX,4C00H ;and exit
|
|
INT 21H
|
|
begin ENDP
|
|
|
|
END START
|
|
|
|
|
|
|
|
|
|
CHAPTER 8 CONCLUSION
|
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
|
|
Hopefully this guide has been useful in helping you understand the cracking
|
|
process. It is by no means an all inclusive guide, the main goal I had in mind
|
|
when I wrote it was to give beginners a push without confusing the hell out of
|
|
them.
|
|
|
|
It is not feasable to try and include all of the tricks of the trade that a
|
|
beginner might find useful into one single guide, nor would I want to do this.
|
|
For one thing, this guide would be ten times the size it is now, and even then
|
|
it would not be an encyclopedia of what to do for every situation. If your
|
|
serious enough about cracking, you will discover enough tricks and develop
|
|
your own methods as you progress. And you have to be creative! What works in
|
|
one situation may not work again in a similar one.
|
|
|
|
Instead, I tried to give you a general idea on how a programs code might
|
|
operate and what to look for. A successful cracker is not someone who
|
|
memorizes a specific set of actions to perform on a specific piece of code. A
|
|
successful cracker is someone who understands the flow of the code, and how to
|
|
adapt his methods to successfuly re-write the programs code to behave as he
|
|
wishes and not as the programmer intended it to. There are no set rules for
|
|
this, the very nature of the PC won't allow it.
|
|
|
|
If you have any questions about cracking or are stumped by something, drop me
|
|
a note at fishpaw@helix.xiii.com, I'll be glad to give any advice I can. Or if
|
|
you simply just wish to discuss cracking techniques or anything of that
|
|
nature.
|
|
|
|
NOTE: Do NOT mail me and ask me to crack programs for you! I'm not interested
|
|
in cracking for the masses. If you need something cracked, learn how to crack
|
|
it yourself. If you are unwilling to learn how, then register it.
|
|
|
|
|