Tutorial How to unlink functions in IL2CPP and other native games

HizroMxDz

1/3 Games Approved
Original poster
Dec 25, 2019
92
96
53
x____x
~Read this tutorial first: Basic Hooking Tutorial
~Use this template to do this: LGLTeam/Android-Mod-Menu
~You need some knowledge of C++ to understand this (You can learn C++ on sites such as Geeks For Geeks, TutorialsPoint, Youtube etc. or from apps in the Play Store)

Before I start, what do I mean by "unlink"? Well, have you ever made a hack and the hack was also shared with the enemy (i.e. you gave yourself unlimited ammo or health, but the enemy also gets it) or the other way around (i.e. you made a hack to kill all enemies to auto-win but you also get killed). You will learn how to unlink them with various examples.
(While reading the tutorial, make sure to read the comments I put in the code as well)



1. Using bool functions

I dumped a game and saw this function to get unlimited health but it was shared:
C#:
private int healthPoints(); // 0x436BC7
which means if I hooked it and tried to give myself unlimited health like this:
C++:
int (*old_healthPoints)(void *instance);
int healthPoints(void *instance)
{
    if(instance != NULL) // Check if instance isn't NULL to prevent crashes
    {
        return 999999; // If instance isn't NULL, then make my health 999999
    }
    return old_healthPoints(instance); // If instance is NULL, return original value
}
MsHookFunction((void*)getAbsoluteAddress(0x436BC7), (void*)healthPoints, (void**)&old_healthPoints); // Make sure to call your hooks with MSHook
IT WOULD NOT WORK AND ENEMIES WILL GET UNLIMITED HEALTH TOO SINCE IT'S SHARED!
So how do we unlink it? It's not the same for every game, but try to look for a function inside the same class which you can use to unlink. In my case, I found this function which I can use to unlink:
C#:
public bool get_isMine(); // 0x453C91
How would we do it? Well, we declare a function pointer to it and this is how you declare function pointers:
C++:
FunctionType(*FunctionName)(void *instance, AnyAdditionalParameters);
FunctionName = (FunctionType (*)(void*, TypesOfAdditionalParameters))getAbsoluteAddress(targetLibName, 0xOFFSET);
IMPORTANT: We split the function pointer like that because when your lib is loaded, sometimes the libil2cpp.so isn't loaded but your lib tries to read the offset anyways which results in a crash, splitting the function pointer like I did will fix this.
Here's the function pointer for get_isMine:
C++:
bool (*get_isMine)(void *instance); // get_isMine doesn't have any additional parameters

int (*old_healthPoints)(void *instance);
int healthPoints(void *instance)
{
    if(instance != NULL) // Check if instance isn't NULL to prevent crashes
    {
        return 999999; // If instance isn't NULL, then make my health 999999
    }
    return old_healthPoints(instance); // If instance is NULL, return original value
}
MsHookFunction((void*)getAbsoluteAddress(0x436BC7), (void*)healthPoints, (void**)&old_healthPoints); // Make sure to call your hooks with MSHook

get_isMine = (bool (*)(void*))getAbsoluteAddress(targetLibName, 0x453C91); // Again, get_isMine doesn't take any additional parameters
Now that we have a function pointer to get_isMine, we can make a variable to it and use it to unlink our hack like this:
C++:
bool (*get_isMine)(void *instance); // get_isMine doesn't have any additional parameters

int (*old_healthPoints)(void *instance);
int healthPoints(void *instance)
{
    if(instance != NULL) // Check if instance isn't NULL to prevent crashes
    {
        bool isMine = get_isMine(instance); // Declare a variable to our function pointer
        if(isMine) // Check if it's you
        {
        return 999999; // And if it's you, then make my health 999999 (Now only you get unlimited health)
        }
    }
    return old_healthPoints(instance); // If instance is NULL OR IF IT'S NOT YOU, return original value
}
MsHookFunction((void*)getAbsoluteAddress(0x436BC7), (void*)healthPoints, (void**)&old_healthPoints); // Make sure to call your hooks with MSHook

get_isMine = (bool (*)(void*))getAbsoluteAddress(targetLibName, 0x453C91); // Again, get_isMine doesn't take any additional parameters


2. Using int functions

So i dumped a game and in the dump.cs I saw this function in the class CharacterController which I used to get auto-win, but it was shared:
C#:
private bool isDead(); // 0x832BC29
Which means If I hooked it and tried to make an auto-win hack like this:
C++:
bool (*old_isDead)(void *instance);
bool isDead(void *instance)
{
    if(instance != NULL) // Check if instance isn't NULL to prevent crashes
    {
        return true; // If instance isn't NULL, return true and kill everyone
    }
    return old_isDead(instance); // Return original value if instance is NULL
}
MsHookFunction((void*)getAbsoluteAddress(0x832BC29), (void*)isDead, (void**)&old_isDead); // Make sure to call your hooks with MSHook
IT WOULD NOT WORK AND I WOULD DIE TOO SINCE IT'S SHARED!!
So how do we unlink it? Well, just like in the first example, we look for a function in the CharacterController class which we can use to unlink our hack. BUT what if you don't have a bool function in your class to do it? What you need to do is look for other functions in your class which you can use to unlink if you don't have a bool function to do it. In my case, I found these two functions which aren't bools but can help me unlink my hack:
C#:
private int ownerIDNumber(); // 0x832C39
ownerIDNumber holds your ID number
C#:
private int playerIDNumber(); // 0x3279BC
playerIDNumber holds the ID number of every player
So how would I use these two to unlink my hack and make it kill all enemies only? Well we create function pointers to both of them like this:
C++:
int (*ownerIDNumber)(void *instance); // Function pointer for int ownerIDNumber (doesn't take any additional parameters)
int (*playerIDNumber)(void *instance); // Function pointer for int playerIDNumber (doesn't take any additional parameters)

bool (*old_isDead)(void *instance);
bool isDead(void *instance)
{
    if(instance != NULL) // Check if instance isn't NULL to prevent crashes
    {
        return true; // If instance isn't NULL, return true and kill everyone
    }
    return old_isDead(instance); // Return original value if instance is NULL
}
MsHookFunction((void*)getAbsoluteAddress(0x832BC29), (void*)isDead, (void**)&old_isDead); // Make sure to call your hooks with MSHook

ownerIDNumber = (int (*)(void*))getAbsoluteAddress(targetLibName, 0x832C39); // Offset of ownerIDNumber
playerIDNumber = (int (*)(void*))getAbsoluteAddress(targetLibName, 0x3279BC); // Offset of playerIDNumber
Now that we have function pointers for both, we can declares variables to them and unlink our hack like this:
C++:
int (*ownerIDNumber)(void *instance); // Function pointer for int ownerIDNumber (doesn't take any additional parameters)
int (*playerIDNumber)(void *instance); // Function pointer for int playerIDNumber (doesn't take any additional parameters)

bool (*old_isDead)(void *instance);
bool isDead(void *instance)
{
    if(instance != NULL) // Check if instance isn't NULL to prevent crashes
    {
    int OwnerID = ownerIDNumber(instance); // Variables for both of our function pointers
    int PlayerID = playerIDNumber(instance);
    if(OwnerID != PlayerID) // Check If the player's ID does not equal to your ID
    {
    return true; // And if it doesn't equal to your ID, then kill them
    }
    }
    return old_isDead(instance); // Return original value if instance is NULL OR IF PLAYER ID DOES EQUAL TO YOUR ID
}
MsHookFunction((void*)getAbsoluteAddress(0x832BC29), (void*)isDead, (void**)&old_isDead); // Make sure to call your hooks with MSHook

ownerIDNumber = (int (*)(void*))getAbsoluteAddress(targetLibName, 0x832C39); // Offset of ownerIDNumber
playerIDNumber = (int (*)(void*))getAbsoluteAddress(targetLibName, 0x3279BC); // Offset of playerIDNumber


