Hello! I have decided to share some useful examples regarding the usage of Il2cppInspector C++ scaffold. In this guide, I will provide examples of how to interact with defined Il2cpp API functions.
You can get the latest version of Il2CppInspector 2023.1 here.
Having knowledge of C++ and C# is essential. You should also be familiar with the basics of the Unity game engine.
Note
I wrote my own helper class. You can access it under the lib folder. Throughout the guide, I will be using the helper functions I created.
If you have any questions, feel free to reach out to me.
Discord: Jadis0x
Steam: Jadis0x
Tip
You can find more detailed examples by taking a look at HERE using the Il2cppInspector analysis tool I used to create the cheat.
Note
I am updating the guide. I will try to add detailed explanations as much as I can.
- Get the assemblies
- Getting the type from a class (Il2CppObject* to Type*)
- Getting class names and types from a specific assembly
- Getting information about any method
- Calling a function with "il2cpp_runtime_invoke"
- Getting a List of All Functions in the Target Class
- Getting Information about Class Fields (FieldInfo)
- Modifiying the Value of a Field
This code snippet demonstrates how to retrieve a list of all assemblies within the active domain using il2cpp API functions.
// Get the active domain
const Il2CppDomain* domain = il2cpp_domain_get();
// Define variables to hold the assembly list
const Il2CppAssembly** assemblies;
size_t size;
// Use the il2cpp_domain_get_assemblies function to retrieve all assemblies
assemblies = il2cpp_domain_get_assemblies(domain, &size);
// Iterate through each assembly in the list
for (size_t i = 0; i < size; ++i) {
const Il2CppAssembly* assembly = assemblies[i];
if (assembly) {
// Get the assembly name using il2cpp_image_get_name function
const char* assemblyName = il2cpp_image_get_name(assembly->image);
// Print the name of the assembly to the console
std::cout << assemblyName << "\n";
}
}
-
Get the Active Domain:
- Function:
il2cpp_domain_get
- Description: Retrieves the active domain (
Il2CppDomain
) where the program is currently running.
- Function:
-
Retrieve Assemblies:
- Function:
il2cpp_domain_get_assemblies
- Description: Retrieves a list of all assemblies (
Il2CppAssembly
) within the specified domain (Il2CppDomain
). The function returns an array ofIl2CppAssembly
pointers (assemblies
) and updates thesize
variable with the number of assemblies found.
- Function:
-
Iterate Through Assemblies:
- Using a
for
loop, iterate through each assembly in theassemblies
array.
- Using a
-
Get Assembly Name:
- Function:
il2cpp_image_get_name
- Description: Retrieves the name of the assembly associated with the given image (
Il2CppImage
) within the assembly structure (Il2CppAssembly
). It returns aconst char*
containing the assembly name.
- Function:
-
Print Assembly Names:
- Print each assembly name retrieved from
il2cpp_image_get_name
to the console (std::cout
).
- Print each assembly name retrieved from
Output:
To obtain the type information here, we will use the GetTypeFromClass function inside my Il2cppHelper class.
Il2CppObject* Il2CppHelper::GetTypeFromClass(const Il2CppImage* _image, const char* _namespaze, const char* _name)
{
// Retrieve the class information using the image, namespace, and class name
Il2CppClass* _targetClass = il2cpp_class_from_name(_image, _namespaze, _name);
// If the class is found
if (_targetClass) {
// Get the type information from the class
const Il2CppType* _targetType = il2cpp_class_get_type(_targetClass);
// If the type information is found
if (_targetType) {
// Get the Il2CppObject from the type information
Il2CppObject* targetObject = il2cpp_type_get_object(_targetType);
// If the object is successfully retrieved, return it
if (targetObject) {
return targetObject;
}
}
}
// If any step fails, return nullptr
return nullptr;
}
First, load the CoreModule from Unity, which contains many fundamental classes such as GameObject.
const Il2CppImage* _CoreModule = _helper->GetImage("UnityEngine.CoreModule.dll");
If the module is successfully loaded, use the GetTypeFromClass function to get the type information for the GameObject class.
if (_CoreModule) {
// Get the type information for the GameObject class in the UnityEngine namespace
Il2CppObject* _object = _helper->GetTypeFromClass(_CoreModule, "UnityEngine", "GameObject");
// If the type information is successfully retrieved
if (_object) {
// Cast the Il2CppObject to Type
Type* gameobjectType = reinterpret_cast<Type*>(_object);
// If the type casting is successful
if (gameobjectType) {
// Find all objects of the GameObject type
Object_1__Array* getAllGameObjects = Object_1_FindObjectsOfType(gameobjectType, nullptr);
// Print the count of GameObject instances
std::cout << "Gameobject count: " << getAllGameObjects->max_length << "\n";
// If any GameObject instances are found
if (getAllGameObjects) {
// Iterate through each GameObject instance
for (int i = 0; i < getAllGameObjects->max_length; i++) {
Object_1* currentGameObject = getAllGameObjects->vector[i];
// If the GameObject is active in the hierarchy
if (GameObject_get_activeInHierarchy(reinterpret_cast<GameObject*>(currentGameObject), nullptr)) {
// Print the name of the active GameObject
std::cout << "GameObject Name: " << il2cppi_to_string(Object_1_GetName(currentGameObject, nullptr)) << "\n";
}
}
}
}
}
}
In this example, we are passing the type of the object to be found (GameObject) as a parameter to the Object_1_FindObjectsOfType function. This actually allows us to achieve the following:
GameObject[] allGameObjects = FindObjectsOfType<GameObject>();
This function is quite slow. Using this function every frame is not recommended.
- Loading the Image:
- Function:
GetImage
- Description: Loads the
CoreModule
image from Unity, which contains fundamental classes likeGameObject
.
- Function:
- Retrieving Class Information:
- Function:
il2cpp_class_from_name
- Description: Retrieves class information using the image, namespace, and class name.
- Function:
- Getting Type Information from Class:
- Function:
il2cpp_class_get_type
- Description: Obtains the type information from the class.
- Function:
- Getting Il2CppObject from Type:
- Function:
il2cpp_type_get_object
- Description: Converts type information into an
Il2CppObject
.
- Function:
- Casting Il2CppObject to Type:
- Operation:
reinterpret_cast<Type*>
- Description: Casts the retrieved
Il2CppObject
toType
.
- Operation:
- Finding Objects of a Specific Type:
- Function:
Object_1_FindObjectsOfType
- Description: Finds all objects of the specified type (
GameObject
in this case).
- Function:
- Checking if GameObject is Active:
- Function:
GameObject_get_activeInHierarchy
- Description: Checks if the
GameObject
is active in the hierarchy.
- Function:
- Getting GameObject Name:
- Function:
Object_1_GetName
- Description: Retrieves the name of the
GameObject
.
- Function:
Output:
This code snippet demonstrates how to retrieve and print the names and namespaces of classes within a specified assembly using il2cpp API functions.
void Il2CppHelper::GetClassesAndNamesFromAssembly(const Il2CppImage* _image)
{
if (_image) {
size_t classCount = il2cpp_image_get_class_count(_image);
std::cout << "{\n";
for (size_t i = 0; i < classCount; ++i) {
const Il2CppClass* _klass = il2cpp_image_get_class(_image, i);
if (_klass) {
char* _name = const_cast<char*>(il2cpp_class_get_name(const_cast<Il2CppClass*>(_klass)));
char* _namespace = const_cast<char*>(il2cpp_class_get_namespace(const_cast<Il2CppClass*>(_klass)));
std::cout << " [\n";
std::cout << "\tName: " << _name << "\n";
std::cout << "\tNamespace: " << _namespace << "\n";
std::cout << " ],\n";
}
}
std::cout << "\n}\n";
}
}
const Il2CppImage* _BoltDll = _helper->GetImage("bolt.dll");
if (_BoltDll) {
_helper->GetClassesAndNamesFromAssembly(_BoltDll);
}
or
const Il2CppImage* _assemblyCSHARP = _helper->GetImage("Assembly-CSharp.dll");
if (_assemblyCSHARP) {
_helper->GetClassesAndNamesFromAssembly(_assemblyCSHARP);
}
Outputs:
- It allows you to print the name, return type, and parameter information of the target method.
void Il2CppHelper::GetMethodInfo(const Il2CppImage* _image, const char* _funcName, int argLength, const char* _class_name, const char* _class_namespace)
{
Il2CppClass* _class = il2cpp_class_from_name(_image, _class_namespace, _class_name);
if (_class == nullptr) return;
const MethodInfo* methodInfo = il2cpp_class_get_method_from_name(_class, _funcName, argLength);
if (methodInfo == nullptr) return;
Il2CppReflectionMethod* reflectionMethod = il2cpp_method_get_object(methodInfo, _class);
// Check if the reflectionMethod is not null
if (reflectionMethod == nullptr) return;
std::cout << "{\n";
// Get the method's name from the reflectionMethod object
const char* methodName = il2cpp_method_get_name(methodInfo);
std::cout << "\tMethod Name: " << methodName << std::endl;
const Il2CppType* returnType = il2cpp_method_get_return_type(methodInfo);
std::cout << "\tReturn Type: " << il2cpp_type_get_name(returnType) << std::endl;
// Get the parameter count of the method using il2cpp_method_get_param_count
int parameterCount = il2cpp_method_get_param_count(methodInfo);
std::cout << "\tParameter Count: " << parameterCount << std::endl;
std::cout << "\t[\n";
// Get the parameter types of the method
for (int i = 0; i < parameterCount; i++) {
// Get the parameter type at index i using il2cpp_method_get_param
const Il2CppType* parameterType = il2cpp_method_get_param(methodInfo, i);
// Get the type name of the parameter type using il2cpp_type_get_name
const char* parameterTypeName = il2cpp_type_get_name(parameterType);
// Print the parameter type name to the console
std::cout << "\t\tParameter " << i << " Type: " << parameterTypeName << std::endl;
}
std::cout << "\t]\n";
std::cout << "}\n";
}
const Il2CppImage* _AssemblyCSharp = _helper->GetImage("Assembly-CSharp.dll");
_helper->GetMethodInfo(_AssemblyCSharp, "SetFOV", 1, "NolanBehaviour", "");
Output:
if (GetAsyncKeyState(VK_F1) & 0x8000) {
const Il2CppImage* _AssemblyCSharp = _helper->GetImage("Assembly-CSharp.dll");
_helper->GetMethodInfo(_AssemblyCSharp, "SetRank", 1, "NolanRankController", "");
}
if (GetAsyncKeyState(VK_F2) & 0x8000) {
const Il2CppImage* _csharp = _helper->GetImage("Assembly-CSharp.dll");
if (_csharp == nullptr) return;
Il2CppObject* nolanObj = _helper->GetTypeFromClass(_csharp, "", "NolanRankController");
Type* TNolan = reinterpret_cast<Type*>(nolanObj);
auto isTypeValid = Object_1_FindObjectOfType(TNolan, nullptr);
if(isTypeValid){
NolanBehaviour* _nb_ = reinterpret_cast<NolanBehaviour*>(isTypeValid);
if (_nb_) {
Il2CppClass* _nbClass = il2cpp_class_from_name(_csharp, "", "NolanRankController");
if (_nbClass == nullptr) return;
const MethodInfo* methodInfo = il2cpp_class_get_method_from_name(_nbClass, "SetRank", 1);
int newRankvalue = 666;
void* params[] = { &newRankvalue };
std::cout << "call function..\n";
il2cpp_runtime_invoke(methodInfo, _nb_, params, nullptr);
}
}
}
Output:
- This function, serves the purpose of obtaining and displaying a list of all methods within a given class (specified by the klass parameter). It iterates through each method in the class using a loop, retrieving information such as the method name and its return type. Subsequently, it prints out the method name along with its return type, providing a clear representation of the methods contained within the class
void Il2CppHelper::PrintMethods(Il2CppClass* klass) {
const MethodInfo* methodIter = nullptr;
void* iter = nullptr;
// Retrieve all methods of the class
while ((methodIter = il2cpp_class_get_methods(klass, &iter)) != nullptr) {
// Get the name of the method
const char* methodName = il2cpp_method_get_name(methodIter);
// Get the return type of the method
const Il2CppType* methodReturnType = il2cpp_method_get_return_type(methodIter);
char* returnTypeName = il2cpp_type_get_name(methodReturnType);
// Print the method name and its return type
std::cout << "Method Name: " << methodName;
std::cout << " (" << returnTypeName << ")\n------------------------------------\n";
// Perform necessary memory operations
il2cpp_free(returnTypeName);
}
}
const Il2CppImage* _image = _helper->GetImage("Assembly-CSharp.dll");
if (_image) {
Il2CppClass* nolanRankControllerClass = il2cpp_class_from_name(_timage, "", "NolanRankController");
if (nolanRankControllerClass != nullptr) {
_helper->PrintMethods(nolanRankControllerClass);
}
}
Output:
- It allows us to get information about the fields of a class
void Il2CppHelper::GetFieldsInformation(Il2CppClass* klass)
{
void* iter = nullptr;
FieldInfo* field = nullptr;
// Iterate through the fields of the class
while ((field = il2cpp_class_get_fields(klass, &iter)) != nullptr)
{
// Get the name of the field
const char* fieldName = il2cpp_field_get_name(field);
// Get the type of the field
const Il2CppType* fieldType = il2cpp_field_get_type(field);
char* fieldTypeStr = il2cpp_type_get_name(fieldType);
// Print the information about the field
std::cout << "Field Name: " << fieldName << std::endl;
std::cout << "Type: " << fieldTypeStr << std::endl;
std::cout << "-----------\n";
}
}
const Il2CppImage* _assemblyCSHARP = _helper->GetImage("Assembly-CSharp.dll");
if (_assemblyCSHARP) {
Il2CppClass* _nolanBehaviourClass = il2cpp_class_from_name(_assemblyCSHARP, "", "NolanBehaviour");
_helper->GetFieldsInformation(_nolanBehaviourClass);
}
Output:
if (GetAsyncKeyState(VK_F1) & 0x8000) {
// Get the Il2CppImage for "Assembly-CSharp.dll"
const Il2CppImage* _AssemblyCSharp = _helper->GetImage("Assembly-CSharp.dll");
// Get the object for the "Menu" class within the "Horror" namespace
Il2CppObject* _horrorMenuClassObject = _helper->GetTypeFromClass(_AssemblyCSharp, "Horror", "Menu");
// Check if the object exists
if (_horrorMenuClassObject) {
// Find the object represented by _horrorMenuClassObject in the app
auto menuType = app::Object_1_FindObjectOfType_1(reinterpret_cast<Type*>(_horrorMenuClassObject), true, nullptr);
// Check if the object was found
if (menuType) {
// Get the Il2CppClass for the "Menu" class
Il2CppClass* menuClass = il2cpp_class_from_name(_AssemblyCSharp, "Horror", "Menu");
if (menuClass == nullptr) return;
// Get the FieldInfo for the "steamName" field
FieldInfo* steamNameField = il2cpp_class_get_field_from_name(menuClass, "steamName");
// Check if the field exists
if (steamNameField) {
std::cout << "field is exists!!\n";
// Define a new value for the field
const char* newSteamNameValue = "il2cpp-field";
// Create a new Il2CppString from the new value
Il2CppString* newSteamNameString = il2cpp_string_new(newSteamNameValue);
// Set the field's value to the new value
il2cpp_field_set_value(_horrorMenuClassObject, steamNameField, newSteamNameString);
}
else {
std::cout << "field is not exists!\n";
}
}
}
}
I will continue to contribute as much as I can. For now, bye!