Archive

Archive for the ‘Reverse Engineering’ Category

Il2CppInspector Tutorial: How to create, use and debug IL2CPP DLL injection projects

November 27, 2020 1 comment

Il2CppInspector allows you to automatically create a Visual Studio solution containing a C++ DLL project targeted at the application you are reverse-engineering, which – when compiled – can be injected into the running application process to monitor or modify the application’s behaviour.

In the bad old days, you used to have to find every type, function, field etc. yourself. An excellent tutorial on this from an unknown author can be found here. Il2CppInspector’s C++ scaffolding projects render the need to perform this work obsolete. These projects give you complete access to all of the C#-equivalent types and methods in the IL2CPP application, plus all available IL2CPP APIs and exports. There is no need to determine any function pointers or add any type declarations.

In this article, we’ll briefly walk through how to create, edit, inject and debug these projects. Details on how to actually write useful code using the framework provided will be covered in later articles.

Creating the project

First, let’s use Il2CppInspector to create a C++ scaffolding project for the application you are targeting. I’ll be using The Long Dark for this example but any application will work. Some applications will detect this kind of invasive action, so naturally, you inject DLLs and attach a debugger to any 3rd party application at your own risk.

First, create the project from the Il2CppInspector GUI (or CLI) like so:

Once you’ve done this, you should have a solution folder that looks as follows:

Double-click on the .sln file to open it. You may be asked to retarget the project, and you should accept this with the following (default) settings:

Note: It is strongly recommended to use Visual Studio 2019 and the MSVC++ Build Tools v142 with Windows 10 SDK when working with Il2CppInspector C++ scaffolding projects.

Compile the initial project to make sure that it succeeds. First-time compilation may take a few minutes as the pre-compiled headers are created. Subsequent re-compilations will be much faster.

Editing the code

Generally you should only edit the files in the user folder, and place any additional source files here. If the target app is patched or updated and you need to re-create the project, Il2CppInspector will overwrite the contents of appdata and framework but leave the user folder intact. In this way, you can re-create the project in-place without losing your work as long as you have only modified the user folder.

Tip: You can find out more about the files generated for the C++ scaffolding project in the related Il2CppInspector documentation section.

The entry point is in user/main.cpp and looks something like this:

// Generated C++ file by Il2CppInspector - http://www.djkaty.com - https://github.com/djkaty
// Custom injected code entry point

#include "pch-il2cpp.h"

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <iostream>
#include "il2cpp-appdata.h"
#include "helpers.h"

using namespace app;

// Set the name of your log file here
extern const LPCWSTR LOG_FILE = L"il2cpp-log.txt";

// Custom injected code entry point
void Run()
{
    // If you would like to write to a log file, specify the name above and use il2cppi_log_write()
    // il2cppi_log_write("Startup");

    // If you would like to output to a new console window, use il2cppi_new_console() to open one and redirect stdout
    // il2cppi_new_console();

    // Place your custom code here
}

Your code will begin to execute from the Run() function as soon as the DLL is injected.

Let’s make a trivial example which simply creates a Vector3 and outputs its co-ordinates. This example can be injected into any Unity 3D IL2CPP application since Vector3 is always included. Here is the code:

void Run()
{
    // Vector3 example

    // (Create a console window using Il2CppInspector helper API)
    il2cppi_new_console();

    // (Call an IL2CPP API function)
    Vector3__Boxed* myVector3 = (Vector3__Boxed*) il2cpp_object_new((Il2CppClass*) *Vector3__TypeInfo);

    // (Call an instance constructor)
    Vector3__ctor(myVector3, 1.0f, 2.0f, 3.0f, nullptr);

    // (Access an instance field)
    std::cout <<   "x: " << std::to_string(myVector3->fields.x)
              << ", y: " << std::to_string(myVector3->fields.y)
              << ", z: " << std::to_string(myVector3->fields.z)
              << std::endl;
}

To create an object with IL2CPP, you must first allocate memory for it with il2cpp_object_new, which returns a pointer to the newly created object. You then pass this as the first argument to the object constructor (Vector3__ctor here). The final value of a C# method in IL2CPP is always a MethodInfo*, which is not needed here so we just use nullptr.

Notice how all of the instance fields of our Vector3 are automatically available via myVector3->fields. These fields mirror the equivalent C# object fields.

Compile the code, making sure to select the Debug configuration first. This will produce a file called in the project folder called x64\Debug\IL2CppDLL.dll. This is the file we will inject into the IL2CPP process.

Injecting the DLL

You can use any DLL injection software to do this, however my preference is to use Cheat Engine due to its ease of use and rich feature set.

First, start the IL2CPP application and Cheat Engine (you can do this in any order).

In Cheat Engine, attach to the IL2CPP process by clicking the top-left toolbar icon and selecting the process as shown below:

Now click the Memory View button to pop up the disassembler, and select Tools -> Inject DLL from the menu or press Ctrl+I. Select the DLL compiled above and choose No when asked if you wish to execute a specific function in the DLL.

If all has gone as planned, Cheat Engine will report that the DLL was injected, and a new console window should appear with the contents of our Vector3 object:

x: 1.000000, y: 2.000000, z: 3.000000

Nice! Now we have successfully injected a DLL, and created and accessed a C# object! You can now start to actually work on your real code.

Tip: When you modify your project, you will typically have to close and re-open the target application before re-injecting the newly compiled DLL. You can however leave Cheat Engine open between runs.

Debugging the code

Once you’ve fleshed out your code, debugging the DLL can be a tricky business: typically if there is a bug, the app will just freeze or crash and you won’t have any idea what’s wrong. We can make the work of debugging less painful by attaching the Visual Studio debugger to the injected process, allowing us to set breakpoints in our DLL and step through it line by line in the normal fashion. To enable this, we’ll edit the project properties in Visual Studio.

Right-click on the project in Solution Explorer and choose Properties, then select the Debugging page in the left-hand pane.

We’ll change two options: first, change the Command to the actual path of the IL2CPP application. Secondly, change Attach from No to Yes. Click OK to save the changes.

Set an initial breakpoint in your code by clicking to the left of the code window to create a red circle marking the breakpoint:

Now, start the target application as normal. Once it is running, press F5 (or your defined keyboard shortcut) in Visual Studio to spin up the debugger. This may take a few moments, then you should see a window like this:

The debugger is now attached to the target application.

Finally, use Cheat Engine to inject the DLL as before, and this time execution should immediately stop at the previously set breakpoint. You can now hover over variables to see their contents, step through the code line by line with F10, see the call stack with Alt+7, set watchpoints, resume execution with F5 and perform all of the other tasks normally associated with using the debugger. For example, hovering over myVector3 in our example above shows us the value of every field:

…and that’s it!

Conclusion

The days of hunting through binaries for function pointers and object instances are over – as are the days of needing to update them with every released patch of your target application. Every class and method is now available automatically, requiring only that you regenerate the scaffolding project when the target application is updated. Hopefully this will help speed up the development of new projects, reducing the need to figure out the binary layout and allowing you to focus on writing useful code.

At the time of writing, the C++ scaffolding project generator is still in its infancy and there are many major quality-of-life improvements still to be made. These will trickle out over time, but for now, I hope this tutorial helps set you on the path to more efficient IL2CPP reverse engineering. Happy hacking!

RaaS – Ranting as a Service

I play video games badly and complain about the state of the video games industry regularly on my livestream: https://trovo.live/djkaty.

I don’t usually code or taking coding questions on the stream so please keep those to the comments below, but if you like games and banter I’d love to meet you! (Warning: very frequent use of mature language and mature themes)

Categories: IL2CPP Tags:

Practical IL2CPP Reverse Engineering: Extracting Protobuf definitions from applications using protobuf-net (Case Study: Fall Guys)

August 10, 2020 Leave a comment

DISCLAIMER: The following information and source code is provided for educational purposes only. I do not condone cheating in online multiplayer games and expressly discourage this behaviour. This tutorial is intended to demonstrate the thought processes and techniques involved in reverse engineering. It is not intended to enable cheating, the modification of gameplay or any interference or alteration of any server-side components of the analysed product in any way whatsoever. Check your local laws before using this software. At the time of writing I have never connected to a Fall Guys network endpoint or launched the client.

