Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


How to make Cheat Engine use my functions to read memory?

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Source -> Plugin development
View previous topic :: View next topic  
Author Message
HSergi
Newbie cheater
Reputation: 0

Joined: 17 Dec 2017
Posts: 22

PostPosted: Sun Jul 11, 2021 10:19 am    Post subject: How to make Cheat Engine use my functions to read memory? Reply with quote

How to make Cheat Engine use my functions to read memory?

I have a lot of encrypted primitive values in the game, for which I would like to scan. I have found encryption and decryption functions but I can't scan with "Bigger than" and "Smaller than" options in Cheat Engine because the values are encrypted.

So before comparing in-game memory to something like 1 and seeing if it's bigger than, I need to first run the memory through the decryption function. That sounds like a job for a plugin(is there an easier way?). So I googled it and found it on the wiki.

It says I need to export 3 functions: GetVersion, DisablePlugin, and InitializePlugin, but doesn't show their definitions and doesn't give links to the pages. So I searched for these words on wiki.

BOOL GetVersion(PPluginVersion pv,int sizeofpluginversion);. I need to define the PPluginVersion struct, which I found on another page by searching the wiki. So I define this in my Dll project:
Code:
   typedef struct _PluginVersion
   {
       unsigned int version;
       char *pluginname;
   } PluginVersion, *PPluginVersion;



It says to set the version member to the plugin version the plugin is expecting? How do I know which plugin version my plugin should be expecting? What do I set this to?

Plugin name is self-explanatory.

BOOL InitializePlugin(PExportedFunctions ef,int pluginid);. The wiki doesn't have a definition of the PExportedFunctions struct. It only shows the InitializePlugin page. The ExportedFunctions shows on the Help_File page on the wiki but the page itself doesn't exist. What's the definition for the ExportedFunctions struct? And what do I put in the ef argument inside the function once I have the definition for the struct?

On the InitializePlugin wiki page it says:
Quote:
It is recommended to register the callback functions in this routine and save ExportedFunctions for later usage. For more information about the callbacks see the relevant help topics

It says to see relevant help topics, but what topics?
Says to register the callback functions in this routine and save the ExportedFunctions for later usage. Should I save the pointer(the ef member) for later usage so I can set pointers to my own functions later, or should I copy the struct ExportedFunction, which PExportedFunctions ef member is pointing to?


pluginid parameter? Is it just arbitary plugin id I should set or is it plugin id that was given to my plugin by Cheat Engine? Looking at the type of the argument - int, probably plugin ID given to me by the Cheat Engine.

BOOL DisablePlugin(void). This one is easy to define: __declspec(dllexport) BOOL DisablePlugin() { return true; }.

So I got to this so far in my dll project:
Code:
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>

typedef struct PluginVersion {
    unsigned int version;
    char* pluginname;
}* PPluginVersion;

__declspec(dllexport) BOOL GetVersion(
    PPluginVersion pv, //pointer to structure you have to fill in
    int sizeofpluginversion //size of pluginversion
) {

    return true;
}

__declspec(dllexport) BOOL InitializePlugin(
    PExportedFunctions ef,
    int pluginid
) {
    return true;
}