3. Using bool fields

Unlinking with fields is similar to unlinking with functions, but you don't use a function pointer.
I dumped a game and in the class Player, I saw this function and wanted to use it to kill all enemies:
C#:
private void killPlayer(); // 0x2485B9
BUT IT'S SHARED WHICH MEANS I WOULD DIE TOO. So how do I unlink it using a field? Well, just like in the examples above, you look for fields in the class which you can use to unlink instead of a function in the class. In my case, I found this field in the Player class which could help me unlink it:
C#:
public bool isEnemy; // 0x8C
In the Player class, I am gonna hook the LateUpdate function instead of killPlayer, and instead use a function pointer for killPlayer. Why do I hook the LateUpdate function? The LateUpdate and Update functions are called as many times in a second as your FPS is. So if your FPS is 60 FPS, then Update is called 60 times every second. Why is this useful? Think about it. We wouldn't want to get and set fields on an object that hasn't been updated for a while right? We need our most current object to modify, and what better way of getting it than hooking a function that is called 60 times every second?
Here's my LateUpdate function in the class Player:
C#:
private void LateUpdate(); // 0x32720AB
Here's the hook for the LateUpdate function:
C++:
void(*old_LateUpdate)(void *instance);
void LateUpdate(void *instance)
{
    if(instance != NULL)
    {

    }
     old_LateUpdate(instance); // We don't use return because we aren't returning anything to the LateUpdate function, we are using function pointer to call killPlayer
}
MsHookFunction((void*)getAbsoluteAddress(0x32720AB), (void*)LateUpdate, (void**)&old_LateUpdate);
So how do we use the field isEnemy to unlink our hack? You declare a variable to it like this:
C++:
void(*old_LateUpdate)(void *instance);
void LateUpdate(void *instance)
{
    if(instance != NULL)
    {
        bool IsEnemy = *(bool*)((uint64_t)instance + 0x8C) // Declare a variable to isEnemy to access it
    }
     old_LateUpdate(instance);
}
MsHookFunction((void*)getAbsoluteAddress(0x32720AB), (void*)LateUpdate, (void**)&old_LateUpdate);
Now that we have a variable to access the field isEnemy, we can use it to unlink our hack and use a function pointer for killPlayer to do it:
C++:
void (*killPlayer)(void *instance); // Doesn't take any additional parameters

void(*old_LateUpdate)(void *instance);
void LateUpdate(void *instance)
{
    if(instance != NULL)
    {
        bool IsEnemy = *(bool*)((uint64_t)instance + 0x8C) // Declare a variable to isEnemy to access it
        if(IsEnemy) // Check if it's enemies
        {
        killPlayer(instance); // And if they are then kill them
        }
    }
     old_LateUpdate(instance); If they aren't enemies then return original value
}
MsHookFunction((void*)getAbsoluteAddress(0x32720AB), (void*)LateUpdate, (void**)&old_LateUpdate);

killPlayer = (void (*)(void *))getAbsoluteAddress(targetLibName, 0x2485B9);


4. Using int fields

I dumped a game and in the dump.cs inside of the class Weapon, I saw this function to get one-hit kill but it was shared:
C#:
private int Damage; // 0x18
What if you want to use a field that isn't a bool to unlink your hack? Well, as always, try to look for a field which could help you unlink your hack. In my case, I saw these fields which could help me unlink my hack:
C#:
private int teamNumber; // 0x50
this field contains the number of all teams. (Not how many teams there are but their assigned number, similar to how other games use an ID)
C#:
private int enemyTeamNumber; // 0x54
this one contains the number of your enemies team only.
This class has an Update function and I am gonna hook it:
C#:
private void Update(); // 0x7273A2
C++:
void (*old_Update)(void *instance);
void Update(void *instance)
{
    if(instance != NULL)
    {

    }
     old_Update(instance);
}
MsHookFunction((void*)getAbsoluteAddress(0x7273A2), (void*)Update, (void**)&old_Update);
Now I will declare variables to both of my fields:
C++:
void (*old_Update)(void *instance);
void Update(void *instance)
{
    if(instance != NULL)
    {
        int TeamNumber = *(int*)((uint64_t)instance + 0x50);
        int EnemyTeam = *(int*)((uint64_t)instance + 0x54);
    }
     old_Update(instance);
}
MsHookFunction((void*)getAbsoluteAddress(0x7273A2), (void*)Update, (void**)&old_Update);
Now that we have access to both of our fields, we can use the variables to unlink our hack and get one-hit kill like this:
C++:
void (*old_Update)(void *instance);
void Update(void *instance)
{
    if(instance != NULL)
    {
        int TeamNumber = *(int*)((uint64_t)instance + 0x50); // Variables to our fields which we use to unlink our hack
        int EnemyTeam = *(int*)((uint64_t)instance + 0x54);
        if(TeamNumber != EnemyTeam) // Check if it's your team
        {
        *(int*)((uint64_t)instance + 0x18) = 999; // private int Damage; // 0x18 now only your team has one-hit kill
        }
    }
    old_Update(instance); // If it's enemy team, then return original value
}
MsHookFunction((void*)getAbsoluteAddress(0x7273A2), (void*)Update, (void**)&old_Update);


5. Using fields from a different class

I dumped a game and saw this function inside the class CharacterAssets which was shared with the enemy:
C#:
public int get_ammoInClip(); // 0x34287
I wanted to unlink it using a field but I could not find any field in the class CharacterAssets that would've helped me unlink it. But inside the class CharacterController I saw this field and I wanted to use it:
C#:
public bool isBot; // 0x24
How would I use it if it's in a different class? Well in your class you try to find an instance to the other class. In my case, I found this field inside of the CharacterAssets class which I can use to get access to the CharacterController class:
C#:
public CharacterController characterController; // 0x32
I can use this instance to get to the field isBot inside of the CharacterController class like this:
C++:
int(*old_get_ammoInClip)(void *instance);
int get_ammoInClip(void *instance)
{
    if(instance != NULL)
    {
    void *AccessCharacterController = *(void**)((uint64_t)instance + 0x32); // Use the field and make a pointer to get access to CharacterController
    if(AccessCharacterController != NULL) // Since we use a pointer for the field 0x32 to get access to the CharacterController class, we have to check if it isn't null
    {
    bool IsBot = *(bool*)((uint64_t)AccessCharacterController + 0x24); // use it to access the field isBot in the class CharacterController
    }
    }
    return old_get_ammoInClip(instance);
}
MsHookFunction((void*)getAbsoluteAddress(0x34287), (void*)get_ammoInClip, (void**)&old_get_ammoInClip);
now that we have access to the class CharacterController and have a variable for the isBot field in that class, we can finish our hack like this:
C++:
int(*old_get_ammoInClip)(void *instance);
int get_ammoInClip(void *instance)
{
    if(instance != NULL)
    {
    void *AccessCharacterController = *(void**)((uint64_t)instance + 0x32); // Use the field and make a pointer to get access to CharacterController
    if(AccessCharacterController != NULL) // Since we use a pointer for the field 0x32 to get access to the CharacterController class, we have to check if it isn't null
    {
    bool IsBot = *(bool*)((uint64_t)AccessCharacterController + 0x24); // use it to access the field isBot in the class CharacterController
    if(!IsBot) // Check if it's not a bot
    {
     return 99999; // Now only you get 99999 ammo
    }
    }
    }
    return old_get_ammoInClip(instance); // Return original value for bots
}
MsHookFunction((void*)getAbsoluteAddress(0x34287), (void*)get_ammoInClip, (void**)&old_get_ammoInClip);


Edit: I forgot to show how to connect them to a toggle, but here's how:
(I am gonna do this on the 2nd example)


Make a bool for your hack in the bools section and set it to false (After the Memory Patches struct):
C++:
bool autowin = false;
Make a toggle for it in the features section and I am gonna assign my own number to it:
C++:
OBFUSCATE("24_Toggle_Auto Win"),
Now in your hook just check for the hack using the bool you made:
C++:
int (*ownerIDNumber)(void *instance);
int (*playerIDNumber)(void *instance);

bool (*old_isDead)(void *instance);
bool isDead(void *instance)
{
    if(instance != NULL && autowin) // Only turn on the feature if the toggle is turned on
    {
    int OwnerID = ownerIDNumber(instance);
    int PlayerID = playerIDNumber(instance);
    if(OwnerID != PlayerID)
    {
    return true;
    }
    }
    return old_isDead(instance);
}
MsHookFunction((void*)getAbsoluteAddress(0x832BC29), (void*)isDead, (void**)&old_isDead);

ownerIDNumber = (int (*)(void*))getAbsoluteAddress(targetLibName, 0x832C39);
playerIDNumber = (int (*)(void*))getAbsoluteAddress(targetLibName, 0x3279BC);
Lastly, add this in the cases section using the bool you made:
C++:
case 24: // The case number is based on the your feature's number, in my case, it was a toggle and I assigned 24 to it
autowin = !autowin;
break;
Now your hack will only be on if you turn on the toggle!

That's it!


Credits:
~@SecretMyAss (His examples helped me a lot)
~Me (Used his examples and made ones suitable for Android)
~Check out Andnixsh's source codes
 
Last edited:

Numark

Awesome Active Platinian
May 23, 2017
116
929
193
That's nice, you can also do "if else" using isMine to return the enemys value to 0 or 1 for health as an example. I don't remember much because I've been busy doing other things, but yeah
 

Wumble

Rookie
May 31, 2022
3
0
1
25
N/A
I'm still learning, can I enter more than one value in this case

Public void ResetCooldown(int skill_id) { }

C++:
void (*ResetCooldown)(void *instance, int skill_id);
void (*_Update)(void *instance);
void Update(void *instance) {
    if(instance != NULL) {
      ResetCooldown(instance, skill_id);
    }
     _Update(instance);
}
 

CHEATS GAMES

Solid & Active Platinian
Aug 9, 2019
64
18
8
39
Brazil
what would be the game of this example?

C++:
int(*old_get_ammoInClip)(void *instance);
int get_ammoInClip(void *instance)
{
    if(instance != NULL)
    {
    void *AccessCharacterController = *(void**)((uint64_t)instance + 0x32); // Use the field and make a pointer to get access to CharacterController
    if(AccessCharacterController != NULL) // Since we use a pointer for the field 0x32 to get access to the CharacterController class, we have to check if it isn't null
    {
    bool IsBot = *(bool*)((uint64_t)AccessCharacterController + 0x24); // use it to access the field isBot in the class CharacterController
    }
    }
    return old_get_ammoInClip(instance);
}
MsHookFunction((void*)getAbsoluteAddress(0x34287), (void*)get_ammoInClip, (void**)&old_get_ammoInClip);
 

dani olmo

Platinian
Apr 26, 2022
19
2
1
24
in your home :0
what would be the game of this example?

C++:
int(*old_get_ammoInClip)(void *instance);
int get_ammoInClip(void *instance)
{
    if(instance != NULL)
    {
    void *AccessCharacterController = *(void**)((uint64_t)instance + 0x32); // Use the field and make a pointer to get access to CharacterController
    if(AccessCharacterController != NULL) // Since we use a pointer for the field 0x32 to get access to the CharacterController class, we have to check if it isn't null
    {
    bool IsBot = *(bool*)((uint64_t)AccessCharacterController + 0x24); // use it to access the field isBot in the class CharacterController
    }
    }
    return old_get_ammoInClip(instance);
}
MsHookFunction((void*)getAbsoluteAddress(0x34287), (void*)get_ammoInClip, (void**)&old_get_ammoInClip);
u can give the bot unlimited ammo