随着 RPCS3 的崛起,自然会有不少人想要使用金手指在那些单人游戏。但是如果你尝试过直接在 RPCS3 上用 Cheat Engine 扫描功能的话想必你应该遇过一些问题,譬如说你想找的地址不存在,或者地址内存的字节倒反了。基于这些原因,这教程会教你如何简单的在 Cheat Engine 上做一些调整,让你能在 RPCS3 上使用基本扫描功能。但在开始前我必须强调虽然理论上这些设置适用于每个 RPCS3 里的游戏,而我也只在 EXVSFB 上做过实践测试,所以我不敢保证不会有出入。如果你看到你的游戏金手指教程的设置和这教程有些出入,那就请你使用配合你游戏的设置。
MEM_MAPPED 设置
首先,我们必须开启 Cheat Engine 里的 MEM_MAPPED 功能。开启这一个功能的目的是为了让 Cheat Engine 有能力扫描模拟器里游戏的内存。然而如果这项功能没开,Cheat Engine 只能扫描到模拟器本身的内存,而不是游戏的内存。换而言之对我们来说是没用的。
在 Cheat Engine 里,按下编辑 -> 设置
在设置窗口弹出后,按下扫描设置。
最重要的是 MEM_PRIVATE, MEM_IMAGE 和 MEM_MAPPED 都打了勾然后按下确定, 如图
逆向字节 (Big Endian) 支持
由于模拟器的内存方式不同,如果要扫描地址普通的字节是行不通的。在大部分模拟器里都需要逆向字节才能扫描。但是在普通的 Cheat Engine 里没有逆向字节的数值类型,所以我们必须自定义新的数值类型。
在 Cheat Engine 里, 鼠标右击数值类型旁的选项,然后选择定义新的“自定义类型”(自动汇编),如图
按下过后会有一个自动汇编的窗口弹出,把里面的东西全部删了,然后把下面这些代码贴入
alloc(TypeName,256)
alloc(ByteSize,4)
alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)
alloc(UsesFloat,1)
alloc(CallMethod,1)
TypeName:
db '逆向2字节',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]
贴入过后,你的自动汇编应该会和下面的图片内容一样。核对了过后便可按下确定
按下确定了过后,你便会发现数值类型里会多了一个逆向2字节的选项,如图
接下来我们用一样的方式把逆向4字节加入数值类型里。
alloc(TypeName,256)
alloc(ByteSize,4)
alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)
alloc(UsesFloat,1)
alloc(CallMethod,1)
TypeName:
db '逆向4字节',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]
按下确定了过后,在用一样的方式把逆向浮游加入数值类型里 。
alloc(TypeName,256)
alloc(ByteSize,4)
alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)
alloc(UsesFloat,4)
alloc(CallMethod,1)
TypeName:
db '逆向浮游',0
ByteSize:
dd 4
UsesFloat:
db 01
ConvertRoutine:
[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
bswap eax
pop ebp
ret 4
[/32-bit]
[64-bit]
//rcx=address of input
mov eax,[rcx] //eax now contains the bytes 'input' pointed to
bswap eax
ret
[/64-bit]
ConvertBackRoutine:
[32-bit]
push ebp
mov ebp,esp
//[ebp+8]=input
//[ebp+c]=address of output
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]
[64-bit]
//ecx=input
//rdx=address of output
bswap ecx
mov [rdx],ecx //place the integer the 4 bytes pointed to by rdx
ret
[/64-bit]
按下确定了过后,简单的设置就完成了!
你的数值类型应该会有3个新的选项
下一次扫描 RPCS3 游戏里的数据时,你便可以用逆向字节或逆向浮游。
接下来我们将会实践扫描 EXVSFB 里的数值,开启无限生命值等等。