You can download the full source code for this tutorial from the Il2CppProtoExtractor-FallGuys GitHub repo.

Introduction

Il2CppInspector provides several powerful tools to interact with IL2CPP application code and data via static analysis:

  • A low-level binary representation (Il2CppInspector) which allows you to query the IL2CPP metadata in its original format
  • A .NET type model (TypeModel) which provides a Reflection-style API to all of the types in the application
  • An application model (AppModel) which provides an API to query the compiled C++ types, methods and other symbols in the binary, including those not represented by .NET types

In this article, we will leverage the .NET type model to inspect a game and derive a Google Protobuf .proto file encapsulating its network protocol.

Pre-requisites:

  • Knowledge of .NET, C# and LINQ
  • Basic awareness with what IL2CPP is and what it does (no in-depth knowledge needed)
  • Basic awareness of what Google Protobuf is
  • Basic knowledge of how to use a disassembler such as IDA and how to read basic x86-64 assembly code
  • An inquisitive mind

In this article, you will learn:

  • How to set up a new Visual Studio project which uses Il2CppInspector
  • How to load an IL2CPP application and create a type model
  • How to use LINQ to query .NET types, interfaces, fields, properties, generic type arguments, arrays and attributes in an IL2CPP application
  • How to extract constructor arguments to custom attributes not retained by IL2CPP in the metadata
  • How to transform all of the combined data into a .proto file

The game at hand today is Fall Guys published by Devolver Digital, a Battle Royale-style party game where 60 players race around in bright colorful maps vying for victory. The game requires an upfront purchase and then has microtransactions on top. Being asked to pay more for the rest of the content when I’ve already purchased a game makes me very cantankerous, and Fall Guys also happens to be compiled with IL2CPP, which makes it the perfect target for some reverse engineering fun!

Although I’m using Fall Guys for this example, many of the techniques described below are applicable to any game deployed with IL2CPP and using Protobuf.

Read more…
Categories: IL2CPP Tags:

IL2CPP Reverse Engineering Part 1: Hello World and the IL2CPP Toolchain

June 24, 2020 3 comments

[You can use Il2CppInspector to help automate the techniques outlined in this series]

[Updated 27th November 2020: the command-line build instructions were updated to be compatible with Il2CppInspector 2020.2.1]

In this article, you will learn:

  • what IL2CPP is and why it exists
  • what the generated C++ source code and binary disassembly of a simple function looks like compared to native C#, IL and C++ code
  • how to setup your environment to generate C++ source code and IL2CPP binaries from your own C# code so that you can examine and compare them with your original code
  • how to use IL2CPP at the command-line on arbitrary code without Unity

Introduction to IL2CPP

IL2CPP is an alternative application deployment model introduced into Unity in 2015 which is designed to bring significant performance improvements to Unity games. It’s a beautiful mess, and today we’re going to start picking it apart.

A standard Unity game is distributed as a series of .NET assemblies which are executed by the managed runtime (CLR) on the target platform of choice as per the norm for any .NET application. The premise of IL2CPP is to take these assemblies, parse the IL, generate C++ equivalent source code from it, then compile this C++ into machine code for faster, unmanaged execution. This is described quite well on this page of the Unity manual with this diagram:

A diagram of the automatic steps taken when building a project using IL2CPP

There are several excellent guides about how IL2CPP generates code such as Unity’s own IL2CPP Internals blog series and Jackson Dunstan’s exquisitely detailed musings, so I’m not going to repeat that work here. Instead, I want to focus on the opposite perspective: how do we reverse engineer compiled IL2CPP binaries?

Unity games have traditionally been exceptionally easy to reverse engineer, generally requiring nothing more than a copy of ILSpy (or my preferred tool Telerik JustDecompile) and a dream. IL2CPP changes all that: we go from neat assemblies – often with all of the function and variable names intact – to straight up machine code that we have to wade through in a disassembler. Suddenly, even finding the areas of interest becomes magnitudes tougher. How can we make this task easier?

To answer that question, we’re going to need to develop a deep understanding of how IL2CPP manages types and data under the hood, and that’s what this series is all about. Buckle up!

Read more…
Categories: IL2CPP Tags:
%d bloggers like this: