|
|
|
|
Original file converted to Html format by:- +Sandman | |
|
This 'manual' was written quite some time
ago and as such, uses the DOS DEBUG as it's choice of debugger since Softice
was not then available. However, the techniques and methods described within
the 'manual' are still important for all newbies and makes a good foundation
for future *cracks*. The Sandman.
INTRODUCTION
Welcome to the wonderful world of cracking. What is cracking? If you don't know and you're reading this, ask yourself why? Anyway, cracking is the art of removing or disabling copy protected coding from programs. Why do this? In recent years, software companies have been fighting to keep copy protection in their software to avoid their work to be illegally copied. Users feel that such copy protection is ridiculous in that it violate their own rights to make backups of their sometimes expensive investments.
Whichever side you may favor, this manual will go into some detail on removing copy protection from programs. If you feel offended by this, then I would suggest you stop here.
Please note, I do not endorse cracking for the illegal copying of software. Please take into consideration the hard work and effort of many programmers to make the software.
Illegal copying would only increase prices
on software for all people. Use this manual with discretion as I place
into your trust and judgement with the following knowledge.
WHAT YOU WILL NEED
Like all programming, cracking is the debugging stage of software development. It is the most tedious and hectic part of programming as you shall see. However, unlike software development, you are given no source code, only the machine level code commonly called machine language. Cracking demands patience. No patience, no cracking.
Before we begin, you will need certain tools. These include:
- A decent computer. By this, I mean
at minimum a 286
computer with 2 or more megs of RAM. A
386 is the
ideal since it can load a debugger into
usable memory.
- A source level debugger (eg. Turbo Debugger)
- A low level debugger (eg. DEBUG)
- An assembler system (eg. MASM, LINK,
EXE2BIN)
- A hex dumping program (eg. Norton Utilities)
The source level debugger is what you will try to be using most of the time. It provides many features that are a convenience to the cracker, such as interrupt redirection.
Become comfortable with its features. However, in some instances, the source level debugger may not be suitable for cracking huge games since the debugger itself may take up too much memory. In such a case, a low level debugger must be used since their memory usage may be considered negligible. This manual will focus on its use.
The assembler package will be used in the creation of the famed loaders, which provide the cracker with dynamic memory alterations without changing the original program.
CRASH COURSE IN ASSEMBLY LANGUAGE
If you are already well familiar with the assembly language, you may wish to skip this section. Cracking demands the knowledge of assembly language. If you wish to become a "serious" cracker, you might like to read up more about this fascinating language. This section will only give you enough info for intermediate level cracking.
At this point, you should familiarize yourself with DEBUG and its commands as we will be using them shortly.
Registers
One of the neato things that you will be fooling around most often with are called the registers. Registers are like variables (such as in BASIC) that are located within the CPU itself. These registers may hold a positive integer from 0 to 255 or from 0 to 65535. They can also hold negative integers from -128 to 127 or from -32768 to 32767. The registers are given names as follows:
AX => accumulator - this register is most commonly used for mathematical or I/O operations
BX => base - this register is used commonly as a base or a pointer register (we'll talk more about this later)
CX => count - used commonly for counting instructions such as loops
DX => displacement - much like the base register
The registers stated above are considered general purpose registers, since they can basically be used to store whatever the user wants.
These general purpose registers can also be "split" in half into its higher and lower order components. Instead of having one register AX, you can have two registers, AH and AL. Note however that while you have a range of 0 to FFFFh for AX, you will now have a range of 0 to FF for AH and AL. You cannot change these directly in debug, but be aware that programs will use it. If AX contains 0A4Ch, then AH will contain 0Ah and AL will contain 4Ch.
The following are called the segment registers:
CS => code segment - the block of memory where the code (instructions are located)
DS => data segment - the block of memory where data can be accessed. In block move operations in which huge blocks of memory are moved, this is commonly the segment in which the CPU reads from.
ES => extra segment - also another data segment. In block move operations in which huge blocks of memory are moved, this is commonly the segment in which the CPU writes to.
SS => stack segment - this is the block of memory in which the CPU uses to store return addresses from subroutines. (more on this later)
In introductory level of cracking, we don't mess around with these registers. Later, we will see how we can use these to trick a program into thinking other things, but that's later. There are other registers that we use to see what the program is doing. These registers can also be change in debug. Included are the following:
SI => source index - this register is used
in conjunction with block move instructions.
This is a pointer within a segment (usually
DS) that is read from by the CPU.
DI => destination index - this register is also used in conjunction with block move instructions. This is a pointer within a segment (usually ES) that is written to by the CPU.
BP => base pointer - a pointer used commonly with the stack segment
SP => stack pointer - another pointer used commonly with the stack segment (this one, you don't touch)
By now, you may probably be confused about this segment/pointer bit. Here is an analogy that my straighten things out. Pretend you are in kindergarden learning to read. There are four black boards surrounding the room. These black boards are like SEGMENTS. Let's pretend the front blackboard is the code segment (CS). The teacher has written some instructions on pronunciation rules. This is what the students refer to when they try to pronounce words. In a program, this is what the CPU refers to when it follows directions. Okay, now the teacher has gone to the blackboard on the left of the classroom. We will call this board the data segment (DS). The teacher has also written a set of words on the board. Then she uses a wooden stick or a POINTER to point to a word. Let's pretend this stick is the source index (SI). She points to the word "their". Now, the students look at the front blackboard (CS) to see how to pronounce the word and they say "their". Now, the instructor wants the students to learn how to write.
She points the stick to the word "apple".
The students pronounce the word. Then she goes to the blackboard on the
right. We shall call this one the extra segment (ES).
She then uses her finger as a different
POINTER and points to a location on the board where Mary Jane will write
"apple".
That's basically what segments and pointers are. Segments are the blackboards and pointers are the teacher's wooden stick or finger.
One last important register is the flags
register. These registers control how certain instruction work, such as
the conditional jumps (in BASIC, they are like IF-THEN's).
They are stored as bits (0's or 1's) in
the flags register. We will most often use:
zero => ZR/NZ (zero/not zero) - tells you whether an instruction (such as subtraction) yielded a zero as an answer
sign => NG/PL (negative/positive) - tells you whether an instruction yielded a positive or negative number
carry => CY/NC (carry/no carry) - tells you whether an instruction needed to carry a bit (like in addition, you carry a number over to the next digit). Various system (BIOS) functions use this flag to denote an error.
direction => DN/UP (decrement/increment)
- tells a block instruction to either move forward or backwards in reads
and writes
The Instructions
MOV - move
----------
Now we get to the actual instructions or commands that the CPU will use. The first instruction you will see most often is the move instruction. Its form is MOV {destination},{source}. Let's try programming now.
Run DEGUG.EXE. Now, type in "A {enter}". You will see a bunch of number to the left. You can think of these as line numbers. Now type in "MOV AX,7A7A {enter}". Then type "MOV DX,AX" and so on until your program looks similar to the one below: (type "U 100" to see)
xxxx:0100 B8A77A MOV
AX,7AA7
xxxx:0103 89C2 MOV DX,AX
xxxx:0105 B90000 MOV
CX,0000
xxxx:0108 88D1 MOV CL,DL
xxxx:010A 890E0005 MOV
[0500],CX
xxxx:010E 8B160005 MOV
DX,[0500]
xxxx:0112 BB0200 MOV
BX,0002
xxxx:0115 26A30005 MOV
ES:[0500],AX
Press enter again until you see the "-"
prompt again. You are ready to run your first program. Type "R {enter}"
and note the values of the general purpose registers. Then type in "T {enter}".
Debug will automatically display the registers after the execution of the
instruction. What is in the AX register? It should be 7AA7h. Now, "T" again.
What
is in the DX register? It should also
be 7AA7h. Trace again using "T" and note that CX should be 0 if it was
not already. Trace again and note what is in the CX register. It should
be 00A7h. Now trace another step. What is this instruction doing? It is
now moving the contents of CX into memory location 500h in the data segment
(DS). Dump the memory by typing in "D 500". The first two two-digit numbers
should be the same as in the CX register. But wait a minute you say. They
are not the same. They are backwards. Instead of 00A7h, it is A700h. This
is important. The CPU stores 16 bit numbers in memory backwards to allow
for faster access. For 8 bit numbers, it is the same. Now, continue tracing.
This instruction is moving the memory contents of address 500h into the
DX register. DX should be 00A7h, the same as CX regardless of how it looked
in memory. The next trace should be nothing new. The next trace again moves
the contents of a register into memory. But notice it is using the BX register
as a displacement. That means it adds the contents of BX and 500h to get
the address, which turns out to be 502h. But also not the "ES:" in front
of the address.
This additional statement tells the CPU to use the extra segment (ES) rather than the data segment (DS which is the default). Now dump address 502h by entering "D ES:502" and you should see A77Ah, which is backwards from 7AA7h.
CMP/J? - compare/conditional jump
Another instruction you will see quite often is the CMP or compare instruction. This instruction compares the two "variables" and changes the flags register accordingly. The source and destination operands are the same as those for the move instruction.
Let's consider an example in which the AX register holds 21 and the BX register holds 22. Then "CMP AX,BX" is performed. The compare instruction is like a subtraction instruction, but it doesn't change the contents of the AX register. So, when 22 is subtracted from 21, the answer will be -1, but we will never see the answer, only the flags which have resulted from the operation. Number 21 is less than 22, so the carry flag and the sign flag should be set. Just remember that when the carry flag is set, the first number is less than the second number. The same is true for the sign flag. Why have two flags if they tell us the same thing?
This is more complicated and you should not concern yourself with it. It requires knowledge of hexadecimal arithmetic, the denotation of signed and unsigned integers.
So, now that we have done the compare instruction,
there will most likely be a conditional jump instruction after. If we wanted
to jump if AX is less than BX (which it is), then there would be an instruction
like "JB 200". This instruction says Jump if Below to instruction 200h.
What about if we wanted to jump if AX is greater than BX. Then we might
have "JA 200". This is read Jump if Above to instruction 200. What about
AX equal to BX. We would then have "JZ 200" or "JE 200". (Please note that
the previous
instructions are synonymous.) This is
read Jump if Equal to instruction 200h. Here are the jumps you will most
likely encounter:
Mnemonic Flag(s) Checked Description
JB/JNAE CF=1 Jump if
below/not above or equal (unsigned)
JAE/JNB CF=0 Jump if
above or equal/not above (unsigned)
JBE/JNA CF=1 or ZF=1
Jump if below or equal/not above (unsigned)
JE/JZ ZF=1 Jump if equal/zero
JNE/JNZ ZF=0 Jump if
not equal/not zero
JL/JNGE SF not equal
Jump if less/not greater or to OF equal (signed)
JGE/JNL SF=OF Jump if
greater or equal/not less (signed)
JLE/JNG ZF=1 or SF Jump
is less or equal/not equal OF greater (signed)
JG/JNLE ZF=0 or SF=OFJump
if greater/not less or equal (signed)
JS SF=1 Jump if sign
JNS SF=0 Jump if no
sign
JC CF=1 Jump if carry
JNC CF=0 Jump if no
carry
JO OF=1 Jump if overflow
JNO OF=0 Jump if not
overflow
JP/JPE PF=1 Jump if
parity/parity even
JNP/JPO PF=0 Jump if
no parity/parity odd
These are all the possible combinations of conditional jumps that you will encounter. I realize that we have not discussed some of the flags such as overflow or parity, but be aware that they exist and programs sometimes use them.
JMP - jump
This instruction does what it suggests. It jumps too different sections of code. Several forms of the jump instruction include:
2E0B:0208 EBF6 JMP 0200
2E0B:020A 3EFF24 JMP DWORD PTR DS:[SI]
The first instruction jumps to an address
within the segment. The latter instruction jumps to an address pointed
to by ds:si. The DWORD says that this will be a far jump, a jump to a different
segment (a different blackboard). So, if the double word that is pointed
to by ds:si contains 1000:0040h, then, the instruction will jump to 1000:0040h
whereas the
previous jump instruction will jump within
the current segment (or blackboard).
CALL - procedural transfer
This instruction is the baby that you will
be carefully watching out for most often. This instruction calls another
procedure and upon it's completion, will return to calling
address. For example, consider the following
block of code:
2E0B:1002 E8BB46 CALL 56C0
2E0B:1005 7209 JB 1010
2E0B:1007 0C00 OR AL,00
The first line calls another procedure at "line number" 56C0h. Upon its completion, the instruction pointer will point to the second line. Note that there is a "JC" instruction. Remember that programs often use the carry flag to signal errors. If the call instruction called a copy protection instruction and you entered a wrong code or something, it may return with the carry flag set. The next instruction would then jump if there was an error to an exiting procedure. Note, this is a near call. A program can also have far calls just like jumps.
INT - generate
an interrupt
This instruction is much like the call instruction. It also transfers control to another procedure. However, the number after the INT instruction does not point to an address. Instead, it is a number pointing to an address that is located in something called an interrupt vector. You will commonly see "INT 10", "INT 21", "INT 13". Just know (for now) that they are like calls to procedures.
LODSB/LODSW/STOSB/STOSW
- load/store a byte/word
These instructions either load in or store a byte or a word to or from memory. The DS:SI register pair points to the source data. These are the registers the CPU will use when reading from memory using the LODS instruction. The AX/AL register will hold the number to either read from or write to the memory. So, if DS:SI points to a byte which is maybe 60, then a "LODSB" instruction will load in the number 60 into the AL register. A LODSB or STOSB will use the AL register while the LODSW or STOSW will use the AX register.
The STOS writes whatever is in the AX/AL register to the memory pointed to by ES:DI. So, if ES:DI points to 100:102h and if AL held 50, then the byte at 100:102h will hold 50. After the instruction is finished, the CPU will either increment or decrement SI or DI according to the status of the direction flag. So, if SI was 100h and a "LODSW" instruction was performed with a cleared direction flag (forward), the SI will now point to 102h.
MOVSB/MOVSW - copies a byte/word from source to destination
This instruction gets a byte or a word from the data pointed to by DS:SI and copies it to the data pointed to by the ES:DI address. When the instruction is finished, SI and DI will be incremented or decremented accordingly with the status of the direction flag. So, if DS:SI pointed to a byte with the number 30, a "MOVSB" instruction would copy into the byte pointed to by ES:DI the number 30.
REP - repeat
The REP instruction in front of a MOVS/LODS/STOS
would cause the VS/LODS/STOS instruction to be repeated for a number of
times specified in the CX register. So, if CX
contained 5, then "REP STOSB" would store
whatever was in the AL register into the byte pointed to by ES:DI five
times, increasing DI each time.
LOOP - looping
The LOOP instruction repeats a block of
instructions for a certain number of times. This number will be held in
the CX register. Each time we reach this instruction, the CPU
will decrement the CX register and jump
to a specified instruction until CX becomes zero. This instruction looks
like "LOOP 1A00" where the number indicates the instruction address to
loop to.
Arithmetic Operators
Arithmetic instructions allow you to perform
various arithmetic function of data. "ADD" and "SUB" work the same way
as "MOV" instructions do in that it subtracts whatever is
in the source register from the destination
register and stores it in the destination register. The "MUL" and "DIV"
instructions are a bit more complicated and they are not used as intensively
as the "ADD" or "SUB" since they are slow, so we will not talk about them.
There are also a multitude of other instructions that you should familiarize
yourself with if you are thinking of becoming a serious cracker. The instructions
given above are
only the BARE minimum that you need. There
is no way around learning assembly for better cracking.
Interrupt 13h (the assembly mnemonic is
INT 13) was commonly used to handle such copy protections. It is now very
rare to encounter the once famed INT 13h copy protection method nowadays
since it was quite easy to defeat. Any professional
commercial software will often use their
own custom based disk I/O routines. This involves intimate access to I/O
ports using IN and OUT instructions. This is beyond the
scope of the first release of this manual.
However, if you are lucky, the I/O functions might be called from a "CALL"
instruction in which case you may defeat the protection
without much difficulty. Another disk
based scheme used to denote legality of software is used during the installation
process of the software. With certain programs, when you install it, it
copies the files into the hard drive. But it also sets a specific sector
in the hard drive so that the program can recognize it. This is also similar
to diskette copy protections, but can be defeated in much the same way.
Thank goodness that disk based copy protections are almost completely out
of the software industry. However, a
sometimes more difficult copy protection
scheme has arisen that may sometimes prove to be even more difficult to
crack.
These schemes are commonly known as the doc checks in which the user must have a copy of the manual to bypass the protection. With programs compiled as true assembly (you can call then "normal" programs), these protections are not too bad to trace through and crack. With programs that run scripts (such as Sierra games), this can he a real chore however. Why? It is because it is like running a program within a program. You just have to be very very patient in this case, carefully tracing through the instructions.
As if these copy protection schemes weren't enough, software companies have also added trace inhibition schemes to their code. What does this mean? This means that you will have a hell of a time trying to trace through code. However, if you know how these things work, it should not be too much of a problem.
Run-time compression/decompression and
encryption/decryption of files also make changes to the program difficult.
In this case, the loader sure comes in handy. Also, when the data within
the file changes due to overlays, loaders are also good to use.
DISK BASED COPY PROTECTIONS
Since disk based copy protection schemes are rarely used, we will not go into great depth in its discussion.
INT 13h
I have previously mentioned that INT 13h copy protection schemes are hardly ever used anymore. Nevertheless, it would be good practice for the beginner to learn how to defeat the code. You will most likely see INT 13h used with function 2, read sector. This means that:
AH => will contain the number 2 (function
2)
AL => the number of sectors to read in.
This is commonly only 1 since you just want to check a few sectors for
disk validity.
CH => will contain the cylinder number
CL => will contain the sector number
DH => will contain the head number
DL => will contain the drive number 00h
- 7Fh for floppies 80h - FFh for fixed disks
ES:BX => will point to the address into
which the data read from the disk will be written to Upon the return for
this interrupt, if the carry flag is set, that means that the program could
not read the sector, and therefore the disk is valid. If the carry flag
is clear, that meant that INT 13h could read the sector properly and so
the disk would be bad in the eyes of the program, thinking it was a copied
disk.
Okay, now that we know to look for INT
13h in the program code, we can begin tracing. First, we must know the
difference between debug's "T" and "P". "T" is the trace
instruction, which tells it to follow
instructions step by step. That also means that in LOOP or REP instruction,
the trace will patiently go through the loop until finished.
Also, during CALL instructions, trace will go into the call and execute the instructions pointed to by the call instruction. The "P" command is similar to the "T" but with the difference in that it traces over instructions. That means that if it encounter a LOOP or REP, it will quickly finish up the loop and point to the next instruction. With a CALL, the "P" (proceed) will not go into the subroutine. Instead, it will just execute the procedure, then point to the next instruction.
Okay, before you start tracing for hours
through a program, you must first notice when and where the copy protection
appears. Run the program in DOS first and make
careful note of when things happen. You
might see an intro screen, then the music pops up, then the menu comes
out.
Notice this so you will know where you are in the program. Once you have done that, you can begin debugging the program. Whenever you start out with a program, you use "P" to trace through the program. Be patient as this might take a while. While you are tracing, watch out for CALLs and INTerrupts. When you are just about to execute the step, try to remember the segment and offset of the instruction. The segment is the number to the left of the colon while the offset is the number to the right. As you continue tracing through the program, you will find that the screen might blank and display the intro screen or something like that.
This is a good sign and it tells you that you are headed in the right direction. Start slowing down when you feel that you are near to the copy protection.
Situation 1 - Exit from copy protected CALL
Oops, you have traced over a call that accessed drive A. Unfortunately, you also exited the program. That's good. You have just narrowed down the location of the copy protection code. Now I hope you remembered the address of that CALL. If not, you gotta start all over to find it.
Anyway, restart the program now. Now Go
to that instruction by "G {segment:address}".
Did something go wrong? Did the computer
freeze or something? It is most likely that this is an overlay or encrypted
code or something that caused the code at that location to change. In this
case, you will have to remember the addresses of various instructions along
the way. Instructions that you want to take note of are far calls (if you
remember, calls with a segment:offset address as their operand). You don't
have to do this for every call. As you crack more and more, you will get
the hang of which instructions to keep track of.
Okay, let's assume you have gotten back into the location of the code again. It is a CALL instruction that will access the disk drive. At this point, try skipping the CALL instruction. To do this, type in "RIP {enter}". Then type in the address of the next instruction. Then execute the do or die instruction, "G". If the program runs fine without asking for the copy protection, congratulations! You have cracked the program.
If the program freezes or does something weird, restart the program and trace back to the suspected copy protected location. Now use the "T" command once and start using "P" again. Remember to write down the address of that CALL instruction you just traced into so you can come back to it quickly. As you keep tracing, using the above procedures, pretend you eventually come up to an INT 13h instruction.
See what it does by tracing over it. Make
sure you have a disk in drive A too. If there was no error, force an error
by turning on the carry flag and proceeding. With INT 13h
copy protections, this should be sufficient
to crack the program.
Situation 2 - Return from copy protected CALL
Okay, the CALL that you just traced over accessed the disk drive, but it didn't kick you out. Keep on proceeding and this point. If there is an instruction that causes you to jump because of a carry flag, try fooling around with this carry flag and see how the program reacts. INT 13h copy protections are usually simple enough for you to just change the carry flag to allow the program to bypass the copy protection.
Access to the Hard Drive
The cracking for installation software
is also the same as cracking for the INT 13h. You just keep tracing until
you see some disk activity. At that point, you try messing
around with some of the conditional jumps
to see what happens. If you have the original program, you should run it
also to see the differences between the valid and invalid
copies.
DOC CHECK COPY PROTECTIONS
Okay, we have just quickly scanned over
disk based copy protections because they are rarely used nowadays. Doc
checks will be discussed in greater detail for the rest of
this manual. Unlike the disk based protections,
which are based on hardware identification, doc checks are based on software
identification. Therefore, the only information that will indicate that
a copy protection is happening is the screen,
unlike the whirr of the disk drive. The
moral, watch the screen. Because this copy protection is software based,
it will be more of a challenge to trace, but of course, that is the "fun"
part of cracking.
The Basics
Make sure you have the COMPLETE version of the program you are about to crack. When you do, run the program in DOS. While the program is loading, take note of exactly what goes on with the screens, sounds, etc. Here is what you might want to note:
1) What comes up first? Is it a standard text output that asks you for the type of graphics adaptor you have, the number of joysticks, the sound card?
2) When does the intro screen come up? Is it after the music starts? After the copyright notice? After the text prompt for the graphics mode you will be operating in?
3) What happens now? An animated sequence that brings you through the beginning plot of a game? If so, can you press a key and escape from it?
4) Now what? Is there a main menu? When you start the game by selecting the "START GAME" option from the menu, does the copy protection come up immediately?
5) If it doesn't come up immediately, when does it come up?
6) Does the copy protection only appear when you are playing the game, or does it come up also when you select "CHANGE OPTIONS" from the main menu?
Obviously, these questions are merely prompts
for you to follow. Use your own mind in discovering what to take note of.
There are no set rules for cracking. It is a puzzle that you must use your
mind on. Okay, once you have run the program, go into your
debugger (in our case, DEBUG) and load
up the program. One tip to use when you first start out programming is
to use the "P" command to trace through code. As you become a more advanced
cracker, you might start seeing patterns in coding.
These patterns are characteristic of high level programming languages (Pascal, C, etc.) and are usually the initialization code for the rest of the program. Use "P" for each instruction, one at a time. Be patient as this might take a while. Okay, you have been tracing for some time now and finally, you notice something happen. The screen might have blanked or maybe a message prompting you to enter the graphics mode may have popped up. Was this what you have noted before? It should be and you can assure yourself that you are headed in the right direction. As you keep tracing programs, you notice that CALLs usually do something significant. A CALL might clear the screen or sound some music. When it does something rad like this, write down its address as the segment:offset pair. The segment is the number to the left of the colon while the offset is the number to the right of the colon. Don't be a dork and set a breakpoint there. Write it down on paper or something. We will see later on why breakpoints fail miserably in the cool wares.
Why take note of these instructions? As you trace deeper and deeper into programs, the coding often loads up overlays or maybe decompresses code to the memory location that you have just traced over. Therefore, if you set a breakpoint there, or execute a "G" instruction to that address, you will mess up the program and cause your computer to freeze. We will see why when we examine how breakpoints and single stepping works. Also, while you are tracing using "P", mentally remember the addresses of the CALLs. That way, if you trace over a call that brought you immediately to the copy protection, you won't have to retrace the code again. You don't have to write down all of the addresses, of course, just remember one at a time and write them down if they do anything significant.
Code Guards Through Keyword Entry
Okay, you know that the copy protection
is one in which the program waits for you to type in a keyword that you
have to look up in the manual or something. Here are then
following steps you should take.
Situation 1 - Return from a copy protected CALL
When a copy protection coding reveals itself
on the screen, you can have a situation in which you are returned to the
debugger, waiting for the next instruction to be executed. Now, suppose
that the CALL asked you to enter a code. You entered an incorrect code
and were returned to the debugger, but you have not exited the program.
Make sure
that you have previously recorded the
address of this CALL.
Now, you can do two things, (1) you can try skipping over the CALL, (2) you can trace on further. As you become more experienced, you will be able to better decide. As one with experience, however, I can say that 90% of the time, you will have to trace further on, but hey, you might get lucky. For now, let's say you are lazy and decide that you want to skip over the call to see what happens. To do this, you must restart the program. Then trace your way back to the CALL where the copy protection was located. Use "G {segment:offset}" to do this. If, for some reason, the computer freezes when you do this, you will have to use "G" followed by the addresses of the CALLs that you have noted down to be significant. If that doesn't work, resort to retracing the code over again. As you become more experienced, you will find that you rarely have to retrace the entire code since you can "feel" what is going on. Okay, now that you are at the location of the CALL, this is the time to skip over the instruction. To do this, enter "RIP" and then the address of the next instruction's address. Now enter the "G" command and see what happens. If the program runs just fine, you've cracked the program. If the program kicks you out or crashes, you have to do some more tracing.
Okay, so you've decided to continue tracing
from the point of the copy protection. There are usually a bunch of CMP
and J? CMPS? instructions after the call. This point on is the difficulty
of cracking for a beginner since you don't know what the hell s going on.
All those compares and jumps don't mean shit to you are you are about to
pass out in
frustration. Don't distress, here are
a few tips I can give you. If these don't work, you gotta find out your
own solutions to the problem.
Okay, in all probability, the CALL that you just traced over was acting as a read string procedure (like BASIC's INPUT). That means somewhere in the computer's memory, there lies the code that you typed in and the code that you were supposed to have typed in. What this would mean is that the code after the CALL will do some sort of string comparison. Look out for these. It might be hidden inside another CALL if you're lucky. In such a case, does the program kick you out? If it does, you have to trace into the call using "T" to see what is going on. Okay, the string comparison will most likely take the form of some kind of loop. Maybe "REP CMPSB" or "LOOP". In the case of the REP CMPSB, there might be a JZ/JNZ or JCXZ/JECXZ that follows it. When strings match, the CX register will be zero. If CX is not zero, the strings are not the same and the conditional jump will probably jump to an exit routine. All you have to do is to change the status of the zero flag. Then, try out the "G" instruction. If it still didn't work, start over and do some more tracing. If the string compare is not of the REP form, there will be some kind of loop that will check between two memory locations. In such a case, you will just have to become accustomed to realizing that the code is a string compare. There is no standard code for this. If you know you have entered a wrong code, trace through the loop and see where in the loop you are thrown out of the loop.
At this point, you can go back to it, change some flags to make sure you stay in the loop. When you exit through a different location, you have probably bypassed the code and now, you can enter "G" to see what happens.
Situation 2 - Exit from a copy protected CALL
When a copy protection coding reveals itself
on the screen, you can have a situation in which you are not returned to
the debugger, instead, causing you to exit the program. In this case, you
have to restart the program and trace into the CALL using "T". After that,
you can start using "P" again to uncover the location of the code. You
will most likely encounter a condition that will resemble situation 1.
Follow its instructions.
Shortcuts For Keyword Entry Protections
With keyword entry systems, you might be lucky to have the codes stuck somewhere into file in its uncompressed/unencrypted form. This means that you can "see" the keywords in its ASCII format. This case is cool because you won't have to do any tracing to crack the program. All you have to do is to dump the contents of the files to find something that looks like a keyword. (Always backup the file that you are about to alter.) When you have found such a file and the location of the codes, all you have to do now is to change the codes to values that you know. For example, one code might call for you to enter "PIRATE". It's a bitch if you don't know the code. But if you change the code to your name or something else you will never forget ("CYBORG"), then you'd be set.
However, in most instances, you can't simple just type over the old code with your new code. In high level languages, these codes are stored as strings. In 'C', strings are stored in their ASCII equivalent. They are then terminated with a NULL character (this is a 0). In Pascal, the lengths of the strings are first stored in the first position. Then, the ASCII is stored.
NULL Terminated Strings
So, if you see zeros after the codes, this is a NULL terminated string. Now, start at the beginning of the string and enter your code. Then, enter the '0'. Make sure your string is less than the original string since 'C' refers to these strings also with pointers.
Pre-Length Indentifier
If you see numbers before strings, enter
your own code. Then change the length of the code appropriately. Make sure
you do not exceed the length of the original string.
Code Guards Through Pointed Icons
We have a case where we do not type in keywords. Rather, we must use a pointer device such as the cursor keys on the keyboard, the mouse, or joystick. These protections are a bit more complicated since there are no strings to compare against. Rather, the input will be a number stored in memory or a register. This is what makes this copy protection more difficult to crack. We have to hunt through code to find out which compare instruction is the key. What you have to do is to find the general location of the copy protection code as before. Then, instead of typing in the keyword, you select the icon. Like before, you must step slowly through the code and go until the program JUST STOPS asking you for the code. For example:
2E0B:0000 E8740E CALL 0E77
2E0B:0003 38D0 CMP AL,DL
2E0B:0005 7569 JNZ 0070
2E0B:0007 CB RETF
You might decide to trace over the call at address xxxx:0000. But then, you see that the screen displayed the icons and you got to select the code. Then, the procedure does some disk activity and you return to address xxxx:0003. If you see something happen after you have just finished entering the code or if it is slow in returning you to debug, then, some code must have been performed before you returned. In this case, you must trace into the CALL to see what has happened. If not, there is still a small probability that there were some instructions that formatted the code you entered and saved it to a memory location. (We'll talk about multiple doc checks later.)
Realize that most of the programs that
you will be cracking have been programed by C or some other high level
language. These languages often use the stack (SS:SP) to
pass parameters (variables) or to create
local variables for a procedure's use. Most likely, you will see compares
to data contained within the stack such as "CMP AX,WORD PTR [BP+10]" or
"MOV DX,WORD PTR [BP+10]". This is what you hope
to find, although not always the case.
If you do see some access via the stack using the BP register as a pointer,
you may have something there. Then, all you would have to do is to mess
around the flags register (most likely, JZ/JE will be used) at the compare
instruction.
Multiple Doc Checks
There are some wares that invoke multiple
doc checks, doc checks that pop up either systematically or randomly. In
addition, there could also be two types of this protection.
The doc check could be a similar type
(eg. typing the code found on page...) or they could be different (eg.
typing in the code on page... then select the correct icon), although the
latter is more rarely used due to its extensive memory usage.
Situation 1 - Similar doc checks
Cracking multiple doc checks that are similar
is just like cracking with just one doc check. The procedure to trace is
still the same. Keep Proceeding until you come up
to the CALL that contains the copy protection.
Just use the sequences mentioned above. When you are absolutely positive
that the call contains the copy protection (skip the CALL and see what
happens; if the protection has been bypassed but appears at other times,
you got something), here is what you do.
1) Note what type of CALL it was. Near if the operand (number after the CALL) was a four digit number or far if the operand contained the segment:offset pair.
2) Trace INTO the call.
3) At the first instruction, note the address inside the CALL.
4) Then, type in "A" then the address of that very first instruction.
5) If there was a near call performed, now type in "RETN", otherwise, type in "RETF".
6) Now run the program ("G") and see what happens.
If this call was definitely the copy protection, you should have bypassed the copy protection completely. Otherwise, you might have a case like situation 2.
Situation 2 - Different doc check types
Again, cracking multiple doc checks are
like cracking single doc checks. You follow the same procedures until you
come up to a copy protected location. Then, you would trace into the code
as explained in situation 1 just to make sure that the code is not called
up again. Different doc checks are a bitch to do because you have to manually
keep tracing until you find each one to effectively rid yourself of the
copy protection. There is not sure way of getting rid of all the doc checks
any other way. But luckily, there are very
few wares out there like this. Remember,
the more the company shoves into the program's memory, the more money it's
gonna cost them. Of course, I cannot cover every single type of doc check
since there are too many of them. You'd just have to use
your own imagination to solve some of
them.
SPECIAL SITUATIONS
What all crackers are faced with at one time or another are situations that call for intuitive thinking to overcome the barrier. Remember, there is no one sure way of cracking.
INT 3 - Problems During Tracing
Sometimes, when you start cracking, you
just find your instruction pointer messing up. You keep tracing and tracing,
then your computer freezes. But then, when you type
"G" at the beginning of the program, it
works just fine.
What is happening here? There are
several things that the program could do to impede tracing. Unless you
have a hardware debugger, you have to settle in for more primitive,
intuitive methods. First, we have to find
out how a software debugger works. I now introduce you to INT 3 and INT
1. They are the breakpoint and single stepping interrupts respectively.
We will be looking at INT 3 the most.
What happens when you set breakpoints? Well, here is what the debugger does. At the address you have specified, the debugger will read in the byte at that address and store it somewhere else in its own memory. This byte is part of the whole instruction located at that address. For example, if there was an "INT 13" at that location, the machine language equivalent will be CD13h. Debug will read in the first byte, CDh, and save it in memory. The CDh will then be replaced by INT 3 (CCh). So, the code will now look like CC13h in machine language. When you unassemble this at the address, you will see "INT 3" (the instruction only takes up one byte) and some gibberish after that. So, when the CPU comes up to this address, it will encounter INT 3 and will return control to the debugger. The debugger then replaces the INT 3 with the CDh byte used before. With single stepping, the same thing occurs. Debug will also insert the INT 3 instruction at the instruction after the one you are about to execute. Then, internally, a "G" instruction is performed until it reaches the INT 3, at which point, the byte will be replaced and everything will be cool.
Use of INT 3 to Call Up Other Interrupts
This INT 3 deal seems to be cool, working
in many situations. But what if the software vendor reprograms INT 3 to
point to an INT 21? Many programs use INT 21 to access
DOS functions like reading a file, etc.
There would be a conflict now as the program uses INT 3 to call up DOS
while debug wants to use INT 3 for its breakpoints. There is also another
problem. INT 21 uses two bytes (CD21h) while INT 3 uses only one byte (CCh).
Therefore, you cannot replace INT 3 with the INT 21. Also, INT 3 could
be reprogrammed so that everytime it is used, the program will just exit
to its higher process. So everytime you single step, you will be kicked
out of the program.
Parity Errors with INT 3
The tough copy protections use the change of memory to obstruct tracing. Examine the code below:
2E0B:0500 FC CLD
2E0B:0501 B80000 MOV AX,0000
2E0B:0504 BB0000 MOV BX,0000
2E0B:0507 BE0005 MOV SI,0500
2E0B:050A BF0010 MOV DI,1000
2E0B:050D B90005 MOV CX,0500
2E0B:0510 AC LODSB
2E0B:0511 345A XOR AL,5A ;'Z'
2E0B:0513 01C3 ADD BX,AX
2E0B:0515 AA STOSB
2E0B:0516 E2F8 LOOP 0510
2E0B:0518 3B1E0043 CMP BX,[4300]
2E0B:051C 7403 JZ 0521
2E0B:051E E9EF2A JMP 3010
2E0B:0521 D1E0 SHL AX,1
Notice what the program is doing. It is performing a simple decryption of a block of code from address 500h and putting it in address 1000h. In addition, there is a checksum being performed at address . The program is adding all those bytes up, then comparing the number with some other number (a checksum value) in memory at address 4300h. So what you may say. When the program is run without any set breakpoints, the program will run fine. But when you start tracing through the code, or putting a breakpoint somewhere after the loop, the program will cause you to exit. If you decide to change the program so that it will let you pass regardless of the checksum value, somewhere along the line, the program will mess up.
This goes back to the idea of INT 3. Right before debug executes an instruction, it places an INT 3 at the next instruction. In this program, when debug places this interrupt and executes an instruction, the program is reading in this INT 3 at the address and copies it to a different address. INT 3 is obviously a different number than the other instructions, so the checksum value will be different. So, now that INT 3 is copied to another location in memory, debug also cannot replace that with it's original byte value.
Therefore, if you try to force the checksum
to match and continue running the program, the program will crash because
the INT 3 is causing the instructions after itself to be
interpreted incorrectly by the CPU. To
bypass this, you have to make sure not to get your INT 3 placed in the
wrong place at the wrong time. Looking at the program, you can keep tracing
normally until the SI register points to any byte past the CMP instruction
at address 519h. Then, you can do a "G 518" to finish off the loop quicker.
Debug will place a temporary INT 3 at address 518h, but it doesn't matter
now since SI will be past 518h.
This is obviously a simple example, but
it gets the point across that you have to watch where you trace.
OVERLAYS/LOADERS
Sometimes, programs will have an initialization
code and upon its completion, call up another program or overlay. These
programs present unique situations in which it is
sometimes difficult, after finding the
copy protection code, to write the changes to disk. Let's see what these
programs do before we go on to the next topic of making changes
permanent.
Loaders are usually small programs that
might first ask you for the graphics mode or what sound card you have.
When finished, it will load up another program. Sometimes, this is done
with DOS' interrupt 21h, function 4B00h (load and execute). This is the
same interrupt DOS uses to load up programs when you type them in at the
DOS prompt. You can tell what file is going to be executed by tracing up
to the INT 21 instruction and dumping the address pointed to by DS:DX (type
in "D DS:DX"). Also, internal procedures could be used to call up the program.
Use what you've learned to
trace through them.
Code decryptions or dynamic heap allocation where data is to be loaded presents problems as well. Code that changes as the program progresses makes code changes difficult in the file itself. And when you want to alter sometime in the data area, something called a heap is often used to store the data. The thing with the heap is that it can be allocated at anytime and depending on what is currently in memory, you can't tell where the memory is going to be located. In these cases, you might choose to go with run-time memory overlays (discussed later).
Writing the Changes Out to the File
Okay, so you've found the copy protection. You also know how to bypass it. Now, the next problem you will most likely encounter is writing it out to a file. But first, let's assume a simple case.
Using a Hex Dump Program
1) At the location of the instruction, copy down the machine language equivalent of the instruction. At instructions after that, also take down their machine level equivalents. This is what you will use to search for the code in the file.
a) If there is a near call or a near jump or a near memory access, you can just write down all the hex numbers.
b) If there is a far call
(CALL DS:[5C10+BX]) or a far jump (JMP DWORD PTR ES:[5080+BX]) or a far
memory access (MOV AX,WORD PTR ES:[10+SI]), then do not write these instructions
down. In .EXE files, anything that is located in different
segments will have different displacement
values. This is a value in the file. At the
beginning of the file is a table that
tells DOS where these instructions are located. When the program is loaded
into memory, the pointers are changed appropriately to match the memory
location. So, write down other near instructions like CLD, JZ 100, INC
AX, etc.
2) After you know what to search for, you
must now know what you will have to be changing. Very often, NOP's are
used to "delete" code. For example, if there is a CALL 3140 and we want
to skip this call, we can NOP it out. The near call takes up three bytes.
The NOP takes up one byte. So, type in "A" at the address of the call and
enter "NOP" three times. Then unassemble the code to make sure that the
code still looks okay. Take down the machine level equivalents of the NOP's
(90h). Same thing
with conditional jumps. Suppose you have
a JZ 90 and you want it to jump to address 90 everytime, then type in "A"
at the jump instruction and enter "JMP 90". Then, just write down the machine
code as before. One thing, however. You cannot do what I
have just said above with far calls. Remember,
the numbers will be different in the file as compared to memory. So what
do you do? No problemo. At the call instruction, trace into the call and
place a "RETF" instruction at the address of the callee.
This will be the location that you will search for (write down the bytes here) and where you will be writing to (RETF is CBh in machine language).
3) Finally, after all this is through,
you can enter your file editor and search for the numbers you wrote down.
Then, you can change the numbers. Now run the program and it should be
cracked. But remember, always backup the file you are about to
change.
Using a Memory Overlay
When do you use these things? You would use memory overlays when step 3 (stated above) has failed in some way. Maybe you couldn't find the code, or when you change it, the program freezes up. Don't fret, the memory overlay is here. What is a memory overlay? It is an external program (TSR) that when it reaches a certain point during program execution, it will change the location in memory you have specified. It overlays the code during run time.
Here is what you will need to do to make
the overlay work. First, you must find some way for the program to call
up the overlay code. This can most easily be done by
reprogramming interrupts. So, the first
thing you have to do is look for an interrupt usage near the copy protection
code (usually an INT 21h or INT 10h). When you find this interrupt (it
must be fairly close to the code), write down the address of the NEXT instruction.
You must get down the segment and the offset. Also, get down the current
status of the registers. For interrupts like INT 21h and INT 10h, write
down the functions numbers (eg. AX,AL,BX,DX,etc.).
Then, keep tracing until the copy protection
code. Get the address of the instruction that you want to change (the segment
and the offset). Also get down the machine language
equivalent of the changed code. This should
be all you need for the overlay program. Here is the overlay program:
INT_SEG equ 1DA5h
;SEG:OFF of instruction after the
INT_OFF equ 05D1h ;
calling interrupt
CHANGE_SEG equ 2DA5h
;SEG:OFF of instruction to change
CHANGE_OFF equ 0432h
OVERLAY segment para 'code'
assume cs:OVERLAY,ds:OVERLAY
org 100h ;This will be a .COM program
START: jmp INITCODE ;Initialization code
;**************************************************************************
OLDINT dw 0,0 ;Storage for old interrupt address
ADDR_OFF equ <word
ptr [bp+2]>
ADDR_SEG equ <word
ptr [bp+4]>
CR equ 0Dh ;Carriage
return
LF equ 0Ah ;Line feed
BEEP equ 07h ;Beep
EOS equ '$' ;End of
DOS string
DISPLACEMENT equ CHANGE_SEG - INTSEG
;**************************************************************************
NEWINT proc far
push bp ;Establish
stack frame
mov bp,sp
push ax ;Save necessary
registers
push bx
push cx
push dx
push si
push di
push ds
push es
mov bx,ADDR_OFF
;Get offset
cmp bx,INT_OFF
jnz EXIT
cmp ax,0201h ;Check
for AX=0201h <=(1)
jnz EXIT
cmp bx,0001h ;Check
for BX=0001h <=(2)
jnz EXIT
mov bx,ADDR_SEG
;Get segment
add bx,DISPLACEMENT
mov ds,bx ;This will
be the segment of change
;change the number
at the next line to point to the offset of
; the address to be
changed
mov bx,1C12h ;This
is the offset of the change
mov al,0EBh ;This is
the byte to be changed
mov [bx],al
;change the number
at the next line to point to the offset of
; the address to be
changed
mov bx,1C20h ;This is
the new offset of the change
mov ax,0B8h ;This is
the byte to be changed
mov [bx],ax
mov al,0 ;This is the
next byte to be changed
mov [bx+2],al
pop es ;Restore
necessary registers
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
pop bp
iret ;Interrupt return
EXIT: pop es ;Restore
necessary registers
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
pop bp
jmp dword ptr cs:OLDINT
;Jump to old interrupt
NEWINT endp
;**************************************************************************
FINISH equ $
MESSAGE db "This is an
overlay loader.",CR,LF
db "Written by The Cyborg.",CR,LF,BEEP,EOS
INITCODE:
mov ax,cs
mov ds,ax ;DS point
to CS
mov ah,9 ;Print
string
mov dx,offset MESSAGE
;The address of the message
int 21h
mov ax,3510h ;Get
old interrupt address
int 21h
mov OLDINT[0],bx ;Save
in memory for later use
mov OLDINT[2],es
mov ax,2510h ;Set
new interrupt address
mov dx,offset NEWINT
;Point to new procedure
int 21h
lea dx,FINISH ;CS:DX
of last byte of code to remain
int 27h ; in memory.
Terminate and stay
; resident.
OVERLAY ends
end START
All you have to do is set the first four values in the first four lines of the file. They are the segment:offset pairs of the interrupt address and the address of the bytes to be changed. Also, change the functions to check for at (1) and (2) to appropriately check for proper code entry. Then, specify which bytes you will be changing at the specified lines. Then compile this crack ("ASM OVL {enter}").
The next program demonstrates a simple
loader. It also demonstrates what you can do if you have a program that
utilizes scripts or dynamically allocated data areas in heap spaces. This
program scans for a known segment in memory for a "keyword". When
it finds this, it can then begin writing
new code to overlay the old data. Note, KEYWORD specifies the keyword to
look for. Then, CRK (0's) is the list of bytes to replace the data areas
pointed to by addresses listed in LIST. The addresses in LIST are displacement
addresses. This means that at the address the keyword was found in, the
appropriate number listed in LIST is added to that address. There are thirteen
addresses whose data are to be changed in this case.
Also interesting to note is that this program is using two interrupt vectors, INT F1h and INT 21h. INT 21h is used in the same way as the above overlay program uses it. It replaces two bytes at offset 1FE5h with CDF1h. This is the machine language equivalent of INT F1h. Now, let's examine what INT F1h actually does. First, it changes the return address in the stack so that instead of returning to the address right after the INT F1h instruction, it will return to another instruction, located at offset 1FE5. This is the location of the INT F1h instruction.
This interrupt, upon its completion, will replace the INT F1h instruction with the original instruction and run the program normally. The loader itself is simple. It reallocates the memory located to itself to accommodate a "daughter" program, the program that it is going to load. If it can't find the program or if an error has occurred trying to execute the program, the loader will load itself up as a TSR. Then, you can run the program via DOS. This loader also checks if INT F1h has been occupied and returns an error if it is.
LOADER segment para 'code'
assume cs:LOADER,ss:LOADER
org 100h
BEGIN: jmp INIT
CR equ 0Dh
LF equ 0Ah
BEEP equ 07h
EOLN equ '$'
OPTION db 1 ;Options
CRC dw 0 ;Cyclic Redundency
Checking data
START equ $
OLDINT1 dw 0,0
OLDINT2 dw 0,0
KEYWORD db "weat"
CRK db 0,0,0,0
LIST dw 0h,014h,019h,02Dh,041h,046h,05Ah,05Fh,073h,087h,08Ch,0A0h,0B4h
;********** New Interrupt 1 **********;
NEWINT1 proc far
push bp ;Establish
stack frame
mov bp,sp
push ax ;Save registers
push bx
push cx
push dx
push di
push si
push ds
mov ax,cs
mov ds,ax
mov ax,word ptr
[bp+2] ;Get offset
cmp ax,1FE7h
jnz EXIT1
NEXT1: mov ax,1FE5h
;Where to return next
mov word ptr [bp+2],ax
mov ax,word ptr
[bp+4] ;Get segment
mov ds,ax ;Put in data
segment
mov bx,1FE5h ;Offset
to change
mov ax,0D803h ;The new
code to put in
mov [bx],ax ;Store changes
mov ax,cs ;Get
current data segment
mov ds,ax
mov di,0 ;Where
to start search
mov dx,0FF00h ;Search
the entire segment
mov bx,0
COMP: mov di,bx ;Where
to begin
mov si,offset KEYWORD
;Get keyword
mov cx,4 ;Lenght of
keyword
repe cmpsb ;Compare
until done
jz MATCH
inc bx
dec dx ;Done?
jz EXIT1 ;If no match,
exit
jmp COMP
MATCH: mov dx,bx
mov ax,0E07h
int 10h
mov bx,offset LIST ;Get
list of codes to change
mov cx,13 ;Number of
locations to change
NEXT2: push cx
mov cx,4 ;Lenght of
string
mov di,[bx] ;Get destination
add di,dx
mov si,offset CRK ;Get
string to copy from
rep movsb ;Copy String
inc bx ;Next location
inc bx
pop cx
loop NEXT2
EXIT1: pop ds ;Restore
registers
pop si
pop di
pop dx
pop cx
pop bx
pop ax
pop bp
iret ;Interrupt return
NEWINT1 endp
;********** New Interrupt 2 **********;
NEWINT2 proc far
push bp ;Establish
stack frame
mov bp,sp
push ax ;Save registers
push bx
push ds
mov bx,word ptr
[bp+2] ;Get offset
cmp bx,0Ch ;See if called
from the proper offset
jnz EXIT2 ;If not, exit
cmp ah,30h ;See
if want this function call
jnz EXIT2 ;If not, exit
mov bx,word ptr
[bp+4] ;Get segment
add bx,0F8Dh ;New segment
mov ds,bx
mov bx,1FE5h ;New offset
mov ax,0F1CDh ;The new
instruction
mov [bx],ax ;Save changes
in memory
EXIT2: pop ds ;Restore
registers
pop bx
pop ax
mov sp,bp
pop bp
jmp dword ptr cs:OLDINT2
;Call old interrupt
NEWINT2 endp
FINISH equ $
;********** Initialization Code **********;
PARAM dw 0
db 80h,0
PARAM1 dw 5 dup(0)
PROG db 8 dup('1234567890')
MESS db 'Savage Empire
àeta Crack v1.0 July 15,1991',CR,LF
db 'Loader needed only
after creating a character.',CR,LF
db "Press {ENTER} at
the copy protection.",CR,LF,BEEP,EOLN
ERR1 db 'ERROR: Not
enough memory. '
db 'Activating TSR sequence.',CR,LF,BEEP,EOLN
ERR2 db 'ERROR: Could
not load program. '
db 'Activating TSR sequence.',CR,LF,BEEP,EOLN
ERR3 db 'ERROR: Interrupt
vector (0xF1) already occupied.',CR,LF
db ' Release memory
before restarting.',CR,LF,LF,BEEP,EOLN
INIT: mov ah,9 ;Print
string
mov dx,offset MESS
int 21h
mov ax,35F1h ;Get
interrupt vector
int 21h
mov OLDINT1[0],bx ;Save
in memory
mov OLDINT1[2],es
cmp word ptr es:[bx],8B55h
;Check for vector occupation
jnz CONT1
mov ah,9 ;Write
string
mov dx,offset ERR3
int 21h
mov ax,4C03h ;Exit with
error 3
int 21h
CONT1: mov ax,25F1h ;Set
interrupt vector
mov dx,offset NEWINT1
int 21h
mov ax,3521h ;Get
interrupt vector
int 21h
mov OLDINT2[0],bx ;Save
in memory
mov OLDINT2[2],es
mov ax,2521h ;Change
interrupt vector
mov dx,offset NEWINT2
int 21h
cmp OPTION,0 ;See
if wants to run program
jz EXIT3
mov ax,cs
mov ds,ax
mov es,ax
mov bx,offset ENDCODE
;Get end of memory
shr bx,1 ;Convert to
paragraphs
shr bx,1
shr bx,1
shr bx,1
inc bx
mov ah,4Ah ;Reallocate
memory
int 21h
jnc OKAY1 ;If no error,
continue
mov ah,9h ;Write string
mov dx,offset ERR1
int 21h
jmp EXIT3
OKAY1: mov ax,cs
mov PARAM,ax
mov PARAM1,ax
mov bx,offset PARAM
mov dx,offset PROG
mov ax,4B00h ;Load and
execute child
int 21h
jnc OKAY2 ;If no error,
continue
mov ah,9h ;Write
string
mov dx,offset ERR2
int 21h
jmp EXIT3
OKAY2: mov ax,25F1h ;Restore
interrupt vector
lds dx,dword ptr OLDINT1
int 21h
mov ax,2521h ;Restore
interrupt vector
lds dx,dword ptr OLDINT2
int 21h
mov ax,4C00h ;Exit
with error code 0
int 21h
EXIT3: lea dx,FINISH
;Offset of booster
int 27h ;Exit with ejection
of booster
LOADER ends
end BEGIN
CONCLUSION
Okay, so we've seen the processes of cracking.
If you are just a beginner and don't know much about programming, you probably
got lost somewhere right after the introduction. I would suggest that you
spend some time learning assembly before doing
anything else. Actually, you don't have
to start out with assembly. I started programming using BASIC. When I got
really good at it, I jumped into Assembly, regardless of how difficult
people said it was. Assembly is not at all difficult if you have had some
previous knowledge of another language. It is only difficult if you make
it hard. And after you've learned assembly, you get a "feel" for the other
languages and can learn them in a
matter of days. Pascal, Modula-2, C, C++,
..., they're are based on assembly language programming.
Cracking is like the debugging process of programming. To become experienced with debugging is to become adept at cracking. You just need lots o' practice as practice makes perfect. One final note. I got this manual out kinda quickly so there are bound to be errors, inconsistencies in what I've said, unclear passages, etc. Well, too bad. If you really want a good manual, tell me or something and I'll consider it. I got really bored towards the last parts of the manual so it went pretty fast, skipping over some stuff. If a lot (and I mean A LOT) of people want a better manual, tell me and give me suggestions. I'll find the time to do it somehow.
Anyways, have fun!
- The Cyborg
Return |