Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


[help] [assembly] - accessing a pointer to a pointer in 64b

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General Gamehacking
View previous topic :: View next topic  
Author Message
NoMoreBSoD
Advanced Cheater
Reputation: 3

Joined: 03 Sep 2013
Posts: 85

PostPosted: Fri Jun 04, 2021 10:26 am    Post subject: [help] [assembly] - accessing a pointer to a pointer in 64b Reply with quote

I used to be able to push/pop registers to access a pointer to a pointer downstream, but I've just tried it recently with a 64 bits game and it crashes it. What am I doing wrong?


Code:
newmem:
  push r9
  lea r9, [rax+10]
  cmp [r9+18],69746361
  jne code
  mov [pointer_combat_action_points], rax


code:
  pop r9
  movups xmm0,[rax]
  mov r9,[rbp+00000108]
  jmp return


The code accesses a ton of values stored in rax, and I want to filter them through their in-memory structure as registers don't seem to hold any significant value.
So I get a value in rax, and at rax+10 there is a pointer to the description of that value, and at [[rax+10]+18] there is a string that I want to check.

I used to be able to filter those situations with a simple push pop, but it doesn't work currently and I suspect it's because it's a 64 bits game. rax being longer than eax registers, something bugs out.
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 140

Joined: 06 Jul 2014
Posts: 4299

PostPosted: Fri Jun 04, 2021 11:38 am    Post subject: Reply with quote

The lea instruction is superfluous. It doesn't access memory- it just computes the effective address of the source operand. This means you're testing against [rax+10+18], or just [rax+28].

Use the mov instruction if you actually want to dereference a pointer:
Code:
newmem:
  mov r9, [rax+10]
  cmp [r9+18],'acti'
  jne code
  mov [pointer_combat_action_points], rax
code:
  movups xmm0,[rax]
  mov r9,[rbp+00000108]
  jmp return
Three notes:
No need to push/pop r9. The game overwrites it later with "mov r9,[rbp+108]", which means the game isn't using it right now and there's no need to backup or restore it.
IIRC CE assembles sequences of characters (e.g. 'acti') into its little-endian integer representation when used as an immediate in instructions.
This might still fail if [rax+10] isn't valid (e.g. a null pointer).

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
NoMoreBSoD
Advanced Cheater
Reputation: 3

Joined: 03 Sep 2013
Posts: 85

PostPosted: Fri Jun 04, 2021 1:34 pm    Post subject: Reply with quote

ParkourPenguin wrote:
The lea instruction is superfluous. It doesn't access memory- it just computes the effective address of the source operand. This means you're testing against [rax+10+18], or just [rax+28].

Use the mov instruction if you actually want to dereference a pointer:
Code:
newmem:
  mov r9, [rax+10]
  cmp [r9+18],'acti'
  jne code
  mov [pointer_combat_action_points], rax
code:
  movups xmm0,[rax]
  mov r9,[rbp+00000108]
  jmp return
Three notes:
No need to push/pop r9. The game overwrites it later with "mov r9,[rbp+108]", which means the game isn't using it right now and there's no need to backup or restore it.
IIRC CE assembles sequences of characters (e.g. 'acti') into its little-endian integer representation when used as an immediate in instructions.
This might still fail if [rax+10] isn't valid (e.g. a null pointer).

Right about lea! I usually do the push pop as an automatism and it wasn't necessary here.

And those were very insightful 3 tips, thanks! You even translated the byte code to a human readable string Shocked Cool

I try updating my code but it still crashes:

Code:
newmem:
  mov r9, [rax+10]
  cmp r9, 0
  je code
  cmp [r9+18],'acti'
  jne code
  mov [pointer_combat_action_points], rax


code:
  movups xmm0,[rax]
  mov r9,[rbp+00000108]
  jmp return


And if I just remove the whole newmem part, it doesn't crash so it shouldn't be an injection/detection problem.

Code:
newmem:


code:
  movups xmm0,[rax]
  mov r9,[rbp+00000108]
  jmp return



I also tried tracing back the pointers with pointer scan and "what accesses this address", and 4 levels deep I still get functions that accesses a ton of address or nothing.

How can I fix my injection? Or is there an alternative path that I haven't thought of yet?

