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 


script local/global variables & nested labels & redu

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine
View previous topic :: View next topic  
Author Message
fun1
Newbie cheater
Reputation: 0

Joined: 04 May 2025
Posts: 16

PostPosted: Tue May 06, 2025 5:55 am    Post subject: script local/global variables & nested labels & redu Reply with quote

I have 2 scripts, which run at same time, and must inject in 2 different game functions (fyi, one for the player health by self damages, eg. fall from high, and the other for the damages inflicted by weapons)... they do exactly the same, amof find the logical solution was really very simple... but instead, it took few hours to make them functional in CE... the problems you will understand by the story...

first version scripts, I simply used the same template found in another approved working scripts from the forum:

Code:

[ENABLE] //script-1
aobscan(INJECT, *blabla-1 )
globalalloc(newmem,$1000)
label(newcode,return,saveinject)
registersymbol(saveinject)

newmem:
saveinject:
readmem(INJECT,6)
jmp return
newcode:
cmp ebx,1234
jne newmem
jmp return

INJECT:
  jmp newcode
  nop
return:

[DISABLE]
INJECT:
readmem(saveinject,6)
unregistersymbol(saveinject)

Code:

[ENABLE] //script-2
aobscan(INJECT, *blabla-2 )
globalalloc(newmem,$1000)
label(newcode,return,saveinject)
registersymbol(saveinject)

newmem:
saveinject:
readmem(INJECT,6)
jmp return
newcode:
cmp ebx,1234
jne newmem
jmp return

INJECT:
  jmp newcode
  nop
return:

[DISABLE]
INJECT:
readmem(saveinject,6)
unregistersymbol(saveinject)


... total crash... so after a lot of trial and suppositions, I thought, maybe variables "newmem" and "INJECT" and "saveinject" are global variables? by default shared between the 2 scripts? and mess in each other?
so I try set some unique variable names...

Code:

[ENABLE] //script-1
aobscan(INJECT1, *blabla-1 )
globalalloc(newmem1,$1000)
label(newcode,return,saveinject1)
registersymbol(saveinject1)

newmem1:
saveinject1:
readmem(INJECT1,6)
jmp return
newcode:
cmp ebx,1234
jne newmem1
jmp return

INJECT1:
  jmp newcode
  nop
return:

[DISABLE]
INJECT1:
readmem(saveinject1,6)
unregistersymbol(saveinject1)

Code:

[ENABLE] //script-2
aobscan(INJECT2, *blabla-2 )
globalalloc(newmem2,$1000)
label(newcode,return,saveinject2)
registersymbol(saveinject2)

newmem2:
saveinject2:
readmem(INJECT2,6)
jmp return
newcode:
cmp ebx,1234
jne newmem2
jmp return

INJECT2:
  jmp newcode
  nop
return:

[DISABLE]
INJECT2:
readmem(saveinject2,6)
unregistersymbol(saveinject2)

... slightly better... when I clicked to ENABLE didnt crash, but when I clicked to DISABLE, the scripts failed with error smtng. cant find readmem0...
I checked the injected code, and in memory view, the location at newmem1/2 is marked only newmem1/2, while the other label saveinject1/2 is nowhere/ignored...
So, I tried to get rid completely of the label saveinject1/2...

Code:

[ENABLE] //script-1
aobscan(INJECT1, *blabla-1 )
globalalloc(newmem1,$1000)
label(newcode,return)

newmem1:
readmem(INJECT1,6)
jmp return
newcode:
cmp ebx,1234
jne newmem1
jmp return

INJECT1:
  jmp newcode
  nop
return:

[DISABLE]
INJECT1:
readmem(newmem1,6)

Code:

[ENABLE] //script-2
aobscan(INJECT2, *blabla-2 )
globalalloc(newmem2,$1000)
label(newcode,return)

newmem2:
readmem(INJECT2,6)
jmp return
newcode:
cmp ebx,1234
jne newmem2
jmp return