__declspec(dllexport) BOOL DisablePlugin() {
    return true;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


And PExportedFunctions isn't defined, so I can't compile.

After defining PExportedFunctions(where do I find the definition for it?), what else do I need to do to read the virtual memory, pass the read value through the decryption function and only then pass it to the Cheat Engine(the decrypted value), so the smaller than and bigger than comparisons work? At the moment I'm interested in 4 byte values only.

Will I have to set a pointer to my own function in the PExportedFunctions ef argument? That would be my guess.
Back to top
View user's profile Send private message Send e-mail
ParkourPenguin
I post too much
Reputation: 137

Joined: 06 Jul 2014
Posts: 4250

PostPosted: Sun Jul 11, 2021 12:07 pm    Post subject: Reply with quote

I'm not familiar with these types of plugins, but:
Quote:
That sounds like a job for a plugin(is there an easier way?)
My first thought is adding a custom type.

If you know assembly well enough, you can write it yourself; otherwise, use a compiled language to generate it for you.
(if you don't care about fast scans, you can use a Lua custom type instead)

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 457

Joined: 09 May 2003
Posts: 25252
Location: The netherlands

PostPosted: Sun Jul 11, 2021 2:37 pm    Post subject: Reply with quote

ExportedFunctions has a pointer to the pointer used for readProcessMemory

Change that pointer to your implementation

_________________
Do not ask me about online cheats. I don't know any and wont help finding them.

Like my help? Join me on Patreon so i can keep helping
Back to top
View user's profile Send private message MSN Messenger
HSergi
Newbie cheater
Reputation: 0

Joined: 17 Dec 2017
Posts: 22

PostPosted: Tue Jul 13, 2021 7:53 am    Post subject: Reply with quote

ParkourPenguin wrote:
I'm not familiar with these types of plugins, but:
Quote:
That sounds like a job for a plugin(is there an easier way?)
My first thought is adding a custom type.

If you know assembly well enough, you can write it yourself; otherwise, use a compiled language to generate it for you.
(if you don't care about fast scans, you can use a Lua custom type instead)

Thanks, I'll look into this now, I never tried anything with custom types in CE.

Dark Byte wrote:
ExportedFunctions has a pointer to the pointer used for readProcessMemory

Change that pointer to your implementation


Thanks, I went to Cheat Engine github repo, cloned it, found example in C and copied the parts needed for a minimalist plugin: lua headers, cepluginsdk.h
Code:

#include <windows.h>

#include "CheatEngineSDK.h" // header from the c-example

#define CESDK_VERSION 6;
int selfid;
ExportedFunctions Exported;

BOOL __stdcall MyReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead) {
    return ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead);
}

typedef struct PluginVersion {
    unsigned int version;
    char* pluginname;
}*PPluginVersion;

BOOL __stdcall CEPlugin_GetVersion(PPluginVersion pv, int sizeofpluginversion) {
    pv->version = CESDK_VERSION;
    pv->pluginname = "C Example v1.3 (SDK version 4: 6.0+)";
    return true;
}

BOOL __stdcall CEPlugin_InitializePlugin(PExportedFunctions ef, int pluginid) {
    selfid = pluginid;

    //copy the EF list to Exported
    Exported = *ef; //Exported is defined in the .h

    if(Exported.sizeofExportedFunctions != sizeof(Exported))
        return FALSE;

    *ef->ReadProcessMemory = MyReadProcessMemory;

    Exported.ShowMessage("The \"Example C++\" plugin got enabled");

    return true;
}

BOOL __stdcall CEPlugin_DisablePlugin(void) {
    return true;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


I just forward the calls to Windows' RPM function in the MyReadProcessMemory.

Now I have 2 problems. First problem is that when I add and enable the plugin, Cheat Engine doesn't call MyReadProcessMemory function at all:
Code:

youtu.be/ay8GZdJE87M


This is the link to YT video because I can't post proper links(DarkByte, can you give me the right to post link, please?).

It only calls MyReadProcessMemory function only when plugin is loaded at the start of the program:
Code:

youtu.be/mYAMHG_8nQ0



At first, I thought I reassigned the *ef->ReadProcessMemory pointer incorrectly, but as you can see if I start Cheat Engine when plugin was already previously added and enabled, Cheat Engine starts to use MyReadProcessMemory function. I think I reassigned it correctly, did I?

This brings us to the second problem: even if Cheat Engine calls MyReadProcessMemory function, it calls if for many things and not only for scans, in which I'm insterested.

I looked into the c-example example and I couldn't find how I can figure out if the scan is happening or if Cheat Engine is using RPM for its own stuff. Also, even if I could determine when it uses RPM for scans, there is another problem: when it scans memory, it only reads 1 byte at a time(even when size to scan is set for 4 bytes), so the nSize parameter is always 1, but my values that are encrypted are 4 bytes long.

All I need to do is to call my own function with this signature(before I pass the result to Cheat Engine): inline int __fastcall Decrypt(int valueToDecrypt, __int64 encryptionKey) when Cheat Engine scans for 4-byte values.

Here's the link to the solution if you guys want to reproduce:
Code:

github com/Revester/Read-Memory/tree/main/Read%20Memory


Please help.
Back to top
View user's profile Send private message Send e-mail
Dark Byte
Site Admin
Reputation: 457

Joined: 09 May 2003
Posts: 25252
Location: The netherlands

PostPosted: Tue Jul 13, 2021 9:02 am    Post subject: Reply with quote

there is an API pointerchange event you can register for.
This is triggered when CE changes the pointer for rpm/wpm etc... e.g when clicking ok in settings

CE scans using 512KB RPM calls so you'll have to check the address and modify it in there


Also, it sounds more like you wish to do a custom type

_________________
Do not ask me about online cheats. I don't know any and wont help finding them.

Like my help? Join me on Patreon so i can keep helping
Back to top
View user's profile Send private message MSN Messenger
HSergi
Newbie cheater
Reputation: 0

Joined: 17 Dec 2017
Posts: 22

PostPosted: Thu Jul 15, 2021 4:26 pm    Post subject: Reply with quote

Thanks guys, I looked into the custom types and managed to convert encrypted values and scan for them.

Post the code for completeness:
Code:

alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)
alloc(TypeName,256)
alloc(ByteSize,4)
alloc(UsesFloat,1)
alloc(CallMethod,1)

TypeName:
db 'Encrypted Stats Auto Assembler',0

ByteSize:
dd 4

UsesFloat:
db 0 //Change to 1 if this custom type should be treated as a float

CallMethod:
db 1 //Remove or change to 0 for legacy call mechanism

//The convert routine should hold a routine that converts the data to an integer (in eax)
//function declared as: cdecl int ConvertRoutine(unsigned char *input, PTR_UINT address);
//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
//rdx=address
mov ecx,[rcx] //eax now contains the bytes 'input' pointed to
push rdi
sub rsp,20
mov edi,ecx
xor edi,1D
mov [rsp+30],rbx
movzx eax,dil
mov rbx,EDCCFB96DCA40FBA
and al,1F
jna NotAbove
movzx ecx,al
mov rdx,8000000000000000
nop word ptr [rax+rax+00000000]
NotZero:
test rdx,rbx
mov eax,00000000
setne al
lea rbx,[rax+rbx*2]
sub rcx,01
jne NotZero
NotAbove:
and ebx,-20
xor ebx,edi
shl ebx,08
mov eax,ebx
mov rbx,[rsp+30]
mov ecx,0x100
cdq
idiv ecx
add rsp,20
pop rdi
ret
[/64-bit]

[32-bit]
//jmp dllname.functionname
//or manual:
//parameters: (32-bit)
push ebp
mov ebp,esp
//[ebp+8]=address of input
//[ebp+c]=address
//example:
mov ecx, [ebp+8]

ret
[/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: cdecl void ConvertBackRoutine(int i, PTR_UINT address, unsigned char *output);
ConvertBackRoutine:
//jmp dllname.functionname
//or manual:
[64-bit]
//parameters: (64-bit)
//ecx=input
//rdx=address
//r8=address of output
//example:

mov [r8],ecx //place the integer at the 4 bytes pointed to by r8
ret
[/64-bit]

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

pop ebp
ret
[/32-bit]

Back to top
View user's profile Send private message Send e-mail
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Source -> Plugin development All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites