1140 lines
62 KiB
Plaintext
1140 lines
62 KiB
Plaintext
**********************************************************************
|
||
|
||
Hacking Programs on the Amiga
|
||
|
||
By FunToHack of K1W1 Christchurch New Zealand
|
||
(all spelling mistakes are free)
|
||
Version 1
|
||
|
||
**********************************************************************
|
||
|
||
|
||
AN INTRODUCTION TO HACKING
|
||
|
||
In this article I want to talk a about hacking on the Amiga (a lot of this
|
||
should also apply to the ST). The reason I am writing this article at all
|
||
is the number of time I have heard the question "how do you hack something"
|
||
and it is the one question that can't be answered by pointing the asker at
|
||
a book. So I thought I would put a few of the methods I use down in an
|
||
article to help inspire budding young hackers. I am certainly interested in
|
||
any methods that others are using. If you read this article and say I
|
||
didn't learn anything then the article was not intended for you. If you
|
||
read it then say I could have written a better article then please do so.
|
||
If you have done some hacking you may want to just skim the first stuff and
|
||
look at the bit entitled "THE ACTUAL HACKING OF THE GAME" I want to talk
|
||
about a few techniques of tracing programs and encryption of programs etc.
|
||
In the past I have written two files on the monitor program Monam which is
|
||
HighSofts Devpac's monitor program. One is just the instructions for
|
||
running Monam and the other one is a list of methods for using Monam to
|
||
help you trace etc. When I uploaded the second file there was so much
|
||
confusion as to which was which and if they where the same thing etc. Now
|
||
that I am doing a third one I have decided to hell with it all and have
|
||
included them both at the bottom of this file for completeness.
|
||
|
||
|
||
FORMS OF PROTECTION AND PHOTOCOPYING MANUALS
|
||
|
||
There are two basic forms of protection, there is Disk protection where the
|
||
disk has some funny track on it that is very hard to copy. But the game can
|
||
check whether this track has been copied correctly. This track will come up
|
||
as an error in your copy program although this is not necessarily true in
|
||
all cases. Some disks have some funny bytes out on a long track that the
|
||
game checks for. In fact the disk can be copied without error (missing the
|
||
extra bits) but still won't run. The other form of protection is manual
|
||
protection. This kind of protection is the one where the program comes up
|
||
and asks you for a word on a particular page of the manual so you have to
|
||
have a copy of the manual to run the game. They use to just do exactly that
|
||
but lately they are doing more to make it more difficult such as printing
|
||
the book red on black so that when you photocopy the book you get a lovely
|
||
black blob or they are doing what Future Wars did and getting you to
|
||
identify a colour picture and giving you different copies of the colour
|
||
picture to choose from so that even if you have a colour photocopy of the
|
||
manual the chance of you getting a good enough copy of the manual to tell
|
||
the subtle differences that the Amiga screen will allow is very slim. A
|
||
little note if you are trying to photocopy a page that has something like
|
||
red on black you can achieve good results by placing a filter over the page
|
||
you are photocopying. A filter I here you cry where do I get one of those
|
||
from. Well believe it or not you can get really good results with a white
|
||
plastic bag try it I didn't believe it until I saw someone do it. Of course
|
||
different colour bags can be used for different colour pages. The manual
|
||
form of protection is getting more and more popular with game writers one
|
||
of the main reasons is because you can put out a disk that can be installed
|
||
to hard drive. But the trouble with this is you have to get the manual out
|
||
ever time you want to run the game. This is of course also the case for the
|
||
guy who bought the original game and there are a lot of people who have
|
||
bought a game and are still very interested in a hacked copy of the same
|
||
game to get around the manual. Just ask anyone who bought a copy of
|
||
Midwinter there was a game that could take you anything up to 15 minutes to
|
||
get started, this is no reward for being nice enough to buy a real one.
|
||
|
||
|
||
COPYING THE DISK BEFORE HACKING
|
||
|
||
The first thing you do is copy the disk with a program such as Xcopy.
|
||
Always put verify on and I highly recommend you only use the Doscopy mode
|
||
because Doscopy+ will correct errors on the disk that the program maybe
|
||
looking for. To illustrate the point take a brand new disk that hasn't been
|
||
formatted and use it as the source disk in Xcopy with the Doscopy+ option
|
||
selected and you will find it copies the whole disk correcting each sector
|
||
it couldn't find as it goes. So copy the disk with Xcopy in Doscopy mode.
|
||
There are some games with weird disk formats that you will have to nibble
|
||
copy as well as hack I will talk about these in the section at the end
|
||
called "Disks with funny formats".
|
||
|
||
|
||
|
||
FINDING OUT THINGS ABOUT THE GAME YOU WANT TO HACK
|
||
|
||
The first thing you will want to know is whether the game runs from the CLI
|
||
or whether it boots from the boot sector. The way to find this out is to
|
||
boot the game up and hit CTRL D (repeatedly tap CTRL D not just hold it
|
||
down) and if the game stops executing then and drops to the CLI then you
|
||
know it starts from the startup-sequence and not a Boot sector. The game
|
||
won't stop if it has booted from a boot sector. I found an exception to
|
||
this when I hacked the game Castle Master which had code in the boot sector
|
||
which clobbered a lot of addresses and confused the machine into not
|
||
responding to the the control d sequence. This had me confused for a bit
|
||
but I found all I had to do was install the copy of the disk (so it had an
|
||
ordinary boot sector) and it made no differences to the running of the
|
||
game. I also remember hacking one on an St that booted from a boot sector
|
||
and loaded up everything directly from tracks but it had a directory on it
|
||
with about 12 files with big file lengths none of which were on the disk it
|
||
was just a directory of another disk. Both of these examples are rear but
|
||
serve to remind us that the programmer is trying to make it hard for us.
|
||
Sometime you will find the game stops loading but the screen is completely
|
||
black. There is no great secret here it is just that the preferences have
|
||
all the colours defined as black. The game when it runs would then go on an
|
||
set the colours to whatever is required. It doesn't effect the game at all
|
||
but it makes it hard for us to see what is going on there are two ways
|
||
around this one is to kill the System-Configuration file in the DEVS
|
||
directory or copy one off another disk. The other way is to boot of your
|
||
normal system disk and put the game to be hacked in the other drive and
|
||
have a look at it. Have a good look at the disk you will want to have a
|
||
look at the startup-sequence and see what program actually starts the game.
|
||
One of the things to find out is how far the copied game will go before it
|
||
fails it's protection. Some games stop straight away which are normally
|
||
easier to hack because you dont have to wade through as much code to find
|
||
the protection. Some games go into a demo mode which make the game a lot
|
||
harder to hack because to look at the code the program is busy doing real
|
||
things and it makes it harder to find where it is failing its protection.
|
||
Some noble the game in some way such as PipeManier and Where in Time is
|
||
Carmen Sandiago which let you play one level before it was obvious whether
|
||
you had succeeded in hacking the game or not. Especially Where in Time
|
||
because you had to beat the first level before you found out if you had
|
||
hacked it or not and if you are as thick as I am it takes you two or three
|
||
goes to complete the first level. This of course slows you down plus puts
|
||
you off your hacking. The other important thing to find out about the game
|
||
is will the game run from Monam. A lot of games detect if they are being
|
||
run from another task and go off and count white elephants before they even
|
||
do the protection. If this is the case then you have to find where it does
|
||
the checking and hack that bit first (isn't this fun). Another import thing
|
||
to notice is anything that happens when it fails its protection. On castle
|
||
master every time it failed it's protection it flashed the border red and
|
||
black. This made it a really easy job to find where the protection was you
|
||
just searched the code for the register that controlled the boarder colour
|
||
and worked backwards. It could also display a string saying it fail or even
|
||
if you are lucky you could get a Message from "The Federation Against
|
||
Software Piracy" (I think it is called that). If the game is using manual
|
||
protection then you should note any words asking you for a word from the
|
||
manual. If we can find the part of the program that displays this message
|
||
then we can put a break point there and run the game up until the break
|
||
point. This will save a lot of single steeping and it will probably be
|
||
doing the test for the correct word soon after this. Searching for these
|
||
strings is an easy matter (more about that latter). Another thing to listen
|
||
for is a loud graunching sound this is quite often the program checking for
|
||
the disk protection.
|
||
|
||
|
||
PREPARING TO TRACING THE GAME WHILE IT IS RUNNING
|
||
|
||
I use a Monitor program called Monam which is part of the Devpac package
|
||
from HighSoft. I have already written a file on Monam explaining the
|
||
commands and have added it and another covering handy hints for using Monam
|
||
to the bottom of this file. One of the things you may not know if you have
|
||
a copy of Monam but not a manual is that it has a companion library that
|
||
monam looks for in the LIBS directory (Libfile.Monam) of the system disk
|
||
(the disk you booted off). If Monam finds the library when it is started up
|
||
then when you are tracing the game it will convert all those system call
|
||
numbers to the proper call names so when you are tracing it you are not
|
||
constantly looking up the book all the time to find out what it is about to
|
||
do. Ok so you boot up off of your favorite hacking disk and start Monam
|
||
then insert the game disk and enter the program name and start tracing it.
|
||
This is fine if you don't have to boot off the game disk. Some games will
|
||
let you do this especially ones that have the protection in the program
|
||
that was listed in the startup-sequence and dont load any other files
|
||
before checking the protection. Also some games will go to where ever the
|
||
disk is and load any files it needs. But I don't think I have ever seen one
|
||
that checks for funny tracks on any drive other than drive DF0:. Ok if the
|
||
game really wants the system disk to be the game disk you can boot off of
|
||
the game disk (stopping it with a control D) and then run monam off of the
|
||
second drive with the command df1:c/monam. But it will still look in the
|
||
libs directory of the system disk for the Library file which in this case
|
||
will be the games disk and of course it will not find it there. Of course
|
||
you could always copy it there if there is room on the disk but you never
|
||
know when you are going to copy over something on the disk that is
|
||
important to the game. If you are at all worried there is a safe way to
|
||
have your cake and eat it to. To do this you use the assign command from
|
||
the CLI. Just insert your system disk with monam in DF1: and type
|
||
DF1:C/ASSIGN LIBS: DF1:LIBS so the amiga will treat the games disk as the
|
||
system disk except for the libs directory which it will find on DF1:. Ok if
|
||
you can get away with booting off your favorite hacking disk and loading
|
||
the program of the second drive or just booting of your hacking disk and
|
||
starting up Monam then put the game disk in and enter the program name to
|
||
load up and start tracing it then definitely do it. Some games will let you
|
||
run the whole game off of the second drive (or hard drive) and only go to
|
||
the first drive to check for disk protection. This can be a great help when
|
||
you are trying to find out when it checks for the protection.
|
||
|
||
|
||
**********************************************************************
|
||
|
||
|
||
THE ACTUAL HACKING OF THE GAME
|
||
|
||
Now we have all that preliminary stuff out of the way it is time to start
|
||
getting on with the actual hacking of the game. This is not usually a small
|
||
job. It involve lots of late nights gallons of Coffee and the phone number
|
||
of a 24 hour pizza delivery. I will look at games that run a program first
|
||
and I will look at the boot sector ones at the end of this article. Ok load
|
||
up Monam and load in the program. The program loads into memory as if it
|
||
was going to run and stops with the program counter set to the start of the
|
||
program. I will try to break the file up into logical bits if I can.
|
||
|
||
|
||
The Symbol Table
|
||
|
||
When you are writing a program you use labels. Now if the guy (or girl)
|
||
writing the program has assembled the source code with the symbol table ON
|
||
it will be there when you enter Monam and the labels will appear down the
|
||
left hand side of the column. If you press L the labels and addresses will
|
||
be displayed. If the table is there then have a good look at the labels and
|
||
look for anything intelligent such as a routine called PROTECTION (don't
|
||
laugh it has happened on more than one occasion). Also labels pointing a
|
||
Protection Messages etc.
|
||
|
||
|
||
Tracing the Program
|
||
|
||
As mentioned above there is a file tagged to the bottom of this one on the
|
||
instructions for Monam quickly here are some of the trace commands.
|
||
|
||
CONTROL Z is the command to single step the program an instruction
|
||
at a time
|
||
|
||
CONTROL A is the command to place a break point at the next
|
||
instruction and run the program. This is wonderful
|
||
for running subroutines and also a great instruction
|
||
to run at the bottom of a DBEQ loop that is going
|
||
around and around. It will do all the loops and stop
|
||
when the program counter hits the breakpoint
|
||
AMIGA Z expand the window you are in to full screen
|
||
|
||
TAB move to the next window
|
||
|
||
G search for
|
||
B Byte 8 bits
|
||
W Wide 16 bits
|
||
L Long 32 bits
|
||
T Text
|
||
I Instruction (source code)
|
||
R Run the program
|
||
|
||
Amiga B set a breakpoint (enter address,parameter)
|
||
parameters =
|
||
nothing for stop first time and clear the break point
|
||
number (1-255) for go through this break point
|
||
the number of times then stop
|
||
* always stop here and never clear the break point
|
||
|
||
|
||
Remember you can't Break Point the rom and following library calls is silly
|
||
they take ages and are not really meant to be single stepped if you have
|
||
accidentally started into the rom or a library call then press H (for
|
||
history) and see where the instruction that you JSR from is and place a
|
||
break point after it then run the program and it will stop when it hits the
|
||
break point.
|
||
|
||
Ok you can trace the program with these instructions and especially if you
|
||
use the CONTROL A function a lot you can find out which subroutine the
|
||
protection lives in. So you trace the program going CONTROL A to execute
|
||
the BSR's and JSR's at full speed until you get the sound of the drive
|
||
checking for disk protection or asking you for a word from the manual. Ok
|
||
now do it all again but when you get to that subroutine don't go control A
|
||
go control Z instead so you can have a look at the subroutine. Normally you
|
||
will find it is a full of code and other subroutines. So you start going
|
||
down the subroutine going control A on its subroutines and see if you can
|
||
narrow down where the protection is. Gee that sounds nice and easy and
|
||
extremely quick to do. But the trouble is a lot of games have the
|
||
protection a long way into the game. You will often find it does an
|
||
introduction sequence before it even thinks about protection.
|
||
|
||
|
||
Jumping ahead in the code
|
||
|
||
Something you have to remember is that a little bit of program code turns
|
||
into an economize bit of source code (anywhere between 10-20 times the size
|
||
of the program code). So this is a hell of a lot of code to look through.
|
||
So this is where the stuff we noticed as the game starts up becomes handy.
|
||
Even if it is something simple like the program loads a Title Screen from
|
||
disk before the protection. You search through the program for the name of
|
||
the title screen and work backwards to find out what bit of code is using
|
||
the file name to load the file (see searching for strings). You can place a
|
||
Break point somewhere after the title screen is loaded. This saves you a
|
||
hell of a lot of single stepping to get to somewhere that might be of
|
||
interest to us. Anything you can find will save you a lot of time.
|
||
|
||
|
||
Searching for strings
|
||
|
||
Ok a lot of games these days are using Manual protection. This is quite
|
||
good for games that you want to install to hard drive. See "Forms of
|
||
Protection and Photocopying Manuals" (above) for advice about copying
|
||
colour protected manuals. Ok the games usually come up and ask you for a
|
||
word form the manual. The lines normally pretty easy to find you just
|
||
search memory for them. If you find the string check you haven't just found
|
||
a string left over from an earlier load of the game. The way to do this is
|
||
to note the address and then press Help (for hunk list) and check the
|
||
address is contained in one of the hunks otherwise it is not part of the
|
||
program we are running. Remember to get the case of the letters of the
|
||
string right or it wont find the string. Also remember you don't have to
|
||
search for the whole string just enough to make it unique. Also the longer
|
||
the string you search for the longer it takes to find it. Dont search for
|
||
strings like "Page Number Of the Manual" because "Page Number" and "Of
|
||
the Manual" might be two different strings that might have spaces between
|
||
them or might not. And of course dont search for any page numbers even if
|
||
you can see them on the screen. Because the chance that the program patches
|
||
the number into the string before printing the whole string is very
|
||
unlikely. Ok we have found the strings but that doesn't mean a thing, what
|
||
we need is the bit of code that uses the string not the string it's self.
|
||
So what we have to do is work backwards from there. First have a quick look
|
||
around to see if anything is pointing at the address of the string. More
|
||
likely than not you will have to search for it. I normally search for the
|
||
address with the Instruction version of the search command because of the
|
||
different addressing modes on the 68000 doesn't mean that the string will
|
||
be pointed to by a hex number it could be in the form of A3+1234 or
|
||
something like that. The hex searches won't find it but the Instruction
|
||
search will if A3 has the value it is supposed to. Another thing I have had
|
||
work a couple of times is to search for the distance between the start of
|
||
the Hunk that the string is in and the start of the string it's self (use
|
||
the O command to work it out). An important thing you must remember about
|
||
the string you are searching for is where it actually starts. To do this
|
||
you have to have a look at the string itself, some strings have the length
|
||
of the string in a byte in front of the string and the value you should be
|
||
searching will more likely be it's address and not the start of the letters
|
||
(also there could be control codes in front of the string as well). If you
|
||
are using the Instruction search there is nothing stopping you from
|
||
searching for part of the number i.e. say the string is at 71234 hex you
|
||
could search for 7123 and it would report all numbers from 71230-7123f.
|
||
Unfortunately there is no guarantee you will find the bit of code with any
|
||
of these methods because of the fact that the address of the string could
|
||
have to be calculated and addressed from a register. If you do find it it
|
||
will probably be in the form of PEA $71234. Ok you searched the file and
|
||
you didn't find the actual string it's self at all. There could be a number
|
||
of reasons other than the address having to calculated. One reason could be
|
||
the string that you see is actually graphics and not text, another reason
|
||
is that they are hiding the string in one way or another. One way it could
|
||
be hidden is for it to be in a bit of the program that is encrypted. I will
|
||
talk more about finding this sort of thing in the sections on Decryption
|
||
and Resetting the Machine. Another way of hiding it is to put it in another
|
||
file. The best way I have found to check whether this is the case or not is
|
||
to use the ARP'S search command. What? you are not running ARP Arp stands
|
||
for the Amiga Resource Project (or something like that) it has a dos
|
||
command word "SEARCH". You can use it from the dos line like this
|
||
|
||
|
||
SEARCH DF1: Manual ALL
|
||
|
||
|
||
The word "SEARCH" is the command, df1: is the drive, the word "Manual" is
|
||
the word we are searching for and ALL means do all files in all sub
|
||
dirrectories. If you have this file you are reading now then chances are
|
||
you got it from an Amiga BBS then if you want ARP you will surely find it
|
||
on the BBS. I highly recommend ARP in its entirety. If SEARCH finds the
|
||
word it tells you what file in what directory and the offset address in the
|
||
file (what more could you want). Ok if you find it in an other file then
|
||
you look through the main program and find where it loads the file and
|
||
start working from there.
|
||
|
||
Happy hunting.
|
||
|
||
|
||
CLEVER THINGS THEY DO TO STOP US
|
||
|
||
The protection systems are getting much better (harder). I remember when I
|
||
first got an St finding some of the games just checked for the protection
|
||
and if it failed they just halted the program by executing an instruction
|
||
like below
|
||
|
||
|
||
Here: BRA Here 60FE
|
||
|
||
|
||
all the instruction does is jump to it's self but the code for is always
|
||
going to be 60FE so if the program just froze I would load the program up
|
||
and search for the hex 60FE. If you found it you knew the protection
|
||
couldn't be far away because not to many games specialize in doing nothing.
|
||
Anyway these days we have encryption and all sorts of things things to make
|
||
it harder.
|
||
|
||
|
||
Address 24 hex
|
||
|
||
Address 24 is a very important address as far as monitor programs (like
|
||
Monam) are concerned. It is the address of the routine the monitor uses for
|
||
tracing programs. If the trace flag is set it gets the address to jump to
|
||
from this address. So a quite common thing to do is to overwrite this
|
||
address to upset any Monitor program. If you come across anything altering
|
||
this address you will have to take steps to get around it. There are two
|
||
ways around this, the first is to just step around the instruction using
|
||
the CONTROL S command which will move the program counter to the next
|
||
instruction without executing current instruction. The other way which can
|
||
be more convenient if the instruction is in a loop is to patch the program
|
||
in memory to replace the code with NOP'S you just change its hex numbers to
|
||
4E71, you of course may need more than one NOP. If you think the program is
|
||
doing this but you don't know where it is then you can search for anything
|
||
altering address 24 by using the instruction search command from monam
|
||
|
||
,$24
|
||
|
||
this will find any instruction that ends with ,$24. Note the comma is
|
||
important it stops you getting all the 24's in the world being dumped at
|
||
you.
|
||
|
||
|
||
Doing things to upset the Monitor
|
||
|
||
It is not uncommon in programs to have it do all sorts of things to upset
|
||
the monitor program. Whether it is deliberate or just part of the program
|
||
setting things up the result is the same, it stops you single stepping.
|
||
They are not to hard to find (just time consuming) you just single step
|
||
along and suddenly a number gets written to an Address and the monitor
|
||
stops listening to your commands. When this happens to you there is very
|
||
little you can do about it but reboot the computer to get out of it. But
|
||
next time you come up to the instruction (while single stepping) you have
|
||
to decide whether it is just in the process of setting the game up or there
|
||
to make life hard for you. If it is just setting the game up you might be
|
||
able to put a breakpoint farther on in the program and run the program
|
||
through the setup bit to the break point. Or you can use CONTROL S to step
|
||
around it or patch it out in memory with NOP's as in the paragraph "Address
|
||
24" (more on patching later).
|
||
|
||
|
||
Using the Illegal vector
|
||
|
||
Address 10 hex holds the address of the routine Illegal instruction
|
||
routine. Whenever the program comes across an Illegal instruction it jumps
|
||
off to the address held in the 10 and goes into supervisor mode which the
|
||
monitor program won't like (you can trace supervisor mode on the ST). So
|
||
what you see in a lot of programs is the address 10 being loaded with the
|
||
address the program wants to jump to and then the program executing and
|
||
ILLEGAL instruction. This is quit a nice simple easy way to through new
|
||
comers to hacking but stupid thing is nine time out of ten when you do see
|
||
it they are jumping to the instruction immediately after the ILLEGAL
|
||
instruction and more often than not they do it about three time in a row.
|
||
To get around this you alter the program counter to the address to be
|
||
placed in 10 hex. The thing you have to watch is the routine checking
|
||
whether it is in supervisor or not. The easiest way for the program to do
|
||
it is to examine the SR (status register) so have a good look at any
|
||
commands playing with SR. The other thing to remember is because the
|
||
program is supposed to be running in supervisor mode. So anything that the
|
||
program does in supervisor mode you will have to handle. One thing that
|
||
springs to mind is the RTE instruction (return from interrupt) which is a
|
||
command that can only be executed in supervisor mode so you cant single
|
||
step it because we are not in supervisor mode. To get the program to go to
|
||
the right place we have to do what a RTE would do for it. I quote from the
|
||
good book "Programming the 68000"
|
||
|
||
"The RTE (return from Exception) is used to load the status register and
|
||
the program counter (PC) by the use of a single instruction. This type of
|
||
operation is required when an operating system in supervisor mode passes
|
||
control to a user program in user mode. The new contents of the status
|
||
register and PC are popped off the stack. The status register is taken from
|
||
the first 16-Bit word on the stack, and the PC from the next 32-bit long
|
||
word. The stack pointer is incremented by six bytes."
|
||
|
||
What you do here depends a lot on the program if you find the program is
|
||
altering what is on the stack then doing a RTE remember the first 16 bits
|
||
are destined for the SR (which you can't alter anyway) and the next 32 and
|
||
the address the program is to return to. The big thing to keep an eye on at
|
||
all times is the stack pointer make sure it is pointing to where the
|
||
program expects it to be. Remember you can alter the stack (A7 normally)
|
||
just like any other register and looking at what is above and below where
|
||
the stack pointer is pointing to can be quite interesting. Quite often the
|
||
routine doesn't do anything important as far as hacking goes so you can go
|
||
through all the crap and work out where it is going to and run the program
|
||
up and just put a break point there and run the program. If there is
|
||
anything such as stamping on address 24 then Nop it out before running it.
|
||
|
||
|
||
The First instruction
|
||
|
||
Something worth noting when you start tracing a program is that Monam
|
||
actually runs the program but sets the trace bit so it stops running. The
|
||
thing to remember about this is the first instruction is actually preformed
|
||
before the trace kicks in and stops the program ready for tracing. I have
|
||
seen at lest one program (ghost & gouls for one) that the first instruction
|
||
was something that froze the keyboard or upset the computer in some way
|
||
that made tracing it impossible. The only way around this is to patch the
|
||
first instruction. But you can't patch the program in memory as you are
|
||
running it because it will freeze the machine before you get a chance to do
|
||
the patch. So you have to either patch it directly on the disk or use the
|
||
binary load method to patch it in memory (see the section on "Patching
|
||
Programs" for details on how to do this).
|
||
|
||
|
||
Not Enough Memory
|
||
|
||
I have done most of my hacking on Half Meg machines (Amiga and ST) and I
|
||
know it can be a problem trying to run a program from Monam that just runs
|
||
and no more in half a meg. Quit often you can still hack it depending on
|
||
what it wants the memory for. I have had heaps of programs that stop the
|
||
program running after they test the amount of available memory and compare
|
||
it to the amount that will be needed to run the program. But infact the
|
||
program does the protection bit before it actually needs all the memory. If
|
||
you can find where it does the memory check it is a simple matter to bypass
|
||
the check and continue on to the protection as long as you really don't
|
||
need the extra memory yet.
|
||
|
||
|
||
The program running another program
|
||
|
||
This is quit a cute method that will keep you up to all hours trying to
|
||
work out what is going on. What the program does is it loads into memory as
|
||
one program. But unbeknown to us is that the program actually consists of
|
||
two parts, both which are separate programs. The first part runs the second
|
||
part as a separate task and it is the second program that goes on an plays
|
||
the game. Monam will only work on the one task so you carry on single
|
||
stepping something that looks quite intelligent but isn't actually doing
|
||
the things that run the game. The first time you come across this you can
|
||
not work out what is going on you get so far through stepping the game and
|
||
something happens on the screen and then you start stepping it again from
|
||
the start of the program and it happens again at a different places in the
|
||
program. The easy way to find out is to run a program like XOPER and list
|
||
the tasks and you will soon see if there are anything running that you
|
||
don't know about. The way you get over to the new program is to find out
|
||
where the program creates the new task and see where it puts it. The best
|
||
way is to put a break point on the instruction that calls the function that
|
||
creates the procedure and run the program until it stops there. You now
|
||
find the it doesn't just pass an address to the function telling it where
|
||
it is. It actually uses a BCPL pointer to point to it which it passes in
|
||
the D3 register. What is a BCPL pointer ? a BCPL pointer is a pain in the
|
||
ass. It's reason for existing instead of just pointing straight at the
|
||
address is it is designed to be easy to convert to longword word aligned.
|
||
You can convert the BCPL pointer to hex by taking the number in D3 and
|
||
multiplying it by 4 then adding 4 to it. You can use Monam to do this for
|
||
you or use a calculator. I find it much better to use Monam because you can
|
||
change to the address while you do it and you can use the memory address D3
|
||
instead of having to key in the number it the memory address (see monam
|
||
methods in the article below). Once you have found where it is going you
|
||
can just go there and have a look around and trace the program visually to
|
||
find the protection. To run it properly you would have to alter the program
|
||
counter to the new address and start single stepping from there. The
|
||
ability to be able to do this depends alot on the first task. If the first
|
||
task runs the second task then kill it's self then you wont have to much
|
||
trouble. But if it passes values to second task or has to be running at the
|
||
same time then things get sticky. Another way to do it is to run the
|
||
program until the new task has been started and then go back to the
|
||
workbench and run XOPER and use the hunks command on the new task. It is
|
||
pointed to by a BCPL pointer as well so you have to multiply it by 4 and
|
||
add 4 to it as well.
|
||
|
||
|
||
ENCRYPTION
|
||
|
||
The biggest problem we face these days is encryption. What is encryption?
|
||
It is where the program self modifies itself on the fly. How? Well the
|
||
simplest way to think of it is to look at some code like the lot on the
|
||
left. The numbers in memory are 4E71 and 4E75. If we go through and add one
|
||
to each number we get the result on the right hand side were it turns into
|
||
something else.
|
||
|
||
HEX INSTRUCTIONS HEX INSTRUCTIONS
|
||
4E71 NOP 4E72 4E76 STOP #4E76
|
||
4E75 RTS 4E72 4E76 STOP #4E76
|
||
4E71 NOP 4E72 4E76 STOP #4E76
|
||
4E75 RTS 4E72 4E76 STOP #4E76
|
||
4E71 NOP
|
||
4E75 RTS
|
||
4E71 NOP
|
||
4E75 RTS
|
||
|
||
|
||
Of course to get the code back to it's original form all the program has to
|
||
do is go through and take one off of each word before it runs it. Please
|
||
remember something like adding one to each is about the simplest way you
|
||
could possible do it. They normally involve XOR'S and complicated loops.
|
||
Why do they do it? Well the first and obvious reason is that you can't read
|
||
what it says until it is decrypted and until then it just looks like a lot
|
||
of crap just another bit of graphics or somethings. An other reason that
|
||
isn't as obvious is you can't put a break point in the code to stop it
|
||
because the code gets changed by the decryption process so the break point
|
||
gets changed so it isn't a break point any more. And the biggest problem
|
||
for us is changing any of the code to what we want (but more about that
|
||
latter). Make sure you don't confuse decryption with a program that is just
|
||
unpacking it's self or translating some packed graphics. There is a big
|
||
problem for the guy writing the routine. To explain this you have to know
|
||
what happens to a program when you run it. When you run a program the
|
||
system loads it into any available bit of ram and uses a table that is
|
||
included in the file to relocate the program to run at that address. Now
|
||
once the program is loaded if the guy who wrote the program starts
|
||
decrypting it then he has to do one of two things he has to either handle
|
||
the relocation of that bit himself or write position independent code so it
|
||
will run anywhere. If the decrypting routine is decrypting code that is
|
||
elsewhere in memory then it is easy to decrypt. Just put a break point
|
||
after the routine for decrypting it and run it up to the break point.
|
||
|
||
|
||
START DECRYPTION CODE
|
||
DECRYPTION CODE
|
||
DECRYPTION CODE
|
||
DECRYPTION CODE
|
||
DECRYPTION LOOP TO START
|
||
MORE PROGRAM CODE <-Just drop a break point
|
||
MORE PROGRAM CODE Here and run it
|
||
MORE PROGRAM CODE
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
|
||
|
||
If the code to be decrypted is straight after the code that is doing the
|
||
decryption (as below) then we have to be a bit more careful.
|
||
|
||
START DECRYPTION CODE
|
||
DECRYPTION CODE
|
||
DECRYPTION CODE
|
||
DECRYPTION CODE
|
||
DECRYPTION LOOP TO START
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
|
||
|
||
We cant just put a break point after the decryption routine because that
|
||
would put a break point at the start of the code to be decrypted. Even
|
||
though monam displays the instruction that has the break point in it as the
|
||
correct instruction it has actually altered the code to put the break point
|
||
in it. So the decryption code trys to decrypt the break point instruction
|
||
instead of the code that should be there. You could single step all the
|
||
decryption routine but that would normally take ages. The way to get around
|
||
this is to make sure you go around the loop a few times by single stepping
|
||
it until the first couple of the instructions are decrypted before you pop
|
||
a Break point in the code and run the program until it hits the break
|
||
point. Another way to decrypt it is if you know where it will be going
|
||
after it has done the decryption (even if it has executed the decrypted
|
||
code) then just pop a break point there and run it. When it hits the break
|
||
point you can go back and have a look at the code that has been decrypted.
|
||
Ok this is still pretty simple decryption. What really stuffs you up is
|
||
when the combine it with the 68000 prefetch.
|
||
|
||
|
||
Prefetch
|
||
|
||
What is prefetch? The 68000 gets an instruction from the memory and then
|
||
while it is working out what the instruction does it prefetches the next
|
||
instruction which it will probably be running it next anyway. The prefetch
|
||
saves bus time, if it does want the next instruction then it gets it from
|
||
the prefetch buffer in the 68000 chip. If it does not want it (because of a
|
||
branch etc) then it is discarded and the instruction from the address to
|
||
branch to is fetched instead. Ok so what! it still get the same job done as
|
||
if it fetched the instruction, did it, then fetched the next instruction
|
||
etc. The thing you have to watch for is self modifying code. Code that
|
||
modifies the next instruction runs differently than it single steps. When
|
||
you single step a program it fetches every instruction as you need it, it
|
||
doesn't have a prefetch buffer. So if you have self modifying code that
|
||
alters the next instruction to be run you can get a different result single
|
||
stepping the code than you do running it. To really illustrate the point
|
||
look at the section of code below.
|
||
|
||
|
||
code1
|
||
code2
|
||
code3
|
||
code4
|
||
code5
|
||
code6
|
||
|
||
What actually happens when you run a program (looking at diagram below) is
|
||
the first Instruction is executed (A line) at the same time the next
|
||
instruction is prefeched (B line) then the second instruction is executed
|
||
from the prefetch buffer and the next instruction (3 line) is prefetched.
|
||
|
||
|
||
(A) execute code1 and prefetch code2 instruction
|
||
(B) execute code2 and prefetch code3 instruction
|
||
(C) execute code3 and prefetch code4 instruction
|
||
(D) execute code4 and prefetch code5 instruction
|
||
(E) execute code5 and prefetch code6 instruction
|
||
|
||
|
||
Ok that is fine until the program alters the next instruction in memory but
|
||
still runs the old one that it has in its prefetch buffer as below
|
||
|
||
|
||
start nop
|
||
nop
|
||
nop
|
||
move.w #$60fe,here
|
||
here nop
|
||
nop <-place the break point on this line
|
||
nop
|
||
|
||
|
||
The hex numbers 60fe is the hex numbers to jump to the same address. So at
|
||
first appearance the code above will do nothing for the first three lines
|
||
then store 60fe in the line with here at the start of it. Then the program
|
||
counter will fall through to the new instruction on the line Here
|
||
|
||
|
||
HERE BRA.S HERE
|
||
|
||
|
||
Which of course will just go around in a circle and never go any farther
|
||
and this is exactly what will happen if you single step the program. But if
|
||
you run the program with a break point on the instruction after the here
|
||
line. Where it would appear the program counter wont ever get to you
|
||
discover the program will stop on the break point not on the new
|
||
instruction BRA.S Here. Even though the instruction BRA.S here is there for
|
||
all to see. The reason is because it ran the instruction from the prefetch
|
||
buffer instead of prefetching the new altered instruction. I suggest you
|
||
set and run this is quite a dag of a thing to see. This is the reason that
|
||
some games wont run on the 68010 68020 68030. It is not because the game
|
||
wont run it is because they fail there own protection. It is quit a funny
|
||
thought to think there are hacked games out there that will run on 68010
|
||
68020 68030's because they have had the protection removed though the
|
||
originals wont
|
||
|
||
|
||
Combining Encryption and Prefetch
|
||
|
||
Now it starts to get complicated if you didn't understand the encryption or
|
||
the prefetch then go back and get to understand them or skip this bit. The
|
||
problem comes when you find a game with the code to be decrypted directly
|
||
after the decryption routine. You would normally just single step around
|
||
the loop a few times to get around it and pop in a break point as discussed
|
||
above. But if the routine is using the prefetch feature at the end of the
|
||
decryption routine then you cant single step the decryption routine at all.
|
||
We cant just pop a break point after the decryption routine and run it
|
||
either because we would be corrupting the code we are trying to decrypt.
|
||
What you normally find is they have the decryption routine directly before
|
||
the code to be decrypted and the loop that does the decryption isn't
|
||
completely there because the program self modifies the code to complete the
|
||
decryption loop (which it wont do if you single step because of the
|
||
prefetch feature). So What do you do. All you have to do is copy the code
|
||
that does the decryption to somewhere else in memory (use the I function in
|
||
Monam to copy it) and run the copy of the code instead of the real code to
|
||
decrypt the decrypted code. You have to make sure that the copy of the code
|
||
you have made still points at the original decrypted code. This is normally
|
||
not hard because it usually loads the values into registers and the
|
||
decryption routines are pretty small so they are normally position
|
||
independent. So the decryption routine will normally run anywhere in memory
|
||
after the registers values have been loaded. All you have to do then is run
|
||
the copy and place a breakpoint after the copy of the decryption routine
|
||
where the program counter would normally fall through after completing the
|
||
decryption.
|
||
|
||
|
||
PATCHING CODE
|
||
|
||
I will talk about simple patches then look at patching stuff that has been
|
||
encrypted.
|
||
|
||
|
||
Simple Patches
|
||
|
||
Ok you have found the bit in the program you want to change and you may
|
||
even be able to run the program up to the bit that needs changing, change
|
||
it by hand then go off and run and play the game. But how do make it
|
||
permanent. We cant just patch the program in memory and then save it back
|
||
to disk. Why cant you?. Ok when you load the program into Monam it pretends
|
||
to run it and stop it after the first instruction. This means the program
|
||
has used the relocation table on the end of the file to relocate it to run
|
||
at this address. We can't just patch the code and save the memory back down
|
||
because there is no guarantee it will load in the same place. Plus all the
|
||
hunks are spread around memory anyway. What you have to do is either use a
|
||
disk editor (such as filemaster) and alter it directly on the disk. To do
|
||
this you want to search the file for the bytes around where you want to
|
||
patch. Now you have to make sure you the bytes you are searching for are
|
||
not some bytes that will be changed when the file is loaded into memory
|
||
(such as absolute memory addresses etc). Changing the file directly on the
|
||
disk makes sure that you don't save the file down on the disk in a place
|
||
that might effect the game. You are normally pretty safe if the game uses a
|
||
sector or track it is normally allocated so it is unavailable. But of
|
||
course nothing is every guaranteed. The other method which is the method I
|
||
use most is to load the program up into Monam and alter it there. Dont load
|
||
the program normally, load it as a binary image by using the B function. To
|
||
do this run Monam and when it asks you for a Program name just press return
|
||
then Press B and enter the name of the program and it will be loaded. The
|
||
program is loaded but you can't run it or trace (because you haven't loaded
|
||
a runable program). You can disassemble it though which is good for finding
|
||
the place to put the patch and checking you have it right. Remember you
|
||
have loaded the complete program this includes the bit at the start and the
|
||
relocation table and symbol table if it has one. Remember it hasn't been
|
||
relocated so it will look a bit different to when you loaded the program as
|
||
a runable program. Ok use the E function of Monam to patch in the Hex
|
||
numbers of the commands you want to put in. There are only a few
|
||
Instructions you ever use such as Nop Rts Bra etc. If you don't know the
|
||
Hex numbers for the Instruction you can either set them in Genam and look
|
||
at the results from Monam or you can use the Instruction search from Monam
|
||
to find one in the Rom or the program and have a look at the Hex values
|
||
from there. After you have patched the code all you have to do is save the
|
||
program back down with the S option. The important thing to do is get the
|
||
exact start and the exact end of the file right which is made very easy by
|
||
using Monams M0 and M1 registers these get set to the value of the START
|
||
and END of the file when it is loaded into Monam so all you have to do when
|
||
Monam asks for the start and end is enter M0,M1 and it has to be write.
|
||
|
||
|
||
Things to note about simple patching
|
||
You have to patch somewhere that wont be altered by the relocation table.
|
||
Dont confuse Instructions that will be alter by the relocation table with
|
||
ones that wont. Just because the instruction disassembles with an address
|
||
in it dosn't mean that the address was put there by the relocation table. A
|
||
lot of instructions are position independent which means they will run at
|
||
any address. The way they do this is instead of saying branch to address
|
||
$12345 the instruction actually says branch 20 instructions ahead (or what
|
||
ever numbers). But Monam disassembles the code with addresses instead for
|
||
our convenience. The way to learn if it is an Instruction that will be
|
||
altered by the relocation table is to have a look at the instructions hex
|
||
dump and see if there is that address in it. All this must be done on a
|
||
program you have loaded in the normal way not just binary loaded. When you
|
||
come across a bit that has been altered by the relocation table and you
|
||
want to alter it there are a few ways around it. If you have something like
|
||
|
||
JMP $C57B96 4EF9 00C5 7B96
|
||
|
||
|
||
and you want to remove it from the program then you can't just put in three
|
||
NOP'S because when the relocation table alters the address where the
|
||
$C57B96 is it will alter the NOP'S instead and god knows what they will
|
||
turn into. Ok the way around this is to just alter the first two Bytes
|
||
(4EF9) to get the program to BRA 6 bytes ahead. You do this by putting a
|
||
6006 where the 4EF9 is you don't bother about the other 4 bytes they will
|
||
disassemble as rubbish but the program counter wont actually go through
|
||
them so it doesn't matter.
|
||
|
||
|
||
More Difficult Patches
|
||
|
||
We need more Difficult patches to get around various things such as
|
||
encryption. When the bit we want to patch is in a section of code that is
|
||
encrypted there is no general way to get around it you have to judge each
|
||
one separately, I can only give you a few examples of ways I have used in
|
||
the past. The first most obvious way is to work out what value the bytes
|
||
need to be so that when the program is decrypted it will end up being the
|
||
bytes that you want. For a simple decryption this is not as hard as it
|
||
sounds remember the guy that wrote the encryption would have used a
|
||
variation of the decryption routine you are looking at to encrypt it to
|
||
start with (remember I said if it is a simple decryption). You may be able
|
||
to work it out with a calculator or here is a method you could try. If you
|
||
decrypt the code from monam (discussed above) and patch the code to what
|
||
you want the code to say after it is decrypted. Then get out the charts of
|
||
what bits XOR's and OR's etc effect and see if you alter his decryption
|
||
routine back to an encryption routine. Now you put the program counter back
|
||
at the start of routine and run the routine again on the code (make sure
|
||
that the registers are Initialised to correct values etc). If you have
|
||
managed to do it correctly then the rest of the code will now be decrypted
|
||
back to it's original form. So all you have to do is find out what values
|
||
the bytes you are interested in are changed to then pop these values into
|
||
some undecrypted code and see if it will work. More than likely you will
|
||
come up against a decryption routine that does something like use the value
|
||
of the byte before it to help decrypt the next value. This of course stuffs
|
||
you for changing just a couple of values in the code because any changes
|
||
you make will effect the bytes after it and you would have to alter all the
|
||
code to be decrypted. One way around this I used a couple of times on the
|
||
ST in programs that encrypted a bit of code but the encryption wasn't to
|
||
complicated was to load the program up as a binary image and then run the
|
||
encryption code on its self. The way to do this is have a dummy file on
|
||
your disk consisting of just a START and an END or just any small program
|
||
and what you do is you load this program as a runable program (use Cntrl L)
|
||
and then load the file you are really interested in as a Binary file. You
|
||
can then move the program counter to the start of the decryption routine
|
||
and set any registers that need loading and run the decryption routine on
|
||
the binary image. When the decryption is complete you then patch the code.
|
||
Now all you have to do is Noble the decryption code so it doesn't try to
|
||
decrypt the code again when the program is run then save the program down
|
||
again. Yet another way get the program to decrypt and then patch it after
|
||
it has decrypted. This is of course a great Idea but as yet I haven't seen
|
||
a program that has the decryption code and a great big patch of NOP's with
|
||
the words "INSERT HACKING CODE HERE". But there are ways to get your code
|
||
executed with out reassembling the program. If the program is one that
|
||
loads code at an absolute address then there is all sorts of things you can
|
||
do you. If you can get some code into memory first then you can jump to it
|
||
and do anything such as decryption and patching and then jump back again.
|
||
This is very messy and you never know when you code is going to be covered
|
||
up by something else being loaded up. A much more elegant way to do it is
|
||
to have the block of code you want to jump to inside the program. There are
|
||
quite often heaps of strings you can hide code in. There is quite often a
|
||
title page with the Authors Name and the Gaming houses Name etc. You have
|
||
to look at the string and see how it is controlled. It normally just has a
|
||
Null (0 Hex) at the end of it and if you move this closer to start of the
|
||
string it will still print what is between the start and the Null but leave
|
||
you all the bytes from the Null to where the real Null is to put code in.
|
||
It may have a CR (0D hex) or it may the number of bytes long the string is
|
||
stored in front of the string so you just make it smaller. It is quite
|
||
surprising just how much code you can get into a string especially if it is
|
||
an 60 char long copywrite message. Ok this string or spare bytes of some
|
||
description has to be in the same hunk as the code you want to play with
|
||
and within reach of a relative branch etc to call it and all the code must
|
||
be position independent because it wont be updated by the relocation table.
|
||
So the program counter is running along happily setting up the game and it
|
||
comes to the decryption routine which you have altered. You could if you
|
||
have enough room have the hole decryption routine hidden somewhere and have
|
||
it decrypt the code and patch it before relative branching back to just
|
||
after the decryption routine to get on with the real code. More than likely
|
||
you wont have enough room and you probably just have altered the bottom
|
||
couple of lines of the decryption routine (the bit that sends the program
|
||
back around again to decrypt more code) to branch off to your code where
|
||
the first thing you will do is any instructions that you have had to patch
|
||
over to get the program counter to branch to your code. You of course just
|
||
keep sending the program counter back to the decryption routine until the
|
||
code is all decrypted then do the patch. Sometimes you find messages to
|
||
hackers in the code that are a good place to put extra code. Please see the
|
||
example below to clear up what I am talking about.
|
||
|
||
************************************************************************
|
||
ORIGINAL CODE
|
||
************************************************************************
|
||
LOOP DECRYPTION CODE 1
|
||
DECRYPTION CODE 2
|
||
DECRYPTION CODE 3
|
||
DECRYPTION CODE 4
|
||
DECRYPTION CODE 5
|
||
IF NOT FINISHED GOTO LOOP
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
|
||
The Next bit is a string somewhere in memory
|
||
|
||
STRING
|
||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||
|
||
|
||
|
||
|
||
***********************************************************************
|
||
PATCHED CODE
|
||
***********************************************************************
|
||
|
||
LOOP DECRYPTION CODE 1
|
||
DECRYPTION CODE 2
|
||
DECRYPTION CODE 3
|
||
DECRYPTION CODE 4
|
||
PATCHED CODE JUMP TO STRING
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
CODE TO BE DECRYPTED
|
||
|
||
The Next bit is the patched string
|
||
|
||
STRING
|
||
DECRYPTION CODE 5
|
||
IF NOT FINISHED GOTO LOOP
|
||
CODE TO PATCH DECRYPTED STUFF
|
||
JMP TO START OF THE DECRYPTED CODE
|
||
|
||
|
||
|
||
|
||
Checksums
|
||
|
||
Sometimes I am amassed how few games actually check to see weather the code
|
||
has been altered in any way. I doubt if it is because they are scared of
|
||
drawing attention to the code that needs changing. It is actually very hard
|
||
to checksum code and reliably get the same answer because the program loads
|
||
where ever it feels like and then relocates so the total can very wildly.
|
||
But I have seen programs that load up chunks of code to absolute addresses
|
||
so it will always be the same and no relocation is necessary and still they
|
||
dont bother to run a quick checksum later in the game to check if all is
|
||
well. Anyway How do you get around checksums? Well the obvious way is to
|
||
find where is does the checksum and noble it. But this can be a real pain
|
||
because it is normally quite a way into the game. There are a couple of
|
||
ways around them. If it is just counting up the bytes you can sometimes
|
||
alter some bytes around it so the total comes out to be the same, don't
|
||
forget it could be adding bytes, words or longs. More than likely if they
|
||
have any sense they will be using some routine where position of the bytes
|
||
is important as well and just modifying something else wont be good enough.
|
||
There is a good way around this. Normally it will only be totalling a small
|
||
bit of the program which is position independent. So what you do is change
|
||
the program back to its original state before the checksum is done. For
|
||
example
|
||
|
||
|
||
CODE
|
||
CODE
|
||
CODE
|
||
BSR Do_Protection
|
||
CODE
|
||
CODE
|
||
CODE
|
||
|
||
|
||
in the above example what we want to do is NOP out the BSR Do_Protection
|
||
instruction but this will cause the checksum to fail so what to do is
|
||
change the code to
|
||
|
||
|
||
CODE
|
||
CODE
|
||
JMP TO_MY_CODE_HIDDEN_IN_A_STRING
|
||
CODE <- COME BACK HERE
|
||
CODE
|
||
CODE
|
||
|
||
Now the code that you have HIDDEN in a string does nothing but restore the
|
||
program to it's original form and then jump to the line with the left arrow
|
||
after it. The effect of this is the same as the BSR Do_Protection being
|
||
NOP'ed out but the checksum is still the same. This is of course a simple
|
||
example you could do heaps of things in the code in the string.
|
||
|
||
|
||
Programs that boot from a boot block
|
||
|
||
These seem at first to be very hard programs to hack but they don't have to
|
||
be. Programs that start from a boot block dont use the filing system to run
|
||
the game they load the game directly from disk. The program handles which
|
||
track and sectors to load etc. The way to see what is going on in the boot
|
||
sector is to use a Disk Monitor of some sort. It doesn't matter how funny a
|
||
disk is you will always be able to load the boot sector. I make mention of
|
||
this because most programs that boot from a boot sector will run fine if
|
||
you just boot off them but if you boot of a system disk and then stick the
|
||
game into the second drive the machine will report the disk as stuffed and
|
||
recommend disk doctor. This is because when you stick a disk into the Amiga
|
||
it goes away and says hello to it and on these funny games disks it finds A
|
||
lot missing so it reports it stuffed. So just keep clicking on cancel and
|
||
then load up the first track. I use DiskMonPro the main reason I use
|
||
DiskMonPro is because it tells you where in memory it has the track.
|
||
DiskMonPro has a monitor program built in but I normally run Monam in the
|
||
background and jump to that after loading the track (or just the boot
|
||
sector). You disassemble the boot sector and have a general look around.
|
||
Save the boot sector down to a disk (remember it is two sectors). We dont
|
||
have to worry about relocation tables with the boot sector because it has
|
||
to be position independent code to be in the boot block so we can load it
|
||
anywhere. I normally reboot to clean up the memory and then load monam.
|
||
What we want to do is to run the boot sector to do this we use the method I
|
||
discussed in "MORE DIFFICULT PATCHES" which is load a small dummy file as a
|
||
runable file then load the boot sector file as a binary file and move the
|
||
program counter to the start of the code in the boot sector (which is a 12
|
||
decimal instructions from the start of the boot sector). Now put the disk
|
||
you want to hack in DF0:. You will probably have to press cancel a couple
|
||
of times to stop it complaining about the disk. Now you just start single
|
||
stepping the boot block as if it was a program. There are two problems with
|
||
single stepping a boot block the first is that when a boot block is being
|
||
booted while playing a game the machine is in supervisor mode. This itself
|
||
isn't a problem if the boot block is well behaved you can run it in user
|
||
mode and get the same results. The problem is it doesn't have to be well
|
||
behaved and if the programmer feels like taking over the machine completely
|
||
you normally are the first thing to stop working. The other problem is
|
||
programmers who make games boot from a boot block and especially the ones
|
||
who take over the machine completely and kickout the operating system drive
|
||
the hardware directly are normally bloody good programmers and tend to make
|
||
it tough for you. So you just keep single stepping the game as if you were
|
||
working on a program you had loaded up. But instead of it loading other
|
||
files it will be loading tracks etc into memory. Once the game has got far
|
||
enough into the program to load up the appropriate track and do the
|
||
protection you note what track it is. You then load up the track with
|
||
DiskMonPro and alter the bytes and save it down again. If you dont know
|
||
what track you are looking at you can use a disk monitor program that will
|
||
let you search the disk for the bytes to find out where it is.
|
||
|
||
|
||
Disks with funny formats
|
||
|
||
These disks are easy to identify they are the ones that wont copy with
|
||
Xcopy Doscopy command. So you have to copy them with the nibble function of
|
||
Xcopy. These disks cause all sorts of problems for us. You cant just load a
|
||
program to have a look at it because everything is loaded from tracks but
|
||
you cant just load up tracks with DiskMonPro and have a look at it from
|
||
Monam because the disk has a funny format that has to be loaded using the
|
||
games loader. The biggest problem is once you have found what you want to
|
||
change and you may even know its exact position on the disk but you cant
|
||
change it because you have to write the funny track down to the disk. You
|
||
could in theory reverse engineer the loading routine and write one to save
|
||
the track down again. This could be pretty easy if you discover that all
|
||
the program is using is a different sync byte but this is not normally the
|
||
case. The other way to do it is to let the program load the track and then
|
||
patch it in memory. To do this you have to use the techniques discussed in
|
||
the patching bits. The big problem comes when the boot sector doesn't load
|
||
the bit you want. Instead it loads a file which it runs and then that loads
|
||
another file which it runs and then loads the bit that you want to patch. I
|
||
had this when I hacked Midwinter you have to patch each lot of tracks that
|
||
are loaded to patch the next lot and the last one patches the protection.
|
||
The best way to do this is to put all the patching code in one block and
|
||
pop it in the boot block and have it relocate to a safe area in memory.
|
||
Then you only have to handle patching the files not trying to hide the
|
||
patches in strings. So the boot sector gets loaded relocates your block of
|
||
patches then loads the first track and jumps to the first of our patches
|
||
which then turns around and patches the first track at the memory location
|
||
after the routine that loads the next lot of tracks. It patches it to jump
|
||
to our second patch. It then jumps to the start of the first track and
|
||
executes all it code including loading the next lot of tracks and hits our
|
||
patch that sends it to the second patch. The second patch patches the next
|
||
lot of tracks after there load routine. You keep doing this until you get
|
||
to the lot of tracks that have the protection in them and patch the
|
||
protection. Midwinter was a dag it did this three time and also checksumed
|
||
the code so I had to restore the file to original condition for the
|
||
checksum to calculate correctly. It was a fun game to hack especially to
|
||
get my Name into but I never did work out how to play it properly.
|
||
|
||
|
||
MISCELLANEOUS OTHER STUFF
|
||
|
||
Resetting after loading
|
||
|
||
This is a good way to see code after it has been decrypted and to have a
|
||
look at code that has come off a special format disk. It can also be the
|
||
only way to hack a game that you dont have enough memory to run with Monam.
|
||
What you do is reset the computer and load up Monam and have a look at what
|
||
is left in memory (I am afraid the ST clears all the available memory when
|
||
any program is loaded so it is gone). If you find Monam or the operating
|
||
system is loading over the code you want to look at then when you boot up
|
||
you can move the bottom 40k up to 40000 hex with a boot block called Multi
|
||
Boot (the version I have doesn't work with 1.3) you can always put the code
|
||
in a boot block yourself (Multi Boot does lots of other things as well.
|
||
|
||
|
||
Bra Here
|
||
|
||
Quite often you are looking through some code and you come across "Branch
|
||
if_whatever_instruction" and you want to know the result but the program
|
||
kicks Monam out or loads all the files in the world and you run out of
|
||
memory. One way to find out is to put the hex numbers
|
||
|
||
|
||
60FE
|
||
|
||
|
||
These are the hex numbers to make the program counter loop to it's self.
|
||
This can be very handy when use in conjunction with Resetting the machine.
|
||
If you run the program and it hangs you know which way it went when it hit
|
||
|
||
|
||
Bye Bye
|
||
I have really only just scratched the surface of hacking. I have confined
|
||
myself to mainly tracing from monam and haven't talking much about other
|
||
programs such as Resource (a disassembler program with balls). If have
|
||
access to a BBS there is a good chance they will have the demo version of
|
||
Resource on it. Download it and have a look at it. There is various things
|
||
you can do with XOPER. I spoke about using the hunks in the section (The
|
||
program running another program). You can also use the stacks command to
|
||
help find out where a program has been by backtracing the tasks stack and
|
||
looking at the addresses that have been pushed onto the stack by BSR and
|
||
JSR calls. You take the stack pointer lower (SPlower) and add the amount
|
||
reserved for the stack (SIZE) (note it is in decimal) then take away the
|
||
amount used (USED). This should give you the position of the stack pointer
|
||
of a frozen task. You now start having a look at the memory around this
|
||
address in hex and looking for intelligent things. There are various
|
||
hardware add-ons you can get to make hacking alot easier. I think these
|
||
take all the sense of achievement out of hacking a game. I hope this
|
||
article has helped anybody starting out wanting to hack programs.
|
||
|
||
Happy Hacking!
|
||
|