ICallHook - Tutorial
Hello everyone I am Rednick16 (Red16) its been a while since I last made a tutorial so stay with me if this is a bit hard to follow along with.
Ann ale (Lets begin).
By now I am sure everyone participating in the unity modding scene have heard about IL2CPP (Intermediate Language To C++)
Okay so let's actually begin. I am going to try and keep this tutorial straight forward and easy as possible to follow along with.
In this tutorial you will be learning how to hook the following icalls. I should also mention that this does not need JIT (Just-In-Time).
UnityEngine.Camera::get_fieldOfView()
UnityEngine.Time::get_timeScale()
UnityEngine.Time::set_timeScale(System.Single)
UnityEngine.Input::GetKeyUpInt(UnityEngine.KeyCode)
UnityEngine.Input::GetKeyDownInt(UnityEngine.KeyCode)
The hooking method.
See the spoiler box for a thorough tutorial on how this was achieved and why it works.
NOTE: Timing is everything with this method. You must be very precise with where you setup the hook calls, for example the best place to put it would be in il2cpp_init ref: https://github.com/Rednick16/ICallHook/blob/a4cb7a17ac1abc9187080f0d7afea8ee6cd7cc46/Tweak.x#L67
#include <stdbool.h>
#include <dlfcn.h>
void* il2cpp_library_get_handle(void)
{
static void *handle = NULL;
#if defined(__ANDROID__)
if (NULL == handle)
handle = dlopen("libil2cpp.so", RTLD_NOLOAD | RTLD_NOW | RTLD_GLOBAL);
return handle;
#elif defined(__APPLE__)
return RTLD_DEFAULT;
#else
#error "Unsupported target"
#endif
}
bool il2cpp_hook_icall(const char* name, void* hook, void** old)
{
if (name == NULL || hook == NULL)
return false;
/* NOTE: resolve these ahead of time if possible, dlsym is really slow, but should be fine to use here
since we only use these at startup anyways */
void *(*il2cpp_resolve_icall)(const char *name) = NULL;
if (il2cpp_resolve_icall == NULL)
il2cpp_resolve_icall = (void * (*)(const char*))dlsym(il2cpp_library_get_handle(), "il2cpp_resolve_icall");
void (*il2cpp_add_internal_call)(const char* name, void* methodPointer) = NULL;
if (!il2cpp_add_internal_call)
il2cpp_add_internal_call = (void (*)(const char*, void*))dlsym(il2cpp_library_get_handle(), "il2cpp_add_internal_call");
if (il2cpp_resolve_icall == NULL || il2cpp_add_internal_call == NULL)
return false;
// The actual impl
void* resolved = il2cpp_resolve_icall(name);
if (!resolved) return false;
if (old)
*old = resolved;
il2cpp_add_internal_call(name, hook);
return true;
}
Lets have some fun and make some hooks
static float (*_get_fieldOfView)(void *) = NULL;
static float hooked_get_fieldOfView(void *_this)
{
float fov = _get_fieldOfView(_this);
// printf(@"--> get_fieldOfView(): %f\n", fov);
return fov;
}
static float (*_get_timeScale)() = NULL;
static float hooked_get_timeScale()
{
float scale = _get_timeScale();
// printf(@"--> get_timeScale(): %f\n", scale);
return scale;
}
static bool (*_GetKeyUpInt)(int keyCode) = NULL;
static bool hooked_GetKeyUpInt(int keyCode)
{
bool status = _GetKeyUpInt(keyCode);
// printf(@"--> GetKeyUpInt(): up: %s\n", status ? "YES" : "NO");
return status;
}
static bool (*_GetKeyDownInt)(int keyCode) = NULL;
static bool hooked_GetKeyDownInt(int keyCode)
{
bool status = _GetKeyDownInt(keyCode);
// printf(@"--> GetKeyDownInt(): down: %s\n", status ? "YES" : "NO");
return status;
}
void Il2CppDidInit(void)
{
// Called after il2cpp_init completes
Il2CppHookInternalCall("UnityEngine.Camera::get_fieldOfView()",
(void *)&hooked_get_fieldOfView,
(void **)&_get_fieldOfView);
Il2CppHookInternalCall("UnityEngine.Time::get_timeScale()",
(void *)&hooked_get_timeScale,
(void **)&_get_timeScale);
Il2CppHookInternalCall("UnityEngine.Input::GetKeyUpInt(UnityEngine.KeyCode)",
(void *)&hooked_GetKeyUpInt,
(void **)&_GetKeyUpInt);
Il2CppHookInternalCall("UnityEngine.Input::GetKeyDownInt(UnityEngine.KeyCode)",
(void *)&hooked_GetKeyDownInt,
(void **)&_GetKeyDownInt);
}
Thats it.
You guys were probably expecting more from me but this all I got haha
Test it out: https://github.com/Rednick16/ICallHook
Yep thats it there are many other techniques that can be used along side this such as methodPointer swaps on Update, LateUpdate, Start, Awake for classes deriving from MonoBehaviour, and VTables are mostly intact as well. goodby
Credits:
@Red16