 |
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
TTR How do I cheat?
Reputation: 0
Joined: 04 May 2020 Posts: 4
|
Posted: Mon May 04, 2020 12:32 pm Post subject: Auto Assembler - declare and assign to variable |
|
|
Hi everyone,
I'm coming from a background with Java, C#, PHP, etc. I have done a little bit of MIPS assembly but that's about it.
Having some serious issues declaring variables in Auto-Assembler. Can anyone point me in the right direction? All I want to do is store the value of some address in a variable that I can then re-use in my address list.
Here's a couple examples I have tried:
Code: |
define(address,"OutSpy.dll"+49378E)
define(bytes,E9 6D C8 BB E6 0F 1F 00)
[ENABLE]
assert(address,bytes)
alloc(newmem,$1000)
alloc(pTest, 4)
label(pTest)
registersymbol(pTest)
label(return)
newmem:
pTest:
dd ebp-0C
lea eax,[ebp-0C]
mov edx,OutSpy.dll+493A78
jmp return
address:
jmp newmem
nop 3
return:
[DISABLE]
address:
db bytes
// lea eax,[ebp-0C]
// mov edx,OutSpy.dll+493A78
unregistersymbol(pTest)
unregistersymbol(newmem)
dealloc(newmem)
|
Code: |
define(address,"OutSpy.dll"+49378E)
define(bytes,E9 6D C8 BB E6 0F 1F 00)
[ENABLE]
assert(address,bytes)
alloc(newmem,$1000)
registersymbol(newmem)
define(pTest, newmem+100)
registersymbol(pTest)
label(return)
newmem:
mov [pTest], ebp-0C
lea eax,[ebp-0C]
mov edx,OutSpy.dll+493A78
jmp return
address:
jmp newmem
nop 3
return:
[DISABLE]
address:
db bytes
// lea eax,[ebp-0C]
// mov edx,OutSpy.dll+493A78
unregistersymbol(pTest)
unregistersymbol(newmem)
dealloc(newmem)
|
Here's a working one, which I have no idea why it works:
Code: |
aobscan(INJECT,48 63 80 D0 00 00 00 85 C0 0F 84) // should be unique
registersymbol(INJECT)
alloc(newmem,$1000,18893394898)
registersymbol(newmem)
define(pTime,newmem+100)
registersymbol(pTime)
newmem:
mov [pTime], rax
movsxd rax,dword ptr [rax+000000D0]
jmp return
INJECT:
jmp newmem
nop
nop
return:
[DISABLE]
INJECT:
db 48 63 80 D0 00 00 00
unregistersymbol(pTime)
unregistersymbol(newmem)
unregistersymbol(INJECT)
dealloc(newmem)
|
Why does the one with pTime work but not the one with pTest?
I tried searching around, I have the cheatengine documentation bookmarked. Feeling a bit clueless here.
Any insight?
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4702
|
Posted: Mon May 04, 2020 1:28 pm Post subject: |
|
|
1st code:
"dd" is a pseudoinstruction that takes a doubleword (32 bit) literal and writes its byte representation to the location dd is placed at. The bytes dd generates are not code and should not be executed.
ebp-OC is not a literal - it's a register and some arithmetic. This is like the difference between a literal and a variable in higher level programming languages.
Examples of literals:
Code: | dd 12 // 0C 00 00 00
dd -3 // FD FF FF FF
dd (float)15.0 // 00 00 70 41
dd (double32h)7.3 // 33 33 1D 40 - I learned this existed just now
|
2nd code:
Code: | mov [pTest], ebp-0C | That instruction is of the form "MOV r/m32,r32" (opcode 0x89; see an instruction reference). The r32 source operand is just a 32-bit register: you're not allowed to do arithmetic there.
3rd code:
Just want to point out you swapped from 32-bit code to 64-bit code.
"mov [pTime],rax " is fine; however, I don't know why this works:
Code: | define(pTime,newmem+100)
registersymbol(pTime) |
"define" basically replaces every instance of pTime with "newmem+100". This doesn't affect registersymbol, but even so, the symbol pTime isn't assigned any address in the script.
Maybe CE does some magic to make that work. Regardless, IMO this would be the more canonical way:
Code: | ...
label(pTime)
registersymbol(pTime)
...
newmem+100:
pTime:
dq 0 // rax is 64-bit -> use dq (declare quadword); ebp is 32-bit -> use dd |
(you could also use a separate alloc for pTime)
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
TTR How do I cheat?
Reputation: 0
Joined: 04 May 2020 Posts: 4
|
Posted: Mon May 04, 2020 2:37 pm Post subject: |
|
|
Thanks for your help ParkourPenguin.
I understand a bit better now. So from what I gathered, dd cannot be used to set a memory address to a "dynamic value". It needs hard-coded bytes/values.
In 2nd code, if I wanted to put the value of ebp-0C inside pTest how would I do that?
Would I first put ebp-0C inside a register? Am I allowed to do that? What does the "m" stand for in r/m32?
Nice catch, guess you noticed from the extra registers. Yeah the first two were for a 32 bit application and the 3rd one was for a 64bit game.
3rd code, honestly I don't know how it works either and I tried using it in other scripts which never works. Very strange.
In the first example, I have an alloc(pTest, 4). If I remove the pTest label and replace dd ebp-0C with mov [pTest], ebp-0C, it still fails to compile. With an error like: mov [0000000+100],ebp-0C cannot compile.
Is that because I'm trying to do a mov r/m32,r32? How do I avoid doing that?
I don't understand why you're doing a dq 0 with pTime. Wouldn't that initialize it to 0 instead of storing the value of rax inside it?
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4702
|
Posted: Mon May 04, 2020 6:30 pm Post subject: |
|
|
TTR wrote: | In 2nd code, if I wanted to put the value of ebp-0C inside pTest how would I do that?
Would I first put ebp-0C inside a register? Am I allowed to do that? What does the "m" stand for in r/m32? | In that specific injection point, I'd do this:
Code: | newmem:
lea eax,[ebp-0C]
mov [pTest],eax
mov edx,OutSpy.dll+493A78
jmp return | That lea instruction is really convenient to have as part of the original code. It basically says "eax = ebp-0C".
In other circumstances, it's usually fine to just get the register itself (i.e. ebp) and apply the offset (i.e. 0x0C) later on. For example, add a memory record as a pointer with the base address of "pTest" and a L1 offset of "0C".
If you must overwrite a register yourself, use push/pop to back it up unless you can see reason not to (e.g. game overwrites the value later).
"r/m32" basically means a 32-bit register or memory location. In this case "[pTest]" is a 32-bit memory location.
TTR wrote: | In the first example, I have an alloc(pTest, 4). If I remove the pTest label and replace dd ebp-0C with mov [pTest], ebp-0C, it still fails to compile. With an error like: mov [0000000+100],ebp-0C cannot compile.
Is that because I'm trying to do a mov r/m32,r32? How do I avoid doing that? | It fails to compile for the same reason as the second code: you can't do arithmetic there. You can't have "ebp-0C", only "ebp". There does not exist an instruction that can do the operation you're trying to do: you must use several instructions to do that. (as shown previously w/ lea then mov)
As an aside, you don't need to call label(...) on symbols introduced with an alloc(...).
TTR wrote: | I don't understand why you're doing a dq 0 with pTime. Wouldn't that initialize it to 0 instead of storing the value of rax inside it? | It'll get initialized to 0 anyway even without that there. The code you write in the code injection to copy the address doesn't happen immediately- it'll only happen at the game's convenience.
Explicitly initializing it to 0 is superfluous, but it isn't a bad habit to have. If you're using multiple values like this:
Code: | label(value1)
label(value2)
label(value3)
newmem+800:
value1:
dd 0
value2:
dq 0
value3:
dd 0 | Then each symbol refers to a unique address: the second 4 bytes after the first, and the third 8 bytes after the second. If those dd / dq instructions weren't there, they'd all have the same address.
Reordering the way values are laid out in memory becomes as simple as copying and pasting a couple lines.
If memory is reused between script invocations (e.g. globalalloc), it might be nice to initialize memory to a known state when the script starts.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
TTR How do I cheat?
Reputation: 0
Joined: 04 May 2020 Posts: 4
|
Posted: Mon May 04, 2020 8:36 pm Post subject: |
|
|
Thank you for your detailed answer! I understand my mistake a lot better now. And more importantly, how to fix it.
My problem was that I was trying to do arithmetic in the second part of the mov. I think I was confused because the lea uses arithmetic in its call.
I'm still a bit confused. It seems like some instructions let me do it and others don't.
I'm so used to chaining function calls. Lol.
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4702
|
Posted: Mon May 04, 2020 10:13 pm Post subject: |
|
|
Addressing memory (stuff between square brackets) is generally the only time you can do arithmetic in operands. Even then, you're still limited.
Look at assembly and you'll notice patterns eventually.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
TTR How do I cheat?
Reputation: 0
Joined: 04 May 2020 Posts: 4
|
Posted: Tue May 05, 2020 1:20 pm Post subject: |
|
|
Thank you ParkourPenguin. You were very helpful.
|
|
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
|
|