Runtime memory editor library

Ruit

Adminstrator
Adminstrator


I will explain in this tutorial how code patching & generally editing memory at runtime.

First let's talk about requirements and my environment that I'm using for this.

Environment:
- Make installed and added to the PATH environment variable.
- NDK r17c installed and added to the PATH environment variable.

Requirements:
C++ knowledge.

many modders can apply their patches by editing binary file with any hex editor, but I've been asked how to apply them at real time.
editing self process memory does not require root access or anything special, and can be done directly. for this tutorial I'm using memcpy to copy bytes from a buffer into an address and vice versa.
I'll be using this only for code patching, but for pointers, they can be directly dereferenced and assigned to any value as long as the object isn't a constant object.
You can check the functions readPointer & writePointer to see how it works with pointers. I won't explain how to work with pointers in this tutorial because It's really some basic stuff in c++ that you can learn on internet.

Let's now start with a simple code patch example because i don't really like to write long articles.
First we include the necessary headers to work with patches:
Code:
#include "KittyMemory/MemoryPatch.h"
I'm going to use a posix thread to run my patches, this would be smart because we don't want to slow or interrupt the execution of the main process thread.
So we are going to include that as well:
Code:
#include <pthread.h>
Now we define our thread function, the prototype of pthread functions is like:
C++:
void *functionName(void *args);
C++:
void *my_test_thread(void *) {
    LOGD("I have been loaded...");
    sleep(15);
    return NULL;
}
We sleep for 15 seconds here, just to make sure our target lib has been loaded into process already.
You can probably do better checks for this purpose, but we will take this in our example.

Now i'm going to create a struct to put my patches there, this is optional but it has better formatting:
C++:
struct My_Patches {
     // let's assume we have patches for these functions for whatever game
     // like show in miniMap boolean function
     MemoryPatch canShowInMinimap;
     // etc...
}my_cool_Patches;
Now after setting up patches and our custom thread, we will start with code patching.
C++:
   my_cool_Patches.canShowInMinimap = MemoryPatch("libil2cpp.so", 0x6A6144,
                                          "\x01\x00\xA0\xE3\x1E\xFF\x2F\xE1", 8);

    LOGD("===== New Patch Entry =====");
    LOGD("Patch Address: %p", (void *)my_cool_Patches.canShowInMinimap.get_TargetAddress());
    LOGD("Patch Size: %zu", my_cool_Patches.canShowInMinimap.get_PatchSize());
    LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());

    // modify & print bytes
    if (my_cool_Patches.canShowInMinimap.Modify()) {
        LOGD("canShowInMinimap has been modified successfully");
        LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());
    }
    // restore & print bytes
    if (my_cool_Patches.canShowInMinimap.Restore()) {
        LOGD("canShowInMinimap has been restored successfully");
        LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());
    }

    LOGD("===========================");
In the code above, first we initialize canShowInMinimap patch that i have declared in my patches struct, we see the MemoryPatch constructor arguments here are first library name which is "il2cpp.so", then a relative address of the function that i want to patch, and then the patch bytes that which is mov r0, #1 & bx lr AKA return true for arm.
the bytes are in little endian, I personally use this online assembler & disassembler, and last argument is how many bytes which I'm writing 8 for this.
I think functions names are self explained, like "Modify" function which is going to apply patch bytes to the address, and "Restore" which is going to write old bytes (Before Patch) to the address.
ToHexString function is also very helpful for debugging as you can see, I keep printing address bytes to see the current state of the patch.

To put this together in the thread function:
C++:
void *my_test_thread(void *) {
    LOGD("I have been loaded...");

    sleep(15);

    my_cool_Patches.canShowInMinimap = MemoryPatch("libil2cpp.so", 0x6A6144,
                                          "\x01\x00\xA0\xE3\x1E\xFF\x2F\xE1", 8);

    LOGD("===== New Patch Entry =====");
    LOGD("Patch Address: %p", (void *)my_cool_Patches.canShowInMinimap.get_TargetAddress());
    LOGD("Patch Size: %zu", my_cool_Patches.canShowInMinimap.get_PatchSize());
    LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());

    // modify & print bytes
    if (my_cool_Patches.canShowInMinimap.Modify()) {
        LOGD("canShowInMinimap has been modified successfully");
        LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());
    }
    // restore & print bytes
    if (my_cool_Patches.canShowInMinimap.Restore()) {
        LOGD("canShowInMinimap has been restored successfully");
        LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());
    }

    LOGD("===========================");

    return NULL;
}
And we finally define our library constructor which will be invoked when our library is loaded:
C++:
__attribute__((constructor))void initializer() {
    pthread_t ptid;
    pthread_create(&ptid, NULL, my_test_thread, NULL);
}
You can learn more about constructor attribute Here.



I hope this wasn't complicated, I have tried to make it as clean and simple as possible.
> Library Source <

~Ruit
 
Last edited:

Sebby Seb

Member


I will explain in this tutorial how code patching & generally editing memory at runtime.

First let's talk about requirements and my environment that I'm using for this.

Environment:
- Make installed and added to the PATH environment variable.
- NDK r17c installed and added to the PATH environment variable.

Requirements:
C++ knowledge.

many modders can apply their patches by editing binary file with any hex editor, but I've been asked how to apply them at real time.
editing self process memory does not require root access or anything special, and can be done directly. for this tutorial I'm using memcpy to copy bytes from a buffer into an address and vice versa.
I'll be using this only for code patching, but for pointers, they can be directly dereferenced and assigned to any value as long as the object isn't a constant object.
You can check the functions readPointer & writePointer to see how it works with pointers. I won't explain how to work with pointers in this tutorial because It's really some basic stuff in c++ that you can learn on internet.

Let's now start with a simple code patch example because i don't really like to write long articles.
First we include the necessary headers to work with patches:
Code:
#include "KittyMemory/MemoryPatch.h"
I'm going to use a posix thread to run my patches, this would be smart because we don't want to slow or interrupt the execution of the main process thread.
So we are going to include that as well:
Code:
#include <pthread.h>
Now we define our thread function, the prototype of pthread functions is like:
Code:
void *functionName(void *args);
Code:
void *my_test_thread(void *) {
    LOGD("I have been loaded...");
    sleep(15);
    return NULL;
}
We sleep for 15 seconds here, just to make sure our target lib has been loaded into process already.
You can probably do better checks for this purpose, but we will take this in our example.

Now i'm going to create a struct to put my patches there, this is optional but it has better formatting:
Code:
struct My_Patches {
     // let's assume we have patches for these functions for whatever game
     // like show in miniMap boolean function
     MemoryPatch canShowInMinimap;
     // etc...
}my_cool_Patches;
Now after setting up patches and our custom thread, we will start with code patching.
Code:
   my_cool_Patches.canShowInMinimap = MemoryPatch("libil2cpp.so", 0x6A6144,
                                          "\x01\x00\xA0\xE3\x1E\xFF\x2F\xE1", 8);

    LOGD("===== New Patch Entry =====");
    LOGD("Patch Address: %p", (void *)my_cool_Patches.canShowInMinimap.get_TargetAddress());
    LOGD("Patch Size: %zu", my_cool_Patches.canShowInMinimap.get_PatchSize());
    LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());

    // modify & print bytes
    if (my_cool_Patches.canShowInMinimap.Modify()) {
        LOGD("canShowInMinimap has been modified successfully");
        LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());
    }
    // restore & print bytes
    if (my_cool_Patches.canShowInMinimap.Restore()) {
        LOGD("canShowInMinimap has been restored successfully");
        LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());
    }

    LOGD("===========================");
In the code above, first we initialize canShowInMinimap patch that i have declared in my patches struct, we see the MemoryPatch constructor arguments here are first library name which is "il2cpp.so", then a relative address of the function that i want to patch, and then the patch bytes that which is mov r0, #1 & bx lr AKA return true for arm.
the bytes are in little endian, I personally use this online assembler & disassembler, and last argument is how many bytes which I'm writing 8 for this.
I think functions names are self explained, like "Modify" function which is going to apply patch bytes to the address, and "Restore" which is going to write old bytes (Before Patch) to the address.
ToHexString function is also very helpful for debugging as you can see, I keep printing address bytes to see the current state of the patch.

To put this together in the thread function:
Code:
void *my_test_thread(void *) {
    LOGD("I have been loaded...");

    sleep(15);

    my_cool_Patches.canShowInMinimap = MemoryPatch("libil2cpp.so", 0x6A6144,
                                          "\x01\x00\xA0\xE3\x1E\xFF\x2F\xE1", 8);

    LOGD("===== New Patch Entry =====");
    LOGD("Patch Address: %p", (void *)my_cool_Patches.canShowInMinimap.get_TargetAddress());
    LOGD("Patch Size: %zu", my_cool_Patches.canShowInMinimap.get_PatchSize());
    LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());

    // modify & print bytes
    if (my_cool_Patches.canShowInMinimap.Modify()) {
        LOGD("canShowInMinimap has been modified successfully");
        LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());
    }
    // restore & print bytes
    if (my_cool_Patches.canShowInMinimap.Restore()) {
        LOGD("canShowInMinimap has been restored successfully");
        LOGD("Current Bytes: %s", my_cool_Patches.canShowInMinimap.ToHexString().c_str());
    }

    LOGD("===========================");

    return NULL;
}
And we finally define our library constructor which will be invoked when our library is loaded:
]
Code:
__attribute__((constructor))void initializer() {
    pthread_t ptid;
    pthread_create(&ptid, NULL, my_test_thread, NULL);
}
You can learn more about constructor attribute Here.



I hope this wasn't complicated, I have tried to make it as clean and simple as possible.
> Library Source <

~Ruit
As you know, I am still very ignorant. But even I understood most of that. Nice one
 

Ted2

New Member
@Ruit , Can't you add a check to see if MSHookMemory exists instead of requiring it to build it for both non-newsubstrate & newsubstrate?
There's MSFindSymbol.. I guess this should be able to do the check.

Anyways.. I'll try it when I've time & let you know if it works if you haven't taken it under consideration yet.
 

Ruit

Adminstrator
Adminstrator
@Ruit , Can't you add a check to see if MSHookMemory exists instead of requiring it to build it for both non-newsubstrate & newsubstrate?
There's MSFindSymbol.. I guess this should be able to do the check.

Anyways.. I'll try it when I've time & let you know if it works if you haven't taken it under consideration yet.
Yeah, pushed an update.
 
Top