|
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
thenic How do I cheat? Reputation: 0
Joined: 13 Apr 2022 Posts: 6
|
Posted: Wed Apr 13, 2022 3:45 pm Post subject: Auto Assembly code crashes game (fstp dword pointer) |
|
|
Hey there,
I'm trying to hack a kids game (more than 20 years old and offline, just for nostalgic reasons for a friend of mine and myself so no doubt that it's anti-cheat free) and thanks to this forum and the Wiki examples, I thought I wrote a working code.
But I was wrong.
I found a reliable pointer for in-game fuel but I fail at Auto Assembly implementation.
Data type is float (default 25.0, needed 100.0).
My assumption was that it's enough to redirect the original line "fstp dword ptr [esi+00000198]" to a new allocation where float 100 is written to the (cleared) st(0) register and after that I can go on with overriding the value of esi+offset by using the original fstp pointer command. But the game crashes as soon as I enter the area in-game where the value is accessed.
Do you know what may cause the crash on access? Wrong memory sizes? Have absolutely no idea ...
Code: | [ENABLE]
alloc(Patch,512)
label(ReturnLoc)
define(bytes, D9 E9 98010000)
registersymbol(Patch)
Patch:
fstp st(0)
mov dword ptr [esi+00000198],(float)100.0
fstp dword ptr [esi+00000198] // ORIGINAL LINE
jmp ReturnLoc
0040F5C5:
jmp Patch
db bytes // ORIGINAL BYTES
ReturnLoc:
[DISABLE]
dealloc(Patch)
0040F5C5:
fstp dword ptr [esi+00000198] |
Thanks in advance,
Nico
Last edited by thenic on Tue Apr 19, 2022 12:30 pm; edited 2 times in total |
|
Back to top |
|
|
ParkourPenguin I post too much Reputation: 140
Joined: 06 Jul 2014 Posts: 4299
|
Posted: Wed Apr 13, 2022 4:47 pm Post subject: |
|
|
You're still executing the original code. This means your code injection is popping two values off the FPU stack - one more than the original code at the injection point.
Comment the original code out or remove it entirely.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
|
thenic How do I cheat? Reputation: 0
Joined: 13 Apr 2022 Posts: 6
|
Posted: Thu Apr 14, 2022 7:57 am Post subject: |
|
|
Alright, thank you ParkourPenguin, I just tried it with
Code: | //fstp dword ptr [esi+00000198] // ORIGINAL LINE |
but the game still crashes.
Just for my understanding:
fstp st(0) | Writes the value of st(0) into st(0) and pops out and removes the value of st(0), so it just clears st(0)?
mov dword ptr [esi+00000198],(float)100.0 | Writes the value "100.0" directly to the esi offset 00000198 without using the FPU st registers?
Also I will try to write the corresponding 4 byte array to esi+00000198 instead of the (float)100.0, maybe the game expects this.
Float is just my interpretation since it's a useful representation (25.0, 100.0 etc.) and because of the use of FPU/fstp.
|
|
Back to top |
|
|
ParkourPenguin I post too much Reputation: 140
Joined: 06 Jul 2014 Posts: 4299
|
Posted: Thu Apr 14, 2022 11:26 am Post subject: |
|
|
Your understanding is correct.
Does that instruction access other addresses? Right click the original instruction in the disassembler and select "Find out what addresses this instruction accesses". If it does, see step 9 of the CE tutorial.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
|
thenic How do I cheat? Reputation: 0
Joined: 13 Apr 2022 Posts: 6
|
Posted: Sat Apr 16, 2022 5:44 am Post subject: |
|
|
Thank you very much.
I still stuck at Level 9 (managed to pass until 8 without skip) but I got a working code for my problem now:
I missed the code template feature before but it is great.
Rewriting the esi+198 value isn't helpful apparently but the corresponding ebp+198 later in the code is much better to replace.
Code: |
alloc(newmem,2048)
label(returnhere)
label(originalcode)
label(exit)
newmem:
mov [ebp+00000198],(float)100.0
originalcode:
//fld dword ptr [ebp+00000198]
exit:
jmp returnhere
"mullemeck.exe"+11AB0:
jmp newmem
nop
returnhere: |
Thanks and have a nice easter weekend,
Nico
|
|
Back to top |
|
|
thenic How do I cheat? Reputation: 0
Joined: 13 Apr 2022 Posts: 6
|
Posted: Tue Apr 19, 2022 12:41 pm Post subject: |
|
|
Hello once again, my last goal is to multiply the flight speed by a predefined value (e. g. 2).
I already found the pointer and I found an opcode where I think I can inject the operation without being ignored or crashing the game:
Code: | 1002B574 - D8 4B 60 - fmul dword ptr [ebx+60]
1002B577 - D9 5C 24 1C - fstp dword ptr [esp+1C]
1002B57B - D8 4B 64 - fmul dword ptr [ebx+64] <<
1002B57E - 89 08 - mov [eax],ecx
1002B580 - 8B 4C 24 1C - mov ecx,[esp+1C]
EAX=00A4D584
EBX=00A4D4C8
ECX=C0CD5F84
EDX=00A4D53C
ESI=0019FB70
EDI=0019FBC8
ESP=0019FB40
EBP=00A4D4C8
EIP=1002B57E |
Storing the mulitplier like 2 as st(0) and using "fmul" for multiplying may work in my imagination.
But how can I store (float)2 to the FPU stack and push it to st(0) for making use of the value in the next operation (fmul dword ptr [ebx+64])?
I have to use a new temporary memory address, right?
Or may something like this help:
Code: |
fld qword [a] ;load a into st0
fmul st0, st0 ;st0 = a * a = a^2 |
(from Wikibooks > X86_Assembly/Floating_Point)
Code: | newmem:
fld dword [2] // the multiplier
fmul dword ptr [ebx+64]
originalcode:
//fmul dword ptr [ebx+64]
//mov [eax],ecx |
crashes the game.
|
|
Back to top |
|
|
ParkourPenguin I post too much Reputation: 140
Joined: 06 Jul 2014 Posts: 4299
|
Posted: Tue Apr 19, 2022 1:06 pm Post subject: |
|
|
In this instruction, 2 is an address, not a value. You can't load an arbitrary immediate onto the fpu stack. You have to load it from memory.
For example, if the value you want to multiply is already loaded on the top of the FPU stack, do this:
Code: | ...
label(multiplier)
newmem:
fmul qword ptr[multiplier]
//...
align 8 CC
multiplier:
dq (double)2.0
... |
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
|
thenic How do I cheat? Reputation: 0
Joined: 13 Apr 2022 Posts: 6
|
Posted: Wed Apr 20, 2022 12:55 pm Post subject: |
|
|
Thanks, yes, already thought that [3] can't reference a value but must name an address to get it's value from.
The game crashes again when I operate this code.
Then I alternated it a bit but it still crashes.
Code: | alloc(newmem,2048)
label(returnhere)
label(originalcode)
label(exit)
label(multiplier)
newmem:
fmul dword ptr [multiplier]
originalcode:
//fld dword ptr [ebx+64]
//mov [eax],ecx
align 8 CC
multiplier:
dd (float)2
exit:
jmp returnhere
"Cc.dll"+2B57B:
jmp newmem
returnhere: |
What does the "align 8 CC" mean? If I understand the CE Auto Assembler:align page correctly, it reserves a memory area for the qword to save?
Also - can I monitor some addresse's values while running the game?
I'd like to know what is ebx+64 but when I take the EBX register address via 'More information' as seen in the tutorial and "add manualy" with offset 64, I only get a value that doesn't change during the game.
Even if I enforce a value change with "mov [ebx+64],(float)5" using AA (which affects the game and my pointer value quite well).
|
|
Back to top |
|
|
ParkourPenguin I post too much Reputation: 140
Joined: 06 Jul 2014 Posts: 4299
|
Posted: Wed Apr 20, 2022 1:44 pm Post subject: |
|
|
The original code isn't being executed. This leaves the fpu stack imbalanced as the original code was pushing a value on it but you don't. Also that mov instruction was probably doing something important.
Are you sure that fld instruction was part of the original code? From your previous post it looks like it should've been `fmul dword ptr [ebx+64]`.
You're storing data in the middle of code you're trying to execute. This will almost certainly crash the game.
The align pseudoinstruction inserts as many bytes as is necessary to put the next address at a certain alignment.
Code: | align 8 CC
multiplier:
dq (double)2.0 | This makes sure the multiplier label is assigned to an address divisible by 8.
This isn't strictly necessary in most cases: most instructions in x86 / x86-64 architectures that address memory don't require strict alignment, but you should have it anyway. A typical example is when an unaligned value crosses a cache line boundary.
If you insist on only storing a float, you'd only need 4 byte alignment. You still shouldn't store it in the middle of code you're trying to execute: it's data, not code.
Also, if you're not going to use aobscans, use the assert from the full injection template.
Code: | assert(...)
alloc(newmem,2048)
label(returnhere)
label(multiplier)
newmem:
fmul dword ptr [multiplier]
mov [eax],ecx
jmp returnhere
align 8 CC
multiplier:
dd (float)2
"Cc.dll"+2B57B:
jmp newmem
returnhere: |
Search for "injection copy" if you want to get an address accessed by an instruction.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
|
thenic How do I cheat? Reputation: 0
Joined: 13 Apr 2022 Posts: 6
|
Posted: Wed Apr 20, 2022 3:04 pm Post subject: |
|
|
Thank your for your patience, much appreciated. It's a steep learning curve and huge respect for everyone who has (that much of) a clue of assembler code. Assembler was always sth like "the supreme power" of computer programming for me.
Oh I get it, an unaligned value may get split into two cache lines (if it's coincidentally at "the end" of the first) and this should be avoided.
You mean storing the multiplier using an operation during the jump isn't clean and it's better to store it before and just reference to it when the specific opcode (that I need/want to manipulate) gets executed? This sounds reasonable to me.
Okay and injection copy looks like I have to mov the base address ([ebx]) right before it gets used/written by the code into a newly allocated memory address that I can monitor (with offset attached). Also I won't remove the original operation. Will have a try asap.
I used the 'full injection' template now but now my code doesn't have any impact.
I will investigate!
(Yes, I messed up the fsp/fmul line in my previous post - sorry for that!)
Code: |
define(address,"Cc.dll"+2B57B)
define(bytes,D8 4B 64 89 08)
[ENABLE]
assert(address,bytes)
alloc(newmem,$1000)
label(code)
label(return)
newmem:
fmul dword ptr [multiplier]
mov [eax],ecx
jmp return
align 8 CC
multiplier:
dd (float)2
code:
fmul dword ptr [ebx+64]
mov [eax],ecx
jmp return
address:
jmp newmem
return:
[DISABLE]
address:
db bytes
// fmul dword ptr [ebx+64]
// mov [eax],ecx
dealloc(newmem)
{
// ORIGINAL CODE - INJECTION POINT: Cc.CcRigidBody::Derive+AB
Cc.CcRigidBody::Derive+82: 8D 74 24 30 - lea esi,[esp+30]
Cc.CcRigidBody::Derive+86: 8D BC 24 88 00 00 00 - lea edi,[esp+00000088]
Cc.CcRigidBody::Derive+8D: C7 44 24 10 03 00 00 00 - mov [esp+10],00000003
Cc.CcRigidBody::Derive+95: D9 C0 - fld st(0)
Cc.CcRigidBody::Derive+97: D8 4B 5C - fmul dword ptr [ebx+5C]
Cc.CcRigidBody::Derive+9A: D9 5C 24 18 - fstp dword ptr [esp+18]
Cc.CcRigidBody::Derive+9E: 8B 4C 24 18 - mov ecx,[esp+18]
Cc.CcRigidBody::Derive+A2: D9 C0 - fld st(0)
Cc.CcRigidBody::Derive+A4: D8 4B 60 - fmul dword ptr [ebx+60]
Cc.CcRigidBody::Derive+A7: D9 5C 24 1C - fstp dword ptr [esp+1C]
// ---------- INJECTING HERE ----------
Cc.CcRigidBody::Derive+AB: D8 4B 64 - fmul dword ptr [ebx+64]
// ---------- DONE INJECTING ----------
Cc.CcRigidBody::Derive+AE: 89 08 - mov [eax],ecx
Cc.CcRigidBody::Derive+B0: 8B 4C 24 1C - mov ecx,[esp+1C]
Cc.CcRigidBody::Derive+B4: 89 48 04 - mov [eax+04],ecx
Cc.CcRigidBody::Derive+B7: D9 5C 24 20 - fstp dword ptr [esp+20]
Cc.CcRigidBody::Derive+BB: 8B 4C 24 20 - mov ecx,[esp+20]
Cc.CcRigidBody::Derive+BF: 89 48 08 - mov [eax+08],ecx
Cc.CcRigidBody::Derive+C2: 8B 02 - mov eax,[edx]
Cc.CcRigidBody::Derive+C4: 8B 4A 0C - mov ecx,[edx+0C]
Cc.CcRigidBody::Derive+C7: 89 44 24 30 - mov [esp+30],eax
Cc.CcRigidBody::Derive+CB: 8B 42 18 - mov eax,[edx+18]
}
|
|
|
Back to top |
|
|
ParkourPenguin I post too much Reputation: 140
Joined: 06 Jul 2014 Posts: 4299
|
Posted: Wed Apr 20, 2022 3:49 pm Post subject: |
|
|
thenic wrote: | You mean storing the multiplier using an operation during the jump isn't clean and it's better to store it before and just reference to it when the specific opcode (that I need/want to manipulate) gets executed? | I don't know what you mean by that.
The multiplier gets stored when the AA script gets executed- not when one of the game's threads executes the assembly code.
Code: | multiplier:
dd (float)2 | "multiplier" is just a label- its only purpose is so that you can more easily refer to the address that label is located at.
The `dd` pseudoinstruction writes a doubleword (in this case a float) into memory. In general, the data being written by `dd` isn't code a CPU thread can execute- it's just data.
Example:
Code: | globalalloc(foo,4096)
label(multiplier)
foo:
// code
fmul dword ptr [multiplier]
// data
align 4 CC
multiplier:
dd (float)2
// back to code
jmp foo+800
{
assembles to:
foo - D8 0D 08009502 - fmul dword ptr [foo+8]
foo+6 - CC - int 3
foo+7 - CC - int 3
foo+8 - 00 00 - add [eax],al
foo+A - 00 40 E9 - add [eax-17],al
foo+D - EF - out dx,eax
foo+E - 07 - pop es
foo+F - 00 00 - add [eax],al
...
} | This script assembles to stuff that looks like garbage because there is garbage in the middle of the code: the float 2.
thenic wrote: | Okay and injection copy looks like I have to mov the base address ([ebx]) right before it gets used/written by the code... | before or after isn't important since ebx isn't being modified.
This would only matter if the register you want to save is also being modified. e.g.:
To save the address being accessed here, you would have to save it before that instruction.
thenic wrote: | Also I won't remove the original operation. | It's fine to leave out the fmul instruction since you're replacing that with your own fmul. The mov instruction you should leave alone because you don't know what that does.
Your code looks fine, except that I'd remove the code label:
Code: | ...
label(code) // remove this
...
code: // remove these 4 lines
fmul dword ptr [ebx+64] // ^
mov [eax],ecx // ^
jmp return // ^
... |
If it's still crashing, I guess you could try writing your multiplier to memory and see what happens:
Code: | ...
newmem:
push ecx
mov ecx,[multiplier]
mov [ebx+64],ecx
pop ecx
fmul dword ptr [ebx+64]
mov [eax],ecx
jmp return
... | Does the instruction `fmul dword ptr [ebx+64]` access more addresses than you intended? That's another reason why it could crash.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
|