【翻译】让cheat engine(CE)支持大字节序搜索

Cheat Engine是当下我所知的唯一好用的Windows下的内存修改器。这个工具有个巨大的缺陷——搜索的数据只能以小端(small endian)排列。搜索双字节或四字节数据时,并不像国人开发的上个时代的FPE或者金山游侠那样,自动转换字节序,只能先用计算器把数据拆成字节,然后用字符串数组才能搜索到结果,非常不便。
今天偶然被我找到了支持多字节大端字节序的方法,特此分享。

来自作者的github

照例,我没用过汉化版的CE,故直接用英文菜单描述,如有不便请忍耐。

1.打开Cheat Engine
2.打开任意进程
3.在【Value Type】下拉列表框上单击鼠标右键
4.点击【Define new custom type (Auto Assembler)】
20231120_cheat_engine_1
5.用下面的代码覆盖弹出对话框里的代码
6.点击OK
7.重复步骤3-6,把三份代码都添加进去。分别是float的大字节序(Float Big Endian),四字节的大字节序(4 Bytes Big Endian)和双字节的大字节序(2 Bytes Big Endian)。
8.重启CE(否则新添加的工具不好用)

代码1:Float Big Endian

alloc(TypeName,256)
alloc(ByteSize,4)
alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)
alloc(UsesFloat,1)
 
 
TypeName:
db 'Float Big Endian',0
 
 
ByteSize:
dd 4

UsesFloat:
db 1
 
 
ConvertRoutine:

[64-bit]

xor eax,eax
mov eax,
[rcx]
 //eax now contains the bytes 'input' pointed to
bswap eax //convert to big endian
ret

[/64-bit]



[32-bit]

push ebp
mov ebp,esp
mov eax,
[ebp+8]
 //place the address that contains the bytes into eax
mov eax,
[eax]
 //place the bytes into eax so it's handled as a normal 4 byte value
bswap eax
pop ebp
ret 4

[/32-bit]

 
 
ConvertBackRoutine:

[64-bit]

bswap ecx //convert the little endian input into a big endian input
mov 
[rdx]
,ecx //place the integer the 4 bytes pointed to by rdx
ret

[/64-bit]



[32-bit]

push ebp
mov ebp,esp
push eax
push ebx
mov eax,
[ebp+8]
 //load the value into eax
mov ebx,
[ebp+c]
 //load the address into ebx
bswap eax
 
mov 
[ebx]
,eax //write the value into the address
pop ebx
pop eax
pop ebp
ret 8

[/32-bit]

代码2:4 Bytes Big Endian

alloc(TypeName,256)
alloc(ByteSize,4)
alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)

TypeName:
db '4 Bytes Big Endian',0

ByteSize:
dd 4

//The convert routine should hold a routine that converts the data to an integer (in eax)
//function declared as: stdcall int ConvertRoutine(unsigned char *input);
//Note: Keep in mind that this routine can be called by multiple threads at the same time.
ConvertRoutine:
//jmp dllname.functionname

[64-bit]

//or manual:
//parameters: (64-bit)
//rcx=address of input
xor eax,eax
mov eax,
[rcx]
 //eax now contains the bytes 'input' pointed to
bswap eax //convert to big endian

ret

[/64-bit]



[32-bit]

//jmp dllname.functionname
//or manual:
//parameters: (32-bit)
push ebp
mov ebp,esp
//
[ebp+8]
=input
//example:
mov eax,
[ebp+8]
 //place the address that contains the bytes into eax
mov eax,
[eax]
 //place the bytes into eax so it's handled as a normal 4 byte value

bswap eax

pop ebp
ret 4

[/32-bit]


//The convert back routine should hold a routine that converts the given integer back to a row of bytes (e.g when the user wats to write a new value)
//function declared as: stdcall void ConvertBackRoutine(int i, unsigned char *output);
ConvertBackRoutine:
//jmp dllname.functionname
//or manual:

[64-bit]

//parameters: (64-bit)
//ecx=input
//rdx=address of output
//example:
bswap ecx //convert the little endian input into a big endian input
mov 
[rdx]
,ecx //place the integer the 4 bytes pointed to by rdx

ret

[/64-bit]



[32-bit]

//parameters: (32-bit)
push ebp
mov ebp,esp
//
[ebp+8]
=input
//
[ebp+c]
=address of output
//example:
push eax
push ebx
mov eax,
[ebp+8]
 //load the value into eax
mov ebx,
[ebp+c]
 //load the address into ebx

//convert the value to big endian
bswap eax

mov 
[ebx]
,eax //write the value into the address
pop ebx
pop eax

pop ebp
ret 8

[/32-bit]

代码3:2 Bytes Big Endian

alloc(TypeName,256)
alloc(ByteSize,4)
alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)

TypeName:
db '2 Bytes Big Endian',0

ByteSize:
dd 2

//The convert routine should hold a routine that converts the data to an integer (in eax)
//function declared as: stdcall int ConvertRoutine(unsigned char *input);
//Note: Keep in mind that this routine can be called by multiple threads at the same time.
ConvertRoutine:
//jmp dllname.functionname

[64-bit]

//or manual:
//parameters: (64-bit)
//rcx=address of input
xor eax,eax
mov ax,
[rcx]
 //eax now contains the bytes 'input' pointed to
xchg ah,al //convert to big endian

ret

[/64-bit]



[32-bit]

//jmp dllname.functionname
//or manual:
//parameters: (32-bit)
push ebp
mov ebp,esp
//
[ebp+8]
=input
//example:
mov eax,
[ebp+8]
 //place the address that contains the bytes into eax
mov ax,
[eax]
 //place the bytes into eax so it's handled as a normal 4 byte value
and eax,ffff //cleanup
xchg ah,al //convert to big endian

pop ebp
ret 4

[/32-bit]


//The convert back routine should hold a routine that converts the given integer back to a row of bytes (e.g when the user wats to write a new value)
//function declared as: stdcall void ConvertBackRoutine(int i, unsigned char *output);
ConvertBackRoutine:
//jmp dllname.functionname
//or manual:

[64-bit]

//parameters: (64-bit)
//ecx=input
//rdx=address of output
//example:
xchg ch,cl //convert the little endian input into a big endian input
mov 
[rdx]
,cx //place the integer the 4 bytes pointed to by rdx

ret

[/64-bit]



[32-bit]

//parameters: (32-bit)
push ebp
mov ebp,esp
//
[ebp+8]
=input
//
[ebp+c]
=address of output
//example:
push eax
push ebx
mov eax,
[ebp+8]
 //load the value into eax
mov ebx,
[ebp+c]
 //load the address into ebx

//convert the value to big endian
xchg ah,al

mov 
[ebx]
,ax //write the value into the address
pop ebx
pop eax

pop ebp
ret 8

[/32-bit]

搞定!试试效果:
20231120_cheat_engine_2

已有4条评论

  1. 怪不得CE有时候不好用,原来不原生支持大端序

    1. 以前用过一个改DOS游戏的工具也是这样,养成了搜不出来就开计算器去掉高位的习惯。

  2. CE内存高低位好像没什么用吧?有什么必须用到的场景?我好像有一次传文件出了问题,一顿搜索求助发现是大小端序不匹配出的问题。。。

    1. 有点用,不然每次都得开计算器取模。传文件一般选binary模式。

  3. 这工具的确好用,现在玩老游戏还是偶尔会用它来“爽”,虽然不太会高级功能……

    1. 其实我更喜欢用FPE2001。但是到了Win10时代再也用不了了。

你好,新朋友。留言前请先填写昵称邮箱