INJECT2:
  jmp newcode
  nop
return:

[DISABLE]
INJECT2:
readmem(newmem2,6)


... and this finally worked! Confused
Now, my point is, I found it utterly confusing trying to "suppose" the local/global visibility of the variables(symbols?), and the need to lose a label (saveinject)... also, when nested labels, is confusing if must use "jne newmem1/2" or "jne saveinject1/2"... also, why nested labels don't READMEM, while still allowed...
(...btw, all this without preemptive warnings, and the same template used&approved in other scripts from the forum...)

Could it be possible to shed some light on what type of variables,labels,symbols(...) are local/global visibility, and what labels to use in jumps, and why nested labels don't work?
Also, in this very specific case, my 2 scripts are very redundant... would it be possible to do the same with only 1 script (containing both INJECT1/2)?
Thanks
Back to top
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 467

Joined: 09 May 2003
Posts: 25705
Location: The netherlands

PostPosted: Wed May 07, 2025 1:06 am    Post subject: Reply with quote

the script you found as example is likely bad as you should never use globalalloc unless you have a really really good reason to use it (e.g when it's impossible to free, which is not the case in your situation)
_________________
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
View user's profile Send private message MSN Messenger
fun1
Newbie cheater
Reputation: 0

Joined: 04 May 2025
Posts: 16

PostPosted: Wed May 07, 2025 3:01 am    Post subject: Reply with quote

Thanks for the point out... yes I understood that globalalloc is not common practice... but I had to shift to that out of frustration during the early attempts to fix the crash, since with simple alloc was keep crashing... Confused

Besides the globalalloc (which I confirm it works anyway), my questions were about... ok, I see my questions were maybe not very clear... I will try to summarize them again:

1) what type of variables,labels,symbols(...) in AA scripts are local/global visibility and/or shared? and how to discern the boundary between local and global?
2) what symbols must be declared? btw, I noticed in the saved file .ct, that CE seem to auto-define by itself (all alone) the allocs, and maybe also the aob, is that possible?
3) when in case of nested lables, what labels must use in the jumps (in my case, newmem* or saveinject*)?
4) why nested labels (or, some of them) don't READMEM, while still allowed?
5) in my very specific case, my 2 scripts are very redundant, would it be possible to do the same with only 1 script (containing both aobscans INJECT1&2)?
Thanks
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 150

Joined: 06 Jul 2014
Posts: 4652

PostPosted: Wed May 07, 2025 12:38 pm    Post subject: Reply with quote

I don't know what "template" you got that from or who "approved" it as working, but it's wrong. Even an AI wouldn't produce that level of crap.

Use the AOBScan template provided by CE.

1) Symbols defined by `globalloc` and passed to `registersymbol` are global. `globalalloc` will only allocate memory once, and that memory will never be deallocated.
2) You should declare all of them. Labels can be implicitly declared on their first use, but that can be dangerous if there's already a symbol with the same name (e.g. `memcpy` or some other API).
If you saved when a script was enabled, the values of the registered symbols might also be saved.
3) Labels aren't nested. `jmp` is an instruction that jumps to an address (the jmp after saveinject is useless).
4) `readmem` is a pseudoinstruction that copies memory. Don't use it unless you know you have a good reason to do so.
5) Yes, you can combine two scripts into one. Don't try to do that unless you know what all the code in the AA script does.

_________________
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
fun1
Newbie cheater
Reputation: 0

Joined: 04 May 2025
Posts: 16

PostPosted: Wed May 07, 2025 2:04 pm    Post subject: Reply with quote

Hi, thanks again for your answer, I have been digging in a lot of scripts, if you insist I can try to find back from where came that template, so you will be able to address your complaint about the level of human proficiency in CE directly to the author Wink

If you dont mind, I still would have a few curiosities to ask:

1) you wrote "Symbols defined by `globalloc` and passed to `registersymbol` are global.". Clear. What about symbols defined in simple ALLOC, global or local? What about symbols defined in AOBSCAN/AOBSCANMODULE, global or local? What about the other symbols defined by LABEL only, global or local? Whar about "children" scripts, do they follow the usual inheritance rule, such as variables/symbols declared in the parents are global/shared with the children, but not viceversa? or how? finally, do the "global" term in CE, means shared with *all* the scripts, or only with the children?

2) you wrote "You should declare all of them". That means it is common practice to REGISTERSYMBOL all of them, correct? why, if some of them are already automatically registered? Then, I understand that "the values of the registered symbols" are infact saved in the .ct file only "when a script" is in ENABLE state, correct? why such distinctive behavior?

3) you wrote "Labels aren't nested". When in the script I write one label and after another label, eg. "newmem: saveinject:", I call this nested. If you have another term for it, please suggest. In my logic, those 2 labels nest, alias are pointing at the same memory address, and are indeed accepted by CE. Unfortunately, only the first was found by readmem, the second not. Just to be sure, is this behavior expected result? Also you wrote, "the jmp after saveinject is useless", care to elaborate on that? Was it useless the declaration of a nested label saveinject? Or you didn't like the jump to a readmem? Or what? And why?

4) readmem came from that template, and I logically found it very useful (actually, mandatory), to backup the original instruction (from which,the need of "saveinject") before the injected jump overwrite... otherwise the original instruction is lost forever, and can't DISABLE the script anymore... moreover, if it's an instruction containing a dynamic address (as I see,it happens), I can't imagine no other way to get it back... where am I wrong?

5) Interesting. Would you suggest, just for the sake of educational, how you could combine my 2 scripts in one (the only difference is the bytes sequence *blabla-1&2 of aobscan)?

6) bonus question! Idea in general, why (or,how possible) sometimes games have the code all sitting in its module memory area (for which, is enough AOBSCANMODULE)? But others, have the code (talking about the code,not the data) allocated (dynamically?) somewhere else (for which, the need for the much longer AOBSCAN)? is it only a matter of totally different compilers, which are so drastically different in coding strategy? or what?
Thanks
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 150

Joined: 06 Jul 2014
Posts: 4652

PostPosted: Wed May 07, 2025 8:21 pm    Post subject: Reply with quote

1) Symbols defined by `alloc`, `label`, and `aobscan` are all local to the script. You can make them global by using `registersymbol`.
Parent / child memory records are only a way to organize the cheat table. Any two unique scripts are separate from each other. There is no form of inheritance.

2) No.
Code:
alloc(foo,4096)  // `foo` declared and defined as some address CE allocated

label(bar)  // `bar` declared

foo+100:
bar:  // `bar` defined as the address 0x100 bytes after the address "foo" refers to

foo+200:
baz:  // `baz` implicitly declared and defined as the address 0x200 bytes after the address "foo" refers to

foo:
  mov eax,[bar]
  mov [baz],eax
  ...
If `baz` was already a symbol (e.g. an export of some dll), it would not be defined as `foo+200` but instead used as is, making instructions like `mov [baz],eax` unintentionally do bad things.
Registered symbols are typically unregistered when the script is disabled. globalallocs are an exception since they can't be deallocated, but I don't know if they're saved to the .CT or not.

3) "alias" is a good term to use for two symbols that refer to the same address. "nested" implies one is inside of the other, which isn't the case.
Labels are defined relative to other addresses. Allocs and aobscans are defined by the AA command that also declared them. Perhaps that's the difference you saw. (see code above)
`readmem` with a registered symbol works fine for me. Maybe you unregistered "saveinject" before the other script could read from it, or maybe you edited the script while it was enabled and "saveinject1" and/or "saveinject2" didn't exist when you tried to disable it.
Regarding that `jmp` being useless, I was wrong. I didn't see the other jump `jne newmem` that executes the data read by `readmem`. That's bad for an entirely different reason- instructions can change their behaviour when relocated to a different address. If you're going to execute the original code without knowing what it is, you should use `reassemble` instead of `readmem`. Use a search engine to search these forums for more information.

4) The majority of injection points don't need that. The only time you need to use `reassemble` / `readmem` is when you have wildcards in the AoB pattern. If you don't need to use wildcards in the AoB pattern, the code the AOBScan template generates is fine.
Open an Auto Assembler script window and select Template -> AOB Injection

5) I'd make more modifications to those two scripts, but sure:
Code:
[ENABLE]
aobscan(uniqueNameInject1,...)
aobscan(uniqueNameInject2,...)

alloc(newmem1,2048,uniqueNameInject1)
alloc(newmem2,2048,uniqueNameInject2)

label(return1)
label(return2)
label(backupInject1)
label(backupInject2)


newmem1:
  cmp ebx,1234
  je @f
  reassemble(uniqueNameInject1)
@@:
  jmp return1

align 10 CC
backupInject1:
  readmem(uniqueNameInject1,6)


newmem2:
  cmp ebx,1234
  je @f
  reassemble(uniqueNameInject2)
@@:
  jmp return2

align 10 CC
backupInject2:
  readmem(uniqueNameInject2,6)


uniqueNameInject1:
  jmp newmem1
  nop
return1:

uniqueNameInject2:
  jmp newmem2
  nop
return2:

registersymbol(uniqueNameInject1)
registersymbol(uniqueNameInject2)
registersymbol(backupInject1)
registersymbol(backupInject2)


[DISABLE]

uniqueNameInject1:
  readmem(backupInject1,6)
uniqueNameInject2:
  readmem(backupInject2,6)

unregistersymbol(uniqueNameInject1)
unregistersymbol(uniqueNameInject2)
unregistersymbol(backupInject1)
unregistersymbol(backupInject2)

dealloc(newmem1)
dealloc(newmem2)


// comments at the end showing code around the injection point are important (use the AOBScan template)

6) The code that's in a module was statically compiled- e.g. C++, Rust. The code in dynamically allocated memory was JIT-compiled- e.g. Java, Unity. Search "JIT compilation" for more information.

_________________
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
fun1
Newbie cheater
Reputation: 0

Joined: 04 May 2025
Posts: 16

PostPosted: Thu May 08, 2025 2:32 am    Post subject: Reply with quote

Hi, thanks for the answers, that cleared some of the fog(-of-war) Wink

1) Finally: `registersymbol` turns the symbols into global/shared. Imho, this detail is of primordial importance, because global/shared visibility is *not* always desiderable (suggest: update wiki @Auto_Assembler:registerSymbol with this info)
About `globalalloc`, it's still not clear to me, why it's so much discouraged. Instead I find it, would say very "optimized", the idea to preserve a memory area for the exact same instructions, and do not allocate/deallocate it every time script ENABLE/DISABLE, because it saves a lot of cpu cycles. Care to elaborate a bit more on that?

3) Following your suggestions, I tried again, the following script fails to DISABLE (but with error different from that previous readmem0):
Code:

[ENABLE]
aobscan(INJECT1,11 22 * * * * 33 44 55 66 * * * * AA FF) // arbitrary wildcard aob
globalalloc(newmem1,$1000)
label(newcode,return,saveinject1) // fail,with or without saveinject1

newmem1:
saveinject1:
readmem(INJECT1,6)
jmp return
newcode:
cmp ebx,1234
jne newmem1 // fail,with saveinject1 or newmem1
jmp return

INJECT1:
  jmp newcode
  nop
return:

[DISABLE]
INJECT1:
readmem(saveinject1,6) // works only with newmem1

For some unknown to me reason, saveinject1 seems undefined in DISABLE, no matter defined in ENABLE... which would suggest that 'alias' labels are neither seen global nor local... do you agree?
Note also, the interesting behavioural change...when use `globalalloc`, injected memory reports allright newmem1... when use simple `alloc`, memory reports generic hex pointer, and disassembler turns to gibberish when try to browse just upper that hex pointer (very annoying)... hence, I still must stay a fan of globalalloc Wink

4) based on what I'm experiencing, I would not fully agree. Personally, I already gave up on "Template -> AOB Injection", because it scares me a lot when it freeze all, and after it always fails sistematically to find the correct unique aob, even in case of static code (maybe I was just unlucky, anyway I prefer much more your template here...)(yes I agree about the auto comments, its useful).
Yes, I found few other posts from you where you suggest `reassemble` instead of `readmem`, but you didn't seem to explain the reason (neither the wiki). Would you mind explaining the diffrence between `reassemble` and `readmem`, and why better use `reassemble` (with some practical examples,if possible)?

5) based on your definition of registersymbol at #1, I tried get rid completely of all registersymbol/unregistersymbol from your code, and all worked as expected Very Happy
Indeed, I really appreciated how CE didn't fail the return1/2 pointers... actually, in case of 2+ different inject points, how CE knows what is the right return pointer? for example, why CE didn't interpret return1 & uniqueNameInject2 as 'alias', since just under each other? what is the correct logic we should follow? Razz
About the `align` (assembler directive?) was it to pad after the `jmp return` to avoid disassembler gibberish? why specifically 10 bytes?
About the instructions "je @f @@:" , that would be CE nomenclature, correct?

6) Is it possible that, by any chance, idtech3 would fall under the category of "JIT compilation"?
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 150

Joined: 06 Jul 2014
Posts: 4652

PostPosted: Thu May 08, 2025 6:13 pm    Post subject: Reply with quote

1) For small things, globalalloc is fine. e.g. store an address the game is using so you can use it elsewhere in the cheat table.
Unless you enable and disable a script hundreds of times per second, you'd never notice the difference between a script that reuses memory and a script that allocates / deallocates memory. You have already noticed that using globalalloc liberally makes it very easy to screw things up- i.e. reusing `newmem`.

2) Need `registersymbol(saveinject1)` / `unregistersymbol(saveinject1)`

Enabling the script and disabling the script are two separate invocations of the auto assembler. When the script is enabled, some extra information called "disable info" is created to help disable the script. Some symbols are saved in the disable info- notably allocs. Not much else is. Standard labels are not saved- you have to use `registersymbol` so the AA script in the [DISABLE] section knows what it was. See my script in the previous example.

The stuff about the disassembler has to do with alignment of instructions in the disassembler. It has nothing to do with globalalloc vs alloc. The only thing that changes is CE will display any registered symbols in place of the address in the disassembler by default.

4) If CE can't find a unique AOB for you, you probably won't be able to find one either. (wildcards don't help with unique AOBs)
`readmem` reads some number of bytes from an address and writes them at the address where `readmem` is placed.
`reassemble` disassembles the instruction at the specified address and re-assembles it where `reassemble` is placed.
The reason you should use `reassemble` instead of `readmem` when executing code is because some instructions change what they do depending on the address they're located at. Copying the bytes of an instruction from one location to another (i.e. `readmem`) can change what that instruction does.
Code:
newmem:
  db EB 00  // jmp newmem+2
  db EB 00  // jmp newmem+4  (simulating readmem- same bytes, different instruction)
  db EB FC  // jmp newmem+2  (simulating reassemble- same instruction, different bytes)
You should still use `readmem` for backing up and restoring the injection point.

5) No it doesn't. `backupInject1` / `backupInject2` must be registered so `readmem` knows what they are in the [DISABLE] section. You already showed it doesn't work with the code you posted in section 3.

return1 & uniqueNameInject2 are not "just under each other". return1 is a label defined relative to uniqueNameInject1 (specifically 6 bytes after uniqueNameInject1), and uniqueNameInject2 was defined by the aobscan.

Adding 0xCC padding makes the separation between code and data clearer in the disassembler. 0x10 (16) bytes is arbitrary.

6) It's written in C, so I doubt it.

_________________
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
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine 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