cheat engine 内联 C 的使用
此内容年代久远,谨慎参考
为群友提供的文字版备忘录,适用 7.3.0.11 以上版本,使用 Tiny C 编译器。
§简介
内联 C 是 7.3 版本增加的新功能,包括 $CCODE 和 $C 代码块。
性能排行:汇编($ASM) > 内联 C($CCODE/$C) > 内联 LUA($LUACODE) > 外部 LUA($LUA)。
内联 C 被编译到目标进程空间中,由于实现方式的原因,有一定的性能损失,相对适合在频繁调用的方法里面,比如 GameLoop.Update()
。
内联 LUA 无依赖的部分被编译到目标进程空间中,有依赖的部分仍然在 CE 中(lua-server),由于实现方式的原因,有一定的性能损失,饥荒这个特例有巨额开销,尽可能不要放在频繁调用的方法里面,比如 GameLoop.Update()
。
外部LUA 基于 CE(lua-server)运行,如果产生数据交互则带来较大开销,一般用于检查、搜索、备份内存使用。
§函数定义
c/*
* $C 中定义的函数和变量可直接在 $CCODE 中使用,且是全局注册符号(即在 CE 的地址列表中也可直接使用)
* 使用 C 函数之前,可以先在内存浏览器中检查是否已导出,否则需要在 $C 中手动导出
* 建议使用到的函数都手动导出,避免被重定向到非目标重载,我自己被这个坑过几次了
*/
/*{$C}*/
void changeValue(int currentValue,int newValue){ currentValue=newValue; }
int getValue(int currentValue){return currentValue+100;}
int setMaxValue(unsigned int *ptrObject){ *(int *)(ptrObject+0x20)=100; }
extern void MessageBoxA(int,const char *,const char *,int);
extern void _vsnprintf_s(char *,int,int,const char *,...va_list)
extern void OutputDebugStringA(const char *)
/*{$ASM}*/
§变量定义
c/*
* $C 中定义的函数和变量可直接在 $CCODE 中使用,且是全局注册符号(即在 CE 的地址列表中也可直接使用)
* healthAddress 友军生命值地址
* godModeSwitch 无敌开关(boolean)
* *ptrStruct 结构指针
* (unsigned int) 与 (unsigned int *) 的区别是,前者在 CE 中使用 [healthAddress] 取得数值,而后者使用 [[moneyAddress]+0]
*/
/*{$C}*/
unsigned int healthAddress=0x00;
unsigned char godModeSwitch=0x00;
unsigned int *moneyAddress=0x00;
/*{$ASM}*/
§引用寄存器
c/*
* 此代码等同汇编
* movaps [align16_memory_address],xmm0
* mov esi,[align16_memory_address]
* mov [eax],esi
* mov esi,[align16_memory_address+04]
* mov [ebx],esi
*/
/*{$CCODE refEAX=eax refEBX=ebx refXMM0=xmm0}*/
*(float *)refEAX=refXMM0.0F;
*(float *)refEBX=refXMM0.1F;
/*{$ASM}*/
/*
* 此代码等同汇编
* movaps [align16_memory_address],xmm0
* mov rsi,[align16_memory_address]
* mov [rax],rsi
* mov rsi,[align16_memory_address+08]
* mov [rbx],rsi
*/
/*{$CCODE refRAX=rax refRBX=rbx refXMM0=xmm0}*/
*(double *)refRAX=refXMM0.0D;
*(double *)refRBX=refXMM0.1D;
/*{$ASM}*/
§逻辑调试
c/*{$CCODE refEAX=eax}*/
unsigned int * ptrEAX = refEAX;
char * ptrStringA1 = (char *)malloc(32);
int v1 = _vsnprintf_s(ptrStringA1,32,31,"ptrEAX => 0x%x",ptrEAX);
OutputDebugStringA(ptrStringA1);
free(ptrStringA1);
/*{$ASM}*/
§综合使用
c// eax 生命值地址
// ebp-08 人物指针
// xmm0 [旧生命值,新生命值,最大生命值,无用数据]
// xmm0.2F 最大生命值
/*{$CCODE refEAX=eax refEBP=ebp refXMM0=xmm0 refXMM0S2=xmm0.2F}*/
unsigned int *ptrEntityStruct=(unsigned int *)(refEBP-0x08);
unsigned char *ptrEntityType=(unsigned char *)(*ptrEntityStruct-0x41);
unsigned int *ptrHealth=(unsigned int *)refEAX;
switch(*ptrEntityType){
// 友军
case 0x01:
*ptrStruct=*ptrEntityStruct;
healthAddress=ptrHealth;
if(godMode==0x01){
*(unsigned int *)refEAX=refXMM0S2;
}else{
*(unsigned int *)refEAX=refXMM0.1F;
}
break;
// 敌人
case 0x02:
*(unsigned int *)refEAX=0F;
break;
// 中立
case 0x03:
*(unsigned int *)refEAX=refXMM0.1F;
break;
// 其它(默认情况下不能直接定义常量字符串,我这里只是演示)
default:
MessageBoxA(0,"遇到问题","当前结构无法判断类型",0);
break;
}
/*{$ASM}*/
§实例参考
- 死寂 => steam-[DeathlyStillness-Win64-Shipping.exe]-amd64-33F2BC694D01EDFACB3690B71976EE382AB7A866.ct
- 尾牙 => steam-[ToothAndTail.exe]-x86-9E419FD77C889D7646AED5967A580DB4DFC20557.ct
- 刺客信条编年史:中国 => upc-[ACCGame-Win32-Shipping.exe]-x86-54BC49A4134AB7F62DF4F164AA9524D3F4F60CF4-ragnaroks.ct