📄 roll engine.shp
字号:
As you see, it works just like the SUB instruction.
Then there are also those instructions that only have 1 parameter like inc and dec.
example:
inc eax :increase the value at eax with 1
dec ecx: decrease the value of ecx with 1
dec [ebp]: Decrease the value stored at the address pointed to by ebp with 1.
Right now i've only shown the 32-bit registers (eax, ebx ecx....) but there are also 16-bit register and 8-bit registers that can be used.
the 16 bit registers are: AX,BX,CX,DX,SP,BP,SI,DI
the 8 bit register are: AH,AL,BH,BL,CH,CL,DH,DL
Note that when changing ah or al you'll also change AX, and if you change AX you'll also change EAX, same goes for bl+bh+bx+ebx,ch+cl+cx+ecx,dh+dl+dx+edx
You can use them almost the same with the instructions for 32 bit but they will only change 1 (8 bit) or 2(16-bit) bytes, instead of 4 (32-bit) bytes.
example:
dec al :decreases the 8 bit register al
sub [esi+12],al :decreases the 1-byte value stored at the location esi+12 points at with the value of al
mov al,[esi+13]:places the 1-byte value stored at the location esi+13 points in the al register.
Note that it is IMPOSSIBLE to use a 16 or 8 bit register for instructions that point to an address. eg: mov [al+12],0 will NOT work.
There are also 64 and 128 bit registers, but I wont discuss them since they are hardly ever used, and cant be used with the other instructions that also work with 32 bit)
Then there are the JUMPS, LOOPS, and CALLS:
JMP:
The JMP instruction is the easiest it changes the Instruction Pointer (EIP) to the location the JMP instruction points at and continues from there.
There are also conditional jumps that will only change the instruction pointer if a special condition has met. (for example set using the compare instruncion (CMP))
JA=Jump if Above
JNA=Ju,p if not above
JB=Jump if below
JE=Jump if equal
JC=Jump if carry
and LOTS of other conditional jump
LOOP:
The loop instruction also points just like the JMP to a memory location, but only jumps to that location if the ECX register is not 0.
and of course, there are also special contitional loops:
LOOPE:Loop while ecx is not 0 AND the zero flag is not set
LOOPZ:same as LOOPE.
LOOPNE:Loop while ECX is not 0 AND the zero flag is set.
LOOPNZ:Same as LOOPNE
I gues I should also explain what flags are, they are bits in the processor that can be used to check the condition of a previous instruction like 'cmp al,12' if al=12 then the zero flag (ZF) will be set to true, else the Zero flag(ZF) will be set to false.
CALL:
Call is the same as JMP except it uses the stack to go back.
Explenation of the stack:
The stack is a location on memory pointed at by the ESP register.
You can put values in it using the PUSH command, and take out it using the POP command. If you use PUSH it will decrease the ESP register and place the value at the location of ESP. If you use POP it will place the value pointed at by pop into the location pointed at by the parameter of POP and increase the value of ESP. In short: The last thing you push in the stack will be the first thing you pop from the stack, the 2nd last item in will be the 2nd item out.
RET:
After CALL has pushed the location of the next instruction onto the stack it jumps to that location. (sets the instruction pointer to that location)
After a while it will encounter a RET instruction, and will then jump to the location that is stored in the stack. (Call pushed the location in the stack, ret pops it out again and jumps to that location)
And thats the tutorial on the basics of assembler, if you have questions about assembler and stuff just ask and I'll try to answer.
Nice file to check out if you want more info:
http://podgoretsky.com/ftp/Docs/Hardware/Processors/Intel/24547111.pdf
note: It's really usefull to understand how those values between brackets work, because then you can make the most use of the pointer stuff in CE 4.1 (It will remove for most games the Dynamic Memory Allocation problem for most games, if you know how how to look at the assembler code that accesses the values you found)
------------------------------------------------------------------
The "flags" are a set of bits stored in a special register. If the bit is "1" the flag is said to be set, and if it's "0" then the flag said to be "clear". Collectively, the flags tell you all about the processor's internal status and gives more information about the results of previous instructions.
There are three types of flags: Status flags that tell you about the results of the last instruction, Control flags that tell you how the processor will behave, and System flags that tell you about the environment your program is executing it.
The flag register is 32 bits: (S=Status flag, C=Control flag, X=System flag)
Code:
0 S Carry
1 (Reserved)
2 S Parity
3 (Reserved)
4 S Auxiliary Carry
5 (Reserved)
6 S Zero
7 S Sign
8 X Trap
9 X Interrupt Enable
10 C Direction
11 S Overflow
12 X I/O Privilage (bits 12&13)
13 X
14 X Nested Task
15 (Reserved)
16 X Resume
17 X Virtual 8086
18 X Alignment Check
19 X Virtual Interrupt
20 X Virtual Interrupt Pending
21 X Identification
22 \
23 |
24 |
25 |
26 |_ (Reserved)
27 |
28 |
29 |
30 |
31 /
Let's go over the status flags, since those are used most often.
Overflow:
When an operation (Addition, subtraction, multiplication, etc) produces a result that is too big to fit in the register (or memory location) used, the Carry flag is set. (If not, it's cleared automatically) For example, if you're using a 16 bit register and your operation produces a value that won't fit in 16 bits, the carry flag is set.
Sign:
Set if the result is negative, cleared if positive. This is typically a mirror of MSB (most significant bit) of a value.
Zero:
Set if result is 0.
Auxiliary Carry:
Similar to Carry, but it will treat the register/memory location as 3-bits instead of 8, 16 or 32. This is used for BCD (Binary coded decimal) stuff and it generally pretty useless otherwise.
Carry:
The carry flag is set if the bit one past the lmit of the register/memory location would have been set. For example, mov al, 0xFF then add al, 1 will cause a carry because the 9th bit would have been set. Also note that the overflow and zero flags would be set and sign flag cleared, too!
----- SHM ---- page 26 ----
Originally posted by scribly:
Lets say you've found the code that decreases your health
Problem is that it seems to also affect the health of your opponents, and somehow you can't seem to find the basepointer.
In those cases doing some code injection using CE's auto assembler is the easiest solution
There are several ways code injection can help solve this problem.
One method is finding another code that accesses your health, but does it only for your own health. (e.g the code that is used to display your current health)
There inject some code that stores the address of your health to a address you can find back again (more on that later)
Then in the code that decreases your health inject some code that checks if the address changes is the address stored by the other routine, if it is, skip the code, otherwhise, just decrease the health, or even create a instant kill cheat by decreasing the health of opponents by a really big value.
Now for the auto assemble script part:
lets say that at 00405000 is the code that reads your health: mov eax,[esi+ecx*4]
and at 00421000 is the code that decreases the health of enemies: mov [esi+ecx*4],edx
First allocate some memory to place your code in, and to allocate space for variables, use alloc(name,size) for that.
Alloc allocates a region of memory with read,write and execute access. (be aware, this wont work in windows me, 98 or 95 so please upgrade if you are using those ancient os's)
So:
Code:
alloc(injectHealthReader,1024) //creates a identifier called injecthealthreader that points to a block of 1024 bytes
alloc(injectHealthWriter,1024) //2nd code cave to handle the code of the decrease health code, for easy management
alloc(playerhealthaddress,4) //this will hold the address of health, a 4 byte value (pointer, in 64 bit this'll have to be 8 bytes)
Now, write your injecthealthreader routine to store the address, and place a jump to your original code as well. dont forget that when placing a jump to your code cave keep in mind that if the instruction was larger than a jump to nop the remaining bytes, and in case of the jump being bigger than the instruction also save the other instruction, AND nop incomplete bytes. Jumps are 5 bytes.
So in the case of "mov eax,[esi+ecx*4]" the bytecode is 8b 04 8e (3 bytes) so you'll also need to save the instruction after it. Let's say it's followed by a "mov ecx,[esi+edx*4+4]" , bytecode=8b 4c 8e 04 (4 bytes), so now we have 7 bytes to place our jump, thats more than enough. So we'll have to place 2 nops after the jump (7-5=2)
Code:
00405000:
jmp InjectHealthAddress //jump to the codecave
nop //nops for the lost space
nop
returnHealthReader: //this is the label that is used to return to the address (so you dont have to write down 00405007 when jumping back, just to make it easy....)
injectHealthReader:
push eax //save eax, not really needed here since eax gets changed anyhow, but it's a good habbit to save and restore registers
lea eax,[esi+ecx*4] //this instruction places the result of esi+ecx*4 into eax
mov [playerhealthaddress],eax
pop eax //restore the register, again, not needed here, but good habbit to do
originalhealthreadercode: //label defining the original code, can be used to jump into or just skip, not needed here
mov eax,[esi+ecx*4] //read health
mov ecx,[esi+edx*4+4] //read something else, my gues, armor
jmp returnHealthReader //jump back to the original game code, when done successfull, it wont crash...
As you see to specify a exact address just type it in ended with a ':' . Everything you type in after that will get assembled on and after that address (the jump and nops in this case, and the definition of the returnHealthReader:, which is in it's own turn also a address specifier, but doesn't change the current address)
This code introduces labels, they are basicly identifiers specifying a address where thay have been placed.
you can't just use a label though, you'll first have to declare it using the label(labelname) function. I usually declare labels right after the part where I alloc memory.
so, right after the alloc I have this code to get the code above working:
Code:
label(returnHealthReader) //tell the assembler than returnHealthReader is a valid identifier, so dont bug out
label(originalhealthreadercode) //same as above
now when you run the game the address of your health will get stored into the location of playerhealthaddress.
You can already use this with cheat engine, because the auto assembler will tell you the address, but the allocation will change each time, so making a table for other people won't work, they'd have to fill in the address each time themselves. (I dont find that a problem but somehow some people do....)
So, let's automate it a little further and use the knowledge of your healthaddress to make yourself invulnerable, but not your oponents.
Just like the injection for the code that reads your health you can do the same for that that decreases health.
And you can put it in the same script as the injection for the reader, as you saw in the alloc part where I already allocated space for the injection for the health decreaser (so you dont have to edit the address the other script allocated)
so, write scriptcode that places a jump over the code that decreases your health, in this case "mov [esi+ecx*4],edx" which has bytecode 89 14 8e (3 bytes), too small, so find a instruction before or after thats also suitable, in this case there's a sub edx,eax just before this instruction, its bytecode is 29 c2 (2 bytes) so a perfect fit (5 bytes, so no need to nop), and even easier to make a code injection for (else I'd have to use a label between the 2 instructions to just skip the original line, but this sub edx,eax line is used to decrease as well, and doesn't change any of the locator addresses, and useless for the rest, so it doesn't hurt to skip it as well)
so to inject your routine that checks if this is your health or not and if so, dont decrease do:
Code:
00421000:
jmp injectHealthWriter
returnHealthWriter: //just declare it here, it'll get address 00421005, so a jmp returnHealthWriter will get converted to jmp 00421005
injectHealthWriter:
//do a check if esi+ecx*4 matches the address stored in playerhealthaddress
//if it matches, skip the original code, if it doesn't just execute it
//save the registers we use, and before I forget, do not touch esp between saving and restoring the registers unless
//it's to read something(like parameters), in which case you'll have to adjust the offset
//also, dont change the registers that you use to find the address
push eax
push ebx
mov eax,[playerhealthaddress]
lea ebx,[esi+ecx*4]
cmp eax,ebx
je itstheplayer
//not the player
pop ebx //I think I could have doen this before the je, but better safe than sorry
pop eax
jmp originaldecreasehealthcode
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -