 |
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
paul44 Expert Cheater
Reputation: 2
Joined: 20 Jul 2017 Posts: 190
|
Posted: Thu Jan 25, 2024 12:23 pm Post subject: readMem: 'the memory could not be fully read' [Solved] |
|
|
prenote: if one is interested in a working script, check out my AC Origins table.
images: see here [ https://ibb.co/album/tTG2z4 ]
readMem: 'the memory could not be fully read' (image 7)
When using 'readMem' in the same script within its ASM_section (see also iimage 2), the 1st time it will inject
incorrect opcode (not initialized yet ?!). (image 6)
When dis/enabling again (and thereafter), the injected opcode remains correct... (image 5)
(tested with both globalalloc(), alloc() and combos incl registersymbol() without success)
Workaround:
a. introduce the aobscan in an earlier (main) script, which seems to properly initialize the readMem variable.
Some alloc() & registerSymbol() takes care of addressing (image 4 & 3)
b. in the actual script, the original aobscan is disabled, and the opcode updated with the new symbols.
The 'readMem' seems to work properly now (incl the Disabled section)... ?! (image 2 & 1)
2 questions:
1. Can this be done in the original script: meaning the proper iniialization of the readMem var so that it can be picked up correctly upon enabling ?
(without throwing everything in the/a lua section)
2. Is the "workaround" a reliable solution ? (will do more testing as i go...)
ps: tested with v7.1 & v7.4
ps2: i'm trying to build a table supporting 2 game versions, with some considerable differences at times.
btw: images are in reverse order during uploading; if not clear, do tell.
Last edited by paul44 on Thu Feb 01, 2024 11:58 am; edited 1 time in total |
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 150
Joined: 06 Jul 2014 Posts: 4646
|
Posted: Thu Jan 25, 2024 3:10 pm Post subject: |
|
|
Just post a full script somewhere. Preferably, reduce it to an SSCCE.
Put stuff that defines symbols near the top. aobscan, define, alloc, and label. Don't mix them up.
Why globalalloc xHealth / xRaceHorse? It's not like you're going to use them outside that script.
You're not using the third parameter to alloc. Unless there is no free memory within 2 GiB of the injection point and you're forced to use a 14-byte jump, this is wrong.
You're only giving 0x100 (256) bytes for newmem. That's enough space for only a few dozen instructions. Make it bigger.
Looks like you're writing to "Health" twice in the disable section? Don't do that. Just back up the entire original injection point and restore it.
Don't use readmem to execute the original code. Use reassemble for that. Some instructions change their behaviour depending on where they are located in memory- see RIP-relative addressing. The only exception would be short jumps contained within the injection point.
readmem / reassemble works before anything is written to memory.
Code: | aobscan(result, 12 34 ?? 56)
alloc(foo,4)
alloc(bar, 4)
foo:
readbytes(result,4) // works fine
bar:
readbytes(foo,4) // doesn't work: `foo` doesn't exist yet |
Example for readmem / reassemble:
https://forum.cheatengine.org/viewtopic.php?p=5485950#5485950
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
paul44 Expert Cheater
Reputation: 2
Joined: 20 Jul 2017 Posts: 190
|
Posted: Sun Jan 28, 2024 9:21 am Post subject: some clarification... |
|
|
^ when I release the updated table, I'll add a link here.
Anyways: my initial problem with Readmem: see image [ https://ibb.co/dknSkJg ] (i've removed other aobscans for readability)
=> as stated it only works after enabling it a 2nd time. and this post [ https://www.cheatengine.org/forum/viewtopic.php?t=569601&sid=2593bfc744470b41dd49113831bb69c1 ] actually explains why (kinda)
=> the 'readMem' seems to be initialised/executed after the injection takes place; hence its variable/symbol does not a) exist yet b) has not received them bytes
Practically: one can not use 'readMem' "initially". By placing/enabling - without actually executing - the aobscan/readMem in an earlier phase/script, the readMem_location gets properly initialized/populated (I have done more testing now, without having any trouble)...
Obviously: not great for making one's table "readable/maintainable".
=> That said: I've never used the 'reassemble' instruction before. If it does what I understand/assume it does, that might/will be a/the better/preferred solution. Will test this out in coming week...
___________________________
re: 'Jump far' instructions: since some time now - with the release of Win 10 and especially win 11 - I'm building (and "migrating") all my x64 tables to use 'jmp far' since standard/normal injections seem to fail regularly. I never had any trouble with them on Win 7/8, but users started to reporting this when running on Win 10. Basically, the injection "fails" somehow: actually what happens is pretty similar to what I've noticed with readMem here as well. (~ similar to what you see in image 6 above)
Note: does not seem to happen with all games, but it does happen plenty enough unfortunately...
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 150
Joined: 06 Jul 2014 Posts: 4646
|
Posted: Sun Jan 28, 2024 2:05 pm Post subject: |
|
|
That post you linked to was a bug in CE that existed more than a decade ago. Try it now and the syntax check completes without error.
And no, it has nothing to do with what you're doing wrong.
paul44 wrote: | Code: | aobscanmodule(RaceHorse,$process,...)
registersymbol(RaceHorse)
alloc(newmem,$200)
globalalloc(xRaceHorse,16)
xRaceHorse:
readMem(RaceHorse+3,16)
...
newmem:
...
readMem(xRaceHorse,16) |
|
ParkourPenguin wrote: | readmem / reassemble works before anything is written to memory.
Code: | aobscan(result, 12 34 ?? 56)
alloc(foo,4)
alloc(bar, 4)
foo:
readbytes(result,4) // works fine
bar:
readbytes(foo,4) // doesn't work: `foo` doesn't exist yet |
| The solution is to use the aobscan symbol.
Code: | newmem:
...
readMem(RaceHorse+3,16) | Again, see readmem vs reassemble. Make sure you know which one you should be using.
`jmp far ...` - If you're referring to this post:
https://fearlessrevolution.com/viewtopic.php?p=246176#p246176
an "offset too big" error is the fault of whoever made the table- e.g. trying to assemble an instruction that directly addresses a location more than 2 GiB away. If using `jmp far` seems to fix it, it doesn't- there are still problems in your table that just so happen to be ignored now.
The only reason why you should be forced to use `jmp far` is a failure to allocate memory near the preferred address. Unfortunately, it seems like CE gives the user the opportunity to ignore this error and see if it works anyway, so there's no guarantee user reports will be accurate.
With the state of AAA games now, I wouldn't be surprised if there are games where there is no free memory within 2 GiB of some injection point, and the preferred alloc might fail. These games are in the minority. Just because it doesn't work for a few games doesn't mean you should be using 14-byte jumps by default- a bigger injection point is prone to other errors far more difficult to find.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
paul44 Expert Cheater
Reputation: 2
Joined: 20 Jul 2017 Posts: 190
|
Posted: Mon Jan 29, 2024 12:26 pm Post subject: Confirmed working... |
|
|
^ I honestly did not grasp what you were getting at untill the 'newmem..." example.I never thought about trying that since I assumed - upon creating the injection - that it would return the jump_instruction bytes !
Anyways: I just tested it, and this works indeed as explained. Much appreciated as this is a very powerfull tool for this type of coding !
Will do more testing this week; if something comes up I'll update accordingly.
***
Just out of interest, how is an injection executed actually: I've always assumed that CE:
1. builds the injection code first
(the original code is still intact, hence readMem picks up the correct bytes, yes ?)
2. then - i assume - it'll make the original opcode location writeable (~protection flags)
3. introduces some sort of "locking" (BP perhaps)
4. writes the actual jmp instruction
5. removes "lock" (restores protection flags ?)
...
***
as for the 'jmp far' issue: yep, that is the correct topic.
And to give you an idea what happens: [ https://ibb.co/6gg91x6 ] (i got "lucky" with my 1st attempt)
=> this is a standard short jump injection: enabled and then disabled ?!
- the first 5 bytes are correct
- normal/correct opcode continues at address+2180818
=> it looks like CE tries to restore 14 bytes here (considering the 00 bytes at the "end")?
=> as i stated before: only started to happen with Win10+ (~ i have tested this with versions 7.1 up to 7.4)
If you know what is happening here, and how to prevent/workaround it then I'm more then willing to test things out...
note 1: the initial injection here is constructed correctly: them 5 bytes, plus jmp back to its proper location. However - if I recall well - in case of additional nopping, things will also go wrong on the injection side (although: do not take my word on that one; has been awhile now)
note 2: no issues at all with 'jmp far's (and yes, they can be a pain sometimes ~ which usually means I'll have to introduce a 'trampoline')
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 150
Joined: 06 Jul 2014 Posts: 4646
|
Posted: Mon Jan 29, 2024 3:53 pm Post subject: |
|
|
paul44 wrote: | Just out of interest, how is an injection executed actually... |
There are two stages: a syntax check and the real execution. The syntax check is just there to check the syntax of the script: nothing gets written to memory, no symbols are registered, and aobscans aren't even run (it's assumed the pattern exists). The second stage actually does everything.
([ENABLE] and [DISABLE] sections just get stripped as needed; think of them as C preprocessor macros)
Each stage goes through two passes: the first pass replaces code (i.e. {$lua} blocks, {$ccode} / {$luacode}, lua / plugin hooks, and reassemble / readmem), defines symbols (labels, allocs, aobscans, registersymbol, define...), and does some other miscellaneous things. The second pass does the other stuff: writes values to memory, creates threads, etc.
Specifically regarding readmem, the first pass replaces it with `db xx xx xx...`, and the second pass executes the `db` pseudoinstruction by writing the bytes to memory. This is the reason why one readmem can't read the memory written by another readmem in the same AA script execution.
paul44 wrote: | Code: | aobscanmodule(INJECT,game.exe,...)
alloc(newmem,$1000,game.exe+2180800)
... |
| You should replace the preferred address with the aobscan symbol (i.e. `alloc(newmem,$1000,INJECT)`).
The template should've done that for you, unless you're using an older version of CE.
I'd need more information to figure out what's going on there. What's the real address of that instruction (`print(('%X'):format(getAddress'INJECT' + 6))`)? Where was newmem allocated?
That might be a bug in CE. Make sure you're using the latest version of CE.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
paul44 Expert Cheater
Reputation: 2
Joined: 20 Jul 2017 Posts: 190
|
Posted: Tue Jan 30, 2024 1:08 pm Post subject: CE tests... |
|
|
prenote: out of principle, I'm still supporting v7.1 onwards; iow I use 7.1 to build my tables.
*********
just tested:
- v7.1: garbage every time (3x consecutive restarting game)
- v7.2: worked fine 2x (then moved on to v7.3)
- v7.3: 'failure allocating...' (iow no injection occured ~ I believe this type of error gave me the incentive to start looking in that direction)
(see also [ https://ibb.co/HKTrkpk ])
=> immediately tried v7.2 again: "garbage" injection
- v7.4: tried 3x, and seems fine with ever restart of the game
- v7.5: tried 2x AND it auto_creates a 'jmp far' instruction each time, even though i use them standard shortcuts !
(normally I have to use [Ctrl] additionally to force this; so CE does detect "something bizar" here ?!)
Some notes:
0. I'm pretty sure this also happens with v7.4 (was already available back then), but do not take my work for it...
At the moment, I do not use v7.5 unless for testing purposes (~ user reporting issues with it)
1. I always set my (global)alloc this way:
> alloc(newmem,$1000,Health) OR alloc(newmem,$1000,$process)
> globalalloc(pHealth,8,$process)
=> I recall a topic where it was stated that it really did not matter what game_offset is used as long as you reference to game_exe/dll in some way; because of the way CE allocates that memory ?!
iow: alloc(newmem,$1000,"tlou-i.exe"+263276D) ~ alloc(newmem,$1000,"tlou-i.exe") ~ alloc(newmem,$1000,$process) will/should all work fine
(i use $process for practical reasons: it's a very easy way to keep/test different game_versions)
2. when using 'jmp far' instruction set, one can NOT use the 3rd parameter; or else you'll get an allocation error pronto.
(v7.5 - see above - does not add that param either; which seems to confirm my observation !)
=> iow when "migrating" those particular tables, I had to remove the 3rd param everywhere
3. if you use only 'alloc()'s, it seems there is a better chance of "getting away with it"...
But it will happen at some point, though no crashing then (you'll get an allocation error and script won't enable at all ~ see above)
=> i vaguely recall a user telling me that either rebooting pc or relaunching game solved the issue... until next time
(~ the usual "it worked yesterday, but today it does not...")
fyi: i also just checked my AC (64b) tables: all of them use 'jmp far's now. And: Watch Dogs (also Ubi title but different game engine) still uses normal short jumps without issues (I happened to make a change just recently). WD makes use of a "preloader" with actual gamecode sitting in a DLL...
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 150
Joined: 06 Jul 2014 Posts: 4646
|
Posted: Tue Jan 30, 2024 2:53 pm Post subject: |
|
|
paul44 wrote: | out of principle, I'm still supporting v7.1 onwards | I don't understand why. All the bugs that come with that are your problem.
I'm not going to look through CE's Git history to figure out what's going on in previous versions.
Regarding only the latest version of CE (right now 7.5), my best bet is user error. Maybe you're not using the third parameter to alloc correctly, or maybe you chose to ignore the error about nearby allocation failure and tried to execute the script anyway.
Or maybe there really is a bug in CE and this just so happens to hit an edge case where nearby allocation succeeds but isn't actually nearby.
paul44 wrote: | 1. I always set my (global)alloc this way:
> alloc(newmem,$1000,Health) OR alloc(newmem,$1000,$process)
> globalalloc(pHealth,8,$process) | Again, that's wrong. The allocated memory being jumped to should be allocated near the injection point.
paul44 wrote: | when using 'jmp far' instruction set, one can NOT use the 3rd parameter; or else you'll get an allocation error pronto. | You can use the third parameter to alloc and `jmp far` together without getting an error. It's just useless to use the third parameter to alloc if you're using a 14-byte jump.
CE tutorial:
Code: | [ENABLE]
aobscanmodule(INJECT,Tutorial-x86_64.exe,29 83 F8 07 00 00 48 8D 4D F8 E8 45 DA FD FF)
alloc(newmem,$1000,INJECT) // works fine
label(return)
newmem:
sub [rbx+000007F8],eax
lea rcx,[rbp-08]
call Tutorial-x86_64.exe+8F10
jmp return
INJECT:
jmp far newmem
nop
return:
registersymbol(INJECT)
[DISABLE]
INJECT:
db 29 83 F8 07 00 00 48 8D 4D F8 E8 45 DA FD FF
unregistersymbol(INJECT)
dealloc(newmem)
{
// ORIGINAL CODE - INJECTION POINT: Tutorial-x86_64.exe+2B4BC
Tutorial-x86_64.exe+2B4AF: B9 05 00 00 00 - mov ecx,00000005
Tutorial-x86_64.exe+2B4B4: E8 57 47 FE FF - call Tutorial-x86_64.exe+FC10
Tutorial-x86_64.exe+2B4B9: 83 C0 01 - add eax,01
// ---------- INJECTING HERE ----------
Tutorial-x86_64.exe+2B4BC: 29 83 F8 07 00 00 - sub [rbx+000007F8],eax
// ---------- DONE INJECTING ----------
Tutorial-x86_64.exe+2B4C2: 48 8D 4D F8 - lea rcx,[rbp-08]
Tutorial-x86_64.exe+2B4C6: E8 45 DA FD FF - call Tutorial-x86_64.exe+8F10
Tutorial-x86_64.exe+2B4CB: 8B 8B F8 07 00 00 - mov ecx,[rbx+000007F8]
Tutorial-x86_64.exe+2B4D1: 41 B9 FF 00 00 00 - mov r9d,000000FF
Tutorial-x86_64.exe+2B4D7: 4C 8D 85 F8 FE FF FF - lea r8,[rbp-00000108]
} | The warning about nearby allocation failure will happen regardless of whether or not you're using `jmp far`.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
Dark Byte Site Admin
Reputation: 467
Joined: 09 May 2003 Posts: 25701 Location: The netherlands
|
Posted: Tue Jan 30, 2024 5:07 pm Post subject: |
|
|
if you have a game where it one time gives the message that nearby allocation fails, then better never use the 3th alloc parameter and always use jmp far or jmp1, or use your own jmp table in an unused block of data
Quote: |
so CE does detect "something bizar" here
|
CE can already see that nearby allocation is going to fail, so it won't even bother to write a script that will function with 5 byte jmps
_________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
 |
paul44 Expert Cheater
Reputation: 2
Joined: 20 Jul 2017 Posts: 190
|
Posted: Thu Feb 01, 2024 11:55 am Post subject: closing... |
|
|
I've got nothing else to add here; and what needed to be explained definitely got explained . I've now updated all appropriate scripts accordingly, and they are working fine as expected.
@ParkourPenguin: as always, much appreciated for taking the time AND patience to get me explain everything in detail.
cheers
|
|
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
|
|