I usually use 5 enquiry paths:
(1)find what writes to this address (usually a bingo 90% of the time)
if accessing an infinite number of addresses, (2) I filter them either by what's around them in memory, or (3) by the register values while they are accessed.

(4) I do a memory scan for the address of the value, or for the address of the pointer of that value. This tend to be pretty fail safe (except in that case.)

If all that fails, (5) I go the pointer scanner, and check for code that accesses 1 of the levels of a random pointer. Which is the most tedious method as I never know whether I'm looking at a real pointer or not. And as checking for "what accesses this address" on multiple addresses tended to crash my computer 10 years, I only check the address one by one.

I guess that what's left is a bit more advanced reverse engineering. Should I be looking at ghidra?
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 140

Joined: 06 Jul 2014
Posts: 4299

PostPosted: Fri Jun 04, 2021 3:42 pm    Post subject: Reply with quote

I'd guess this is crashing because your code makes assumptions about the structure layout that the injection point doesn't.
e.g. C code:
Code:
struct FloatValues {
    float v[4];
};

struct Foo {
    struct FloatValues f;
    const char *id;
};

struct Bar {
    struct FloatValues f;
    int id;
};

void access(struct FloatValues *f);
Pretend you're injecting in the access function. You're assuming everything being passed in is a Foo structure, while the game may also pass in a Bar structure. Treat an arbitrary integer as a pointer and you'll almost certainly crash the game.

Best advice I can give is to find a better value to compare against. The structure dissector will help you find values to identify the address you want.
In your case, use {$try} / {$except} to handle bad access errors.
https://forum.cheatengine.org/viewtopic.php?p=5761822#5761822

If you're feeling lazy, you can use that as the solution. You can also use it to find out what structure is bad, add it to the structure dissector, and use it to find a better value to compare against. i.e. put a breakpoint on an instruction under {$except} that's not normally executed when the access is good.

Here's the steps I go through when making an injection to copy an address:
  1. Find out what instructions access the address. Look for one that accesses only that address.
  2. Look at what accesses other values in that structure- maybe one of them will have a unique instruction.
  3. Go up the callstack and see if it's easier to inject at a caller. Keep track of offsets along the way to form a good pointer path. If data ever goes through a container I don't want to deal with (e.g. hash tables), give up and move on to some other value. This can be really complicated for beginners.
  4. If the caller isn't good and you noticed a new node on the pointer path to the value, look at what accesses that pointer.
  5. Repeat going up the callstack until the real base address is found, then just use that pointer path instead of a code injection. (one time in some JIT-compiled game I found the base address stored as an immediate in an instruction)
  6. Start looking for stuff to distinguish the address you want from the other accessed addresses.
I go through the steps involving reverse engineering first because I enjoy it (for a little while) and I'm good at it. If you're not familiar with reverse engineering and don't really want to learn it, it's fine to skip to the last step.

As an aside, how is the symbol "pointer_combat_action_points" declared? globalalloc, or alloc/label and registersymbol?

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
NoMoreBSoD
Advanced Cheater
Reputation: 3

Joined: 03 Sep 2013
Posts: 85

PostPosted: Fri Jun 04, 2021 4:50 pm    Post subject: Reply with quote

Quote:
as an aside, how is the symbol "pointer_combat_action_points" declared? globalalloc, or alloc/label and registersymbol?

alloc and registersymbol.



I try using the try / except block you suggested but I can't make it work without a crash.

Code:
[ENABLE]

aobscanmodule(aob_combat_action_points,Griftlands.exe,0F 10 00 4C 8B 8D 08 01 00 00) // should be unique
alloc(newmem,$1000,aob_combat_action_points)

label(code)
label(return)
label(no_error)
registersymbol(pointer_combat_action_points)
alloc(pointer_combat_action_points,8)

newmem:
{$try}
  mov r9, [rax+10]
  jmp no_error

{$except}
//error happened
jmp code

no_error:
  cmp r9, 0
  je code
  cmp [r9+18],'acti'
  jne code
  mov [pointer_combat_action_points], rax
  jmp code





code:
  movups xmm0,[rax]
  mov r9,[rbp+00000108]
  jmp return

aob_combat_action_points:
  jmp newmem
  nop 5
