Making a Mass Kill
Target:
Crimsonland version 1.9.8
http://www.crimsonland.com
Tools:
-Softice
-Tsearch 1.6b (http://fly.to/mtc/)
-Code Cave Chaos (http://www.s-i-n.com/chaos/)
Objectives:
With One hit kill hacks being all the rage these days, we will take it a
step further and turn a one hit kill into a kill all mass killer.
Begin:
The first step is that we have to find the routine that deals with the
enemy health. A good idea would be to get the DOCTOR perk (0x48E6C0 =
1), which allows you to see enemy health by holding your mouse cursor
over them. The problem at the beginning of the game is that enemies die
off too quickly to be able to search for health, so wait until level 1.5
( Alien Dens ) and shoot at those big red white enemy spawners.
Doing the standard dec / inc
search you should have come up with something in the 499xxx range, then
put a breakpoint (bpm w) / autohack it.
You should pop up at the monster health routine:
:004200DD D99E34944900 fstp dword ptr [esi+0x499434]
--- side note ---
The fst and fstp instructions copy the value on the top of the floating
point register stack to another floating point register or to a 32, 64,
or 80 bit memory variable. When copying data to a 32 or 64 bit memory
variable, the 80 bit extended precision value on the top of stack is
rounded to the smaller format as specified by the rounding control bits
in the FPU control register.
The fstp instruction pops the value off the top of stack when moving it
to the destination location. It does this by incrementing the top of
stack pointer in the status register after accessing the data in st(0).
If the destination operand is a floating point register, the FPU stores
the value at the specified register number before popping the data off
the top of the stack.
Executing an fstp st(0) instruction effectively pops the data off the
top of stack with no data transfer. Examples:
fst mem_32
fstp mem_64
fstp mem_64[ebx*8]
fst mem_80
fst st(2)
fstp st(1)
The command at 004200DD is just basically storing a number from the
stack at the address of esi+0x499434.
--- end side note ---
Take a look at the registers and jot down some notes. F5 out of Softice
and attack a different monster. Try to understand what is going on in
those registers when you get popped back into SI due to attacking the
monsters. You will also see at the far right that DS: holds the memory
address of what you are currently attacking, then the number after the
equal sign is the value of their health in hex.
--- side note ---
If you want to convert it to
a dec type:
? xxxxxxxx ; where xxxxxxxx is the number after the equal sign. You will
notice that it is a large value like 1140457472, this is because it is
in DWord (4 bytes) format. To get the float version of it, either change
it from 4 Bytes to Float in TSearch or divide it by 2280914.944
--- end side note ---
You will see the following pattern as you go through a bunch of times:
EDI ESI
00 00
01 98
02 130
03 1c8
04 260
05 2f8
06 390
It can be inferred that when monsters spawn, they are assigned an ID
number based on the order in which they were spawned. Also
each Monster struct is 0x98 bytes apart in memory.
--- side note ---
A struct (structure) is simply a way of grouping several variables under
one name like below:
struct Monster
{
float xposition;
float yposition;
float health;
float speed;
};
//example usage Monster.health=100;
--- end side note ---
Now that we know what is going on at
:004200DD D99E34944900 fstp dword ptr [esi+0x499434]
we want full control of what happens in this function. So now it is time
to jump to a code cave and rewrite the function to fit our needs.
Use Code Cave Chaos to find an empty code cave.
For this project I chose to put
my stuff @ 0x46cb58.
Load up TSearch and open up the
Easy Script section, its time for some coding fun. Make sure you have
the latest version of TSearch which supports labels inside of EasyScript.
//-- code start
---
// first we must create the jump to our code so do the following:
offset 004200dd
//jumps to my function
jmp @myfunction
//gets rid of the extra byte that was left over from
//destroying the original fstp dword...
nop
//creates a label to jump back to.
@home:
//next we must
create @myfunction
offset 46cb58
//create a label to jump to by name rather then offset
@myfunction:
//recreates the instruction that we destoryed
fstp dword ptr [esi+0x499434]
//jumps back home to the instruction after our created jmp
jmp @home
//-- code end ---
So with that code above we
created a jump to our code cave, which just recreated the original fstp
instruction. At this point nothing has changed except that when you
attack a monster, the game now goes through our function before
returning back to the games own code.
So now let us add some code
that will cause the current monster's life to be set to 0 when it passes
through our function.
!GREEN COLOR INDICATES NEW CODE THIS TIME AROUND!
!BLUE COLOR INDICATES STUFF THAT WAS
DEACTIVATED OR DELETED!
!NEW COMMENTS ARE THIS COLOR!
//-- code start
---
// first we must create the jump to our code so do the following:
offset 004200dd
//jumps to my function
jmp @myfunction
//gets rid of the extra byte that was left over from
//destroying the original fstp dword...
nop
//creates a label to jump back to.
@home:
//next we must
create @myfunction
offset 46cb58
//create a label to jump to by name rather then offset
@myfunction:
//recreates the instruction that we destoryed
//we are going to replace
this with a mov instruction
//fstp dword ptr
[esi+0x499434]
//move the value of 0 into
the pointer, thus killing current monster
mov dword ptr
[esi+0x499434], 0x0
//jumps back home to the instruction after our created jmp
jmp @home
//-- code end ---
Okay, there you have it. When a
monster is attacked, its health will be replaced with 0 and it will die.
However this is not good enough, we would like to be able to kill all
monsters at once. The monster's health pointer is [esi+0x499434], so if
we were able to go through the whole list of created monsters and
replace their health with 0, then they all would die when one was
attacked.
Remember how I made you pay
attention to those two registers? EDI & ESI? Now we are going to use
them in a loop to help point to the proper memory address of the
monster's health and then we will write a 0 into it to kill them.
//-- code start
---
// first we must create the jump to our code so do the following:
offset 004200dd
//jumps to my function
jmp @myfunction
//gets rid of the extra byte that was left over from
//destroying the original fstp dword...
nop
//creates a label to jump back to.
@home:
//next we must
create @myfunction
offset 46cb58
//create a label to jump to by name rather then offset
@myfunction:
//pushes all registers so
that they are backed up
//on the stack so we can pop them back later so that we have no
//adverse effects on the game
pushad
//set esi to 0
xor
esi, esi
//set edi to 0
xor
edi, edi
//creates a label so that
we can jump back to it by name
//when we need to loop back through our little function again
@loop:
//move the value of 0 into the pointer, thus killing current monster
mov dword ptr [esi+0x499434], 0x0
//Since ESI*EDI*0x98+0x499434 = current monster's hp
//load effective address is the most efficient way of doing this.
//ESI now equals esi+0x98
lea esi,
dword ptr [esi+0x98]
//increases EDI by 1, thus
pointing to the next monster
inc edi
//compares edi and 300, which is
how many times it should loop
//this accounts for all the monsters that could ever spawn in the
//game at once. I choose 300 cause 500 crashed the game =P
cmp edi, 0x12c
//if edi is not yet at its 300th
run through the loop then go to the
//code at the label @loop: (LOOK UP!).
jne @loop
//pop all
registers so that they are at the value they were at right before
//we started messing with them.
popad
//jumps back home to the instruction after our created jmp
jmp @home
//-- code end ---
So this time around we backed up the registers
so that we could reuse them at the end of our function, just incase we
modified any of them during our code.
Then we created a loop that looped 300 times, increasing the value of
EDI until it reached 300 - all the while writing 0 to the health address
of each creature as it goes through the loop.
Conclusion:
For the most part games store players and enemies an arrays of structs
so once you find a pointer for health you can do some math and go
through memory and write new values there.
In the case here, EDI (0,1,2,...) * ESI (0x98) + 0x499434 pointed to the
health of the current monster. We modified it so that it would go
through and write 0 to each Monster.health address. When a monster is
attacked by the player, the game jumps to our code and writes 0 to all
currently spawned monsters - killing them all for one. The only glitch
is you only get exp for what you kill with your bullet, but that's a
story for another time.
DOWNLOAD COMPILED
VERSION OF CODE
DOWNLOAD EASYSCRIPT
VERSION OF CODE
//--FINAL CODE BELOW--//
offset
004200dd
jmp @myfunction
nop
@home:
offset
46cb58
@myfunction:
pushad
xor
esi, esi
xor
edi, edi
@loop:
mov
dword ptr [esi+0x499434], 0x0
lea
esi, dword ptr [esi+0x98]
inc
edi
cmp
edi, 0x12c
jne
@loop
popad
jmp
@home
^chaos^
idxchaos@hotmail.com
http://www.s-i-n.com/chaos/
^chaos^
November 15th, 2003
chaos world (c)
2003
Do not distribute as
your own
|