Listing what a pointer function writes into?

Anything Cheat Engine related, bugs, suggestions, helping others, etc..
Post Reply
cHAOSfRED
Cheater
Cheater
Posts: 48
Joined: Sun Jun 17, 2018 12:26 pm
Reputation: 7

Listing what a pointer function writes into?

Post by cHAOSfRED »

So continuing from my previous thread for my first table. A new problem emerging.
Spoiler
For example, you have an RPG which consist a list of 4 units/characters, each with their own HP stat. The list of their stats in memory viewer is a region like this.

Code: Select all

...
?? ?? ?? ?? ?? ?? ?? ?? MaxHP1 MaxHP1 MaxMP1 MaxMP1 CurHP1 CurHP1 CurMP1 CurMP1 00 00 00 00 00 00 00 00
?? ?? ?? ?? ?? ?? ?? ?? MaxHP2 MaxHP2 MaxMP2 MaxMP2 CurHP2 CurHP2 CurMP2 CurMP2 00 00 00 00 00 00 00 00
?? ?? ?? ?? ?? ?? ?? ?? MaxHP3 MaxHP3 MaxMP3 MaxMP3 CurHP3 CurHP3 CurMP3 CurMP3 00 00 00 00 00 00 00 00
?? ?? ?? ?? ?? ?? ?? ?? MaxHP4 MaxHP4 MaxMP4 MaxMP4 CurHP4 CurHP4 CurMP4 CurMP4 00 00 00 00 00 00 00 00
...
Unfortunately, this region position doesn't have fixed offset from last unique AoB signature (after every game restarts). Altho each stats have fixed offset to each other at least. So I finally used "Find out what writes to this address" and figured there is only one function for every HP change (either when you took damage, heals or change equipment), just one function address managed to do it with a fixed pointer based on a line like this

"Game.exe+00AABBCC" with value of 4-bytes (AA BB CC DD) which read as "mov blablabla"

From this function, you can use "Find out what addresses this instruction accesses" in disassembler to reverse find where those stats region is atm. Or when the pointer saved to the table, one can use "Disassemble this memory region".
------------------
The plan was sorta creating a table tree that looked like this

Code: Select all

> "Enable this and change your HP" (Script & toggle tree)
>>> Unit 1 slot (toggle tree)
>>>>> Unit 1 Max HP (editable value)
>>>>> Unit 1 Max MP (editable value)
>>>>> Unit 1 Current HP (editable value)
>>>>> Unit 1 Current MP (editable value)
>>> Unit 2 slot
>>>>> Unit 2 Max HP
...
>>> Unit 3 slot
>>>>> Unit 3 Max HP
...
>>> Unit 4
>>>>> Unit 4 Max HP
....
--end of table tree--
There are two steps the script has to do:
1. Look up the addresses of what the pointer instruction writes into.
2. Modify it to the children addresses so the children list displayed and editable correctly.

How to do this?

User avatar
EpicBirdi
Fearless Donors
Fearless Donors
Posts: 64
Joined: Sat Jul 21, 2018 2:22 pm
Reputation: 58

Re: Listing what a pointer function writes into?

Post by EpicBirdi »

See if there's a function that accesses the address for MaxHP1. If something runs often you can pull the address itself as a pointer; otherwise look for something that writes to CurrentHP/MP1 and it'll update when either changes, based on your script.

If your on-write instruction is something simple like:
movss [rbx+0000013C],eax
You can pull the address by using a symbol to store the base address at rbx:
mov [myBasePointer],rbx

You can add a pointer to your symbol "myBasePointer" with offset 13C to find your Current HP/MP, and change the offset from that base to properly locate the other nearby values.

If your instruction affects many different addresses often, you may need unique identifier byte(s) to cmp against to only find your one base address. You can find this unique section by using the Structure Dissect tool. You may also need to do this step in general if your instruction is too "simple", or lacks a clear offset:
movss [rbx],eax
In this case, the instruction is likely shared code for many parts of an application, and you'd need that unique section to make sure you only find the address you want.

If you're certain you'll find your address easily, but notice it change randomly, you can run another cmp check against your symbol itself, for the default value you give it when defined. If it's not the default value, you've already stored an address in it and can jump to regular code instead of replacing the symbol's value.

cHAOSfRED
Cheater
Cheater
Posts: 48
Joined: Sun Jun 17, 2018 12:26 pm
Reputation: 7

Re: Listing what a pointer function writes into?

Post by cHAOSfRED »

EpicBirdi wrote:
Thu Jul 15, 2021 7:33 am
Spoiler
See if there's a function that accesses the address for MaxHP1. If something runs often you can pull the address itself as a pointer; otherwise look for something that writes to CurrentHP/MP1 and it'll update when either changes, based on your script.

If your on-write instruction is something simple like:
movss [rbx+0000013C],eax
You can pull the address by using a symbol to store the base address at rbx:
mov [myBasePointer],rbx

You can add a pointer to your symbol "myBasePointer" with offset 13C to find your Current HP/MP, and change the offset from that base to properly locate the other nearby values.

If your instruction affects many different addresses often, you may need unique identifier byte(s) to cmp against to only find your one base address. You can find this unique section by using the Structure Dissect tool. You may also need to do this step in general if your instruction is too "simple", or lacks a clear offset:
movss [rbx],eax
In this case, the instruction is likely shared code for many parts of an application, and you'd need that unique section to make sure you only find the address you want.

If you're certain you'll find your address easily, but notice it change randomly, you can run another cmp check against your symbol itself, for the default value you give it when defined. If it's not the default value, you've already stored an address in it and can jump to regular code instead of replacing the symbol's value.
So instead based from what function writes into "MaxHP", I tried to track down from what accessing the "MaxHP" as mentioned. But there are like 3 other functions than one that writing into it.

Lets say the function that writes into "MaxHP". This function when triggered repeated times will write on two addresses (MaxHP and Current HP) and also some random addresses for only 1 time (displays perhaps?)

Code: Select all

game.exe+AAAA : mov [rcx+01],al


Note the addresses and offsets are different than example I gave in first post, but just to make it simple.

While the other three functions accessing the MaxHP solely it are

Code: Select all

game.exe+BBBB : movzx eax, byte ptr [rdx+01]
game.exe+CCCC :  movzx edx, byte ptr [rax+01]
game.exe+DDDD :  movzx ecx, byte ptr [rax+01]
So what should I write in the table?

GreenHouse
Expert Cheater
Expert Cheater
Posts: 857
Joined: Fri Oct 12, 2018 10:25 pm
Reputation: 889

Re: Listing what a pointer function writes into?

Post by GreenHouse »

That looks like a 'GameMaker' game, which is annoying to make scripts for. But you could Google up something like "GameMaker Cheat Engine". There's a Youtube tutorial for it, explaining how to find things to compare.

cHAOSfRED
Cheater
Cheater
Posts: 48
Joined: Sun Jun 17, 2018 12:26 pm
Reputation: 7

Re: Listing what a pointer function writes into?

Post by cHAOSfRED »

GreenHouse wrote:
Fri Jul 16, 2021 9:26 am
That looks like a 'GameMaker' game, which is annoying to make scripts for. But you could Google up something like "GameMaker Cheat Engine". There's a Youtube tutorial for it, explaining how to find things to compare.
No, it is UE4 game. So not that "pixel RPG" gamemaker kind.

GreenHouse
Expert Cheater
Expert Cheater
Posts: 857
Joined: Fri Oct 12, 2018 10:25 pm
Reputation: 889

Re: Listing what a pointer function writes into?

Post by GreenHouse »

cHAOSfRED wrote:
Sat Jul 17, 2021 1:16 pm
No, it is UE4 game. So not that "pixel RPG" gamemaker kind.
Then do what EpicBirdi said. Remove the offset of the address, and look what accesses it. And then if there are multiple characters, in an specific offset there has to be an ID or something which you can compare in a script, and then mov to an alloc each character that way.

cHAOSfRED
Cheater
Cheater
Posts: 48
Joined: Sun Jun 17, 2018 12:26 pm
Reputation: 7

Re: Listing what a pointer function writes into?

Post by cHAOSfRED »

GreenHouse wrote:
Sat Jul 17, 2021 1:31 pm
cHAOSfRED wrote:
Sat Jul 17, 2021 1:16 pm
No, it is UE4 game. So not that "pixel RPG" gamemaker kind.
Then do what EpicBirdi said. Remove the offset of the address, and look what accesses it. And then if there are multiple characters, in an specific offset there has to be an ID or something which you can compare in a script, and then mov to an alloc each character that way.
When you said "Remove the offset of the address", means I have to look up [rcx+00] address, not the "game.exe+00" address??

GreenHouse
Expert Cheater
Expert Cheater
Posts: 857
Joined: Fri Oct 12, 2018 10:25 pm
Reputation: 889

Re: Listing what a pointer function writes into?

Post by GreenHouse »

cHAOSfRED wrote:
Thu Jul 22, 2021 3:12 am
When you said "Remove the offset of the address", means I have to look up [rcx+00] address, not the "game.exe+00" address??
Yes, if the address that you have is +13C (which is written by movss [rbx+0000013C],eax ), then remove 13C from it and look for that one.

cHAOSfRED
Cheater
Cheater
Posts: 48
Joined: Sun Jun 17, 2018 12:26 pm
Reputation: 7

Re: Listing what a pointer function writes into?

Post by cHAOSfRED »

Okay, sorry for necroing thread.

So up to this point, I reached this function. I am still kinda stuck at understanding the first reply. So I reached one good function to work on. Image inside spoiler below
Spoiler
Image
This "mov [rcx +01],al" function is what used to write on every party member's HP. This function itself is NOT fixed (will move after every game restart), so I can not use "[game.exe+0000XXXX]" as pointer. But it is always definite on this pattern so I probably could just use AOB scan for this. PS: Since I obtain this process by exchanging party slots, it is a "copy-swap" function.

Code: Select all

XXXaddress- 0FB6 42 01  - movzx eax,byte ptr [rdx+01]
XXXaddress - 88 41 01  - mov [rcx+01],al << writes max HP
XXXaddress- 0FB6 42 02  - movzx eax,byte ptr [rdx+02]
XXXaddress - 88 41 02  - mov [rcx+02],al << writes current HP
XXXaddress- 0FB6 42 02  - movzx eax,byte ptr [rdx+03]
XXXaddress - 88 41 02  - mov [rcx+03],al << writes max MP
XXXaddress- 0FB6 42 02  - movzx eax,byte ptr [rdx+04]
XXXaddress - 88 41 02  - mov [rcx+04],al << writes current MP
Now then, the questions are:
How to make this write function to for example "write value of [desired value]"?
Then perhaps create the table where there is customization for the value?

So I tried to take example of other codes atm, one from MH World table, mainly this part since it should fit closest to my goal
Spoiler

Code: Select all

<CheatEntries>
        <CheatEntry>
          <ID>20722</ID>
          <Description>"Re-enter Menu After Hoverover"</Description>
          <LastState Value="" RealAddress="00000000"/>
          <Color>0000FF</Color>
          <GroupHeader>1</GroupHeader>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
    <CheatEntry>
      <ID>1337091759</ID>
      <Description>"Guiding Lands Level Editor"</Description>
      <Options moHideChildren="1"/>
      <LastState/>
      <Color>000000</Color>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>{ Game   : MonsterHunterWorld.exe
  Version: 
  Date   : 2020-01-16
  Author : Squall8
}

[ENABLE]

aobscanmodule(guidinglands,MonsterHunterWorld.exe,8B BC 81 28 B9 27 00) // should be unique
alloc(newmem,$1000,guidinglands)

label(code)
label(return)
label(guidinglandsp)

registersymbol(guidinglands)
registersymbol(guidinglandsp)

newmem:
  cmp rax,0
  jne code
  push rbx
  lea rbx,[rcx+rax*4+0027B928]
  mov [guidinglandsp],rbx
  pop rbx

code:
  mov edi,[rcx+rax*4+0027B928]
  jmp return

guidinglandsp:
  dq 0

guidinglands:
  jmp newmem
  nop 2
return:

[DISABLE]

guidinglands:
  db 8B BC 81 28 B9 27 00

unregistersymbol(guidinglands)
unregistersymbol(guidinglandsp)
dealloc(newmem)
</AssemblerScript>
      <CheatEntries>
        <CheatEntry>
          <ID>1337091760</ID>
          <Description>"Forest Exp"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>0</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091761</ID>
          <Description>"Spire Exp"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>4</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091762</ID>
          <Description>"Coral Exp"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>8</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091763</ID>
          <Description>"Vale Exp"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>C</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091764</ID>
          <Description>"Volcanic Exp"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>10</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337092193</ID>
          <Description>"Tundra Exp"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>14</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091765</ID>
          <Description>"Forest Outcrops"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>24</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091766</ID>
          <Description>"Spire Outcrops"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>28</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091767</ID>
          <Description>"Coral Outcrops"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>2C</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091768</ID>
          <Description>"Vale Outcrops"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>30</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091769</ID>
          <Description>"Volcanic Outcrops"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>34</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337092194</ID>
          <Description>"Tundra Outcrops"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>38</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091770</ID>
          <Description>"Forest Bonepiles"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>44</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091771</ID>
          <Description>"Spire Bonepiles"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>48</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091772</ID>
          <Description>"Coral Bonepiles"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>4C</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091773</ID>
          <Description>"Vale Bonepiles"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>50</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337091774</ID>
          <Description>"Volcanic Bonepiles"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>54</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>1337092195</ID>
          <Description>"Tundra Bonepiles"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>guidinglandsp</Address>
          <Offsets>
            <Offset>58</Offset>
          </Offsets>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
Which I edited into this:
Spoiler

Code: Select all

<CheatEntry>
      <ID>5</ID>
      <Description>"Party HP MP Stats Editor"</Description>
      <Options moHideChildren="1"/>
      <LastState/>
      <Color>000000</Color>
      <VariableType>Auto Assembler Script</VariableType>
  <AssemblerScript>{ Game   : Game.exe
  Version: 
  Date   : 
  Author :
}

[ENABLE]

aobscanmodule(partystats,Game.exe,0FB6 42 01 88 41 01) // should be unique
alloc(newmem,$1000,partystatsmaxhp)

label(code)
label(return)
label(partystatsmaxhp)

registersymbol(partystats)
registersymbol(partystatsmaxhp)

newmem:

??????????????????

code:

??????????????????

partystatsmaxhp:
  dq 0

partystats:
  jmp newmem
  nop 2
return:

[DISABLE]

partystats:
  db 0FB6 42 01 88 41 01

unregistersymbol(partystats)
unregistersymbol(partystatsmaxhp)
dealloc(newmem)
  </AssemblerScript>
  <CheatEntries>
        <CheatEntry>
          <ID>6</ID>
          <Description>"1st Party Max HP"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>partystatsmaxhp</Address>
          <Offsets>
            <Offset>0</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>7</ID>
          <Description>"2nd Party Max HP"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>partystatsmaxhp</Address>
          <Offsets>
            <Offset>10</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>8</ID>
          <Description>"3rd Party Max HP"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>partystatsmaxhp</Address>
          <Offsets>
            <Offset>20</Offset>
          </Offsets>
        </CheatEntry>
        <CheatEntry>
          <ID>9</ID>
          <Description>"4th Party Max HP"</Description>
          <VariableType>4 Bytes</VariableType>
          <Address>partystatsmaxhp</Address>
          <Offsets>
            <Offset>30</Offset>
          </Offsets>
        </CheatEntry>
  </CheatEntries>
</CheatEntry>
What should I write on the newmem, code and partystats part?
Perhaps a better AOB hint?

Post Reply

Who is online

Users browsing this forum: No registered users