return:
registersymbol(aob_combat_action_points)

[DISABLE]

aob_combat_action_points:
  db 0F 10 00 4C 8B 8D 08 01 00 00

unregistersymbol(aob_combat_action_points)
dealloc(newmem)
unregistersymbol(pointer_combat_action_points)
dealloc(pointer_combat_action_points)


I'm honestly at a loss about why it crashes at all. If the value of [rax+10] was equal to 0 and invalid, it should just skip to the usual flow. But maybe some values other than 0 are invalid, but wouldn't a pointer to [0+10] be something trivial to look at and move on? I don't get why it crashes just from that.

Here is was the most immediate call diagram looks like :
That being said, I don't really understand the spawn diagram function as the instruction I right-clicked on to spawn the diagram isn't part of the diagram itself...


I think even the most basic data structures are stored in a hash map / dictionnary / JSON array which is hell to deal with with Cheat Engine.


I also had a sudden illumination and thought that the comparison is what was breaking the game, so I went for the pushfq / popfq combo, which push & pop the flags, but it still crashes.


Could I just load all the rax values into a lua array, and check them from here? But that might slow down the game terribly as the function is called thousands of times per second...
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 140

Joined: 06 Jul 2014
Posts: 4299

PostPosted: Fri Jun 04, 2021 6:24 pm    Post subject: Reply with quote

The problem isn't "mov r9, [rax+10]" - that's obviously fine since the original code accesses memory around that address too with "movups xmm0,[rax]". The problem is "cmp [r9+18],'acti'" since you don't know if r9+18 is a valid address.

By "valid address", I mean an address that is both mapped in the process's virtual address space and is accessible. Try to access an address that isn't mapped and the processor can't do anything but generate an error. It's like someone asking you look at page 392 in a book that only has 85 pages: you simply can't do it. There are also some protections in place to stop you from accessing memory you shouldn't be accessing (i.e. kernel memory), but don't worry about that.

The overwhelming majority of the process's address space isn't mapped to anything. A 64-bit register can have 2^64 possible states: that's 16 exbibytes of memory it could address. Most computers have an amount of memory within a few orders of magnitude of 32 gibibytes: less than 0.0000002% of 2^64. Treating some random integer, float, string, etc., like a pointer is almost certainly going to crash the game. 0 is just the most common "invalid pointer" value you'll find.

If you work with pointers in a language a bit higher-level than assembly (e.g. C, C++), you might find them easier to understand.

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
NoMoreBSoD
Advanced Cheater
Reputation: 3

Joined: 03 Sep 2013
Posts: 85

PostPosted: Fri Jun 11, 2021 4:34 am    Post subject: Reply with quote

ParkourPenguin wrote:
The problem isn't "mov r9, [rax+10]" - that's obviously fine since the original code accesses memory around that address too with "movups xmm0,[rax]". The problem is "cmp [r9+18],'acti'" since you don't know if r9+18 is a valid address.

By "valid address", I mean an address that is both mapped in the process's virtual address space and is accessible. Try to access an address that isn't mapped and the processor can't do anything but generate an error. It's like someone asking you look at page 392 in a book that only has 85 pages: you simply can't do it. There are also some protections in place to stop you from accessing memory you shouldn't be accessing (i.e. kernel memory), but don't worry about that.

The overwhelming majority of the process's address space isn't mapped to anything. A 64-bit register can have 2^64 possible states: that's 16 exbibytes of memory it could address. Most computers have an amount of memory within a few orders of magnitude of 32 gibibytes: less than 0.0000002% of 2^64. Treating some random integer, float, string, etc., like a pointer is almost certainly going to crash the game. 0 is just the most common "invalid pointer" value you'll find.

If you work with pointers in a language a bit higher-level than assembly (e.g. C, C++), you might find them easier to understand.

I've worked with pointers in C but mostly for passing objects around.

I tried getting around this mapped memory by making sure that the pointer doesn't go farther than the allocated memory but that didn't work out for some reason.

How do I make the try / except block work? Just surrounding the code with {&try} and {&except} didn't work out. Sad

I'm quite lost and I waited 10 days to see if anybody else would upload a table to the other forum but nothing came up.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General Gamehacking All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites