Auto Assembler:CCODE

From Cheat Engine
Revision as of 13:47, 29 November 2021 by Dark Byte (talk | contribs)
Jump to navigation Jump to search

CE 7.2 added {$CCODE} / {$ASM} blocks

Within these blocks you can write C code which will get executed by the target process at the spot it is located. It's also possible to debug through these blocks while viewing the original C code and you can look up the variable values/

CCode blocks can access AA variables and other symbols, as long as you declare them properly in the c code using the `extern` keyword

You can give the {$CCODE} block parameters so you have access to certain registers in a format you can work with. When you reach the end of the block (So no 'return') the changed variables will be applied back to the related registers.

Most of the parameters are formatted as variablename=registername seperated by spaces but there are also parameters like KERNELMODE, NODEBUG and PREFIX=something KERNELMODE will allocate the code in kernelmode, NODEBUG will skip the generation of debug code (You won't be able to see the c-code when stepping through it with the debugger) and PREFIX=something will add the text `something.` in front of the symbols, so you can distinguish in the disassembler between symbols of scripts that use the same variable names

The following register notations are allowed:

 RAX/EBX, RBX/RCX, ...: Interpret as a 8/4 byte long
 RAXF,RBXF,RCXF, ... : Interpret value as float  
 XMM0.0 or XMM0.0F (float)
 XMM0.1 or XMM0.1F (float)
 ....
 XMM1.0
 ...
 XMM0.0D (double)
 ... ​

If you just use XMM0 to XMM15: then the variable will be of the following typedef:

 ​typedef struct {
       ​union{
         ​struct{
             ​float f0;
             ​float f1;
             ​float f2;
             ​float f3;
         ​};
         ​struct{
             ​double d0;
             ​double d1;
         ​};
         ​float fa[4];
         ​double da[2];
       ​};
     ​} xmmreg, *pxmmreg; 
‎

and to access double 0 of XMM0 you'd then do varname.d0 or varname.da[0]


example: RCX contains the classinstance of the player, RBX the new health after being hit, and you know that at offset b8 the 4 byte value is 1 when it's player:

{$CCODE playerbase=RCX newhealth=RBX}
int isplayer=*(*int)(playerbase+0xb8);
if (isplayer)
 ​newhealth=100000;
else
 ​newhealth=0;
{$ASM}
‎

this will change the rbx register to 100000 when it's the player, and 0 when it's not

Example of declaring a function if the default guess is wrong: (in this case sprintf which takes a variable parameter count, so has to be cdecl)

{$CCODE decvalue=eax}
extern __cdecl int sprintf(char *, char *, ...);  //will likely pick ntdll.sprintf
‎

e.g in a full AA script:

alloc(newmem,1024,"Tutorial-x86_64.exe"+2B4BC)
label(returnhere)
label(originalcode)
label(exit)

alloc(msglog,1024)
alloc(msglogindex,4)
registersymbol(msglog)


newmem: //this is allocated memory, you have read,write,execute access
//place your code here

{$ccode decval=eax}
extern char msglog[1024];
extern int msglogindex;
extern __cdecl int sprintf(char *, char *, ...);  //will likely pick ntdll.sprintf

if (msglogindex<1000)
{
  int size=sprintf(&msglog[msglogindex], "decval=%d\n\r",decval);
  msglogindex+=size;
}


{$asm}

originalcode:
sub [rbx+000007F8],eax

exit:
jmp returnhere

"Tutorial-x86_64.exe"+2B4BC:
jmp newmem
nop
returnhere:
‎