Auto Assembler:CCODE
CE 7.3 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: