From Vision to Architecture

Introduction

In our previous essay, we talked about the product vision of Cheat Engine. In this essay we focus on the underlying architecture of Cheat Engine, thus looking at the source code and the dependencies and interactions of different components. The source code can be found on their Github page 1. By analyzing the source code, we hope to get a better understanding of the system’s architectural elements and relationships, as well as the software’s principles of the design.

The main architectural style

Although Cheat Engine is a rather large and widely used application, the codebase shows little to no structure, as the functions of the code is unorganised and all over the place. This is mostly caused by the development tools themselves; functionality tends to be added directly to UI elements, and as such are grouped based on the different UI windows. Because a lot of the functionality is directly or indirectly tied to the TMainForm window, most of the important code is part of the MainUnit.pas file 2, containing over 11.000 lines of code. The one interesting pattern used is a server-client setup for the connection between the main process and code injected into other processes.

Containers view

Figure: Container diagram

Since Cheat Engine has quite a specific way of working, the container view is rather limited as well. The main functionality is for a user to interact with local processes using the Cheat Engine application. Besides this functionality, Cheat Engine also allows the user to write and read Cheat Tables. Cheat Engine is able to retrieve and store these tables both locally and online on forums(SRC).

Components view

Figure: Component diagram

As mentioned earlier, most of Cheat Engine’s functionalities are within a single class. Therefore the component’s view is rather limited as well. Some of the main secondary windows are added as components, though they may not have as clear of a separation within the codebase. These secondary components tend to have standalone functionality within the program and don’t interact a lot with each other.

Connectors view

The most important connectors in Cheat Engine are those that are facilitating interaction with other processes. The MonoDataCollector and DotNetDataCollector are components that are injected into other processes. In order to make the Cheat Engine process interact with these components, a server-client pipe setup is used with a custom communication protocol. This has to be used because the injected code is no longer under the control of the main process.

Development View

The main goal of Cheat Engine is to manipulate and change values of memory addresses in a process 3. To achieve this goal, the developers created multiple units with their own tasks and dependencies. As there are more than 50 different units 1, we will focus on the most important units.

The MainUnit controls the main window of the program and is responsible for spawning all other units. A lot of the simpler functionality is also contained within this unit, such as handling the cheat table and doing basic memory scans.

The MemoryBrowserFormUnit is the most important secondary unit. It provides different direct views of the process memory, including a code disassembly view. More complicated interactions with the process, such as using breakpoints or looking for structures within the process are usually initiated from this unit.

For accessing memory addresses, multiple different units are called. Mainly the RemoteMemoryManage and MemoryRecord units are important. The RemoteMemoryManager unit contains a class that will be used to control remotely allocated memory blocks. The MemoryRecord unit records all memory addresses and return their values to the MainUnit

The decision of using one main unit as the core of the program is nothing new, as many software systems rely on a so-called ‘godclass’. The structure of Cheat Engine is quite unique though, in that this main unit is mostly bloated because it contains a lot of the UI event handlers of the TMainForm window. These could and probably should be split over different files based on their functionality.

The older parts of the codebase have very informal comments and barely any documentation. Some of the newer parts do have annotating comments, but their usage is sporadic and the style is very informal and occasionally explicit.


Run-time View

During run-time, the TMainForm window is in control of all the extra units that provide their functionality. What units are loaded heavily depend on the things the user is doing, so to illustrate how some components interact, we go over a simple use-case of the tool;

  1. Opening Cheat Engine
  2. Searching and selecting a process
  3. Scanning for a memory address containing a certain value
  4. Using the next scan
  5. Recording the address
  6. Changing the value.

Opening Cheat Engine

By opening Cheat Engine, the MainUnit is started and creates a GUI which has the name of TMainForm. TMainForm has multiple buttons and panels, which can be used by the user to interact with the other components of the software.

Searching and selecting a process

By pressing the sbOpenProcess button, the MainUnit calls the ProcessWindowUnit. The ProcessWindowUnit shows a list of current processes which can be accessed. The ProcessID and Processhandle are sent back to the MainUnit.

Scanning for a memory address containing a certain value

The MainUnit contains a simple memory scanner that can look for specific data within the process. The most common usage is to look for specific values or arrays of bytes that encode an instruction. The MainUnit interacts with the memscan unit, which is used to find the memory addresses containing the value. An addresslist contains all the addresses that have the value in question.

Using a next scan

Repeated scans can be used to narrow down this list in order to find the exact memory address the user is looking for. It is usually best to try to manipulate the process in a way that the value is different from the initial value, in order to filter out static information and instructions that happen to hold the same value.

Recording the address

Addresses in the addresslist can be moved over to the cheat table. These memory addresses are recorded by TMemoryRecords, as described in the MemoryRecordUnit.

Changing the value

These TMemoryRecords can change their own value by writing new values to the process memory.

These are the key scenarios of regular use of Cheat Engine. The user can of course use other advanced options which make use of other components or units. These advanced options can be accessed by using the MainUnit, as most of the interactions are from the MainUnit to the other units.


Relation between architecture and quality attributes

In our previous essay about the product vision and key attributes, we described the four key quality attributes of Cheat Engine, namely Modularity, Maintainability, Extensibility, and Compatibility. This section analyzes how the architecture implements the key quality attributes and the potential trade-offs.

For Modularity: the developers of Cheat Engine used Pascal to create modules with different functions to implement the main functions of manipulating and changing the values of memory addresses. Each module has dependencies on other modules and handles its tasks. Several units such as RemoteMemoryManage and MemoryRecord are used to access memory addresses. Finally, all of the above modules need to interact with MainUnit to perform different tasks.

For Maintainability: As described in Modularity, the MainUnit is an essential part of the Cheat Engine. The GUI interaction and the execution of various operations require the involvement of the MainUnit, which makes the Cheat Engine challenging to maintain. If the MainUnit fails, the whole application will no longer operate. More importantly, the MainUnit is overloaded with code and undocumented, making maintenance difficult.

For Extensibility: Cheat Engine supports users’ scripts and plugins 4 5. When users use their plugins, the pluginhandler and PluginSync functions in MainUnit are called to configure the plugins.

For Compatibility: Cheat Engine has universal support for Windows and Mac OS systems 6. It detects the framework of the currently running application at runtime and offers different options to the user.


API design principles applied

Cheat Engine itself does not provide any API, but contributors have built the Cheat Engine library 7, which provide several APIs for basic functions from Cheat Engine. The Cheat Engine library provides three main types of APIs; the Process API, the Virtual table API, and the Memory scanner API. The Process API is used to get a list of all running processes. The Virtual table API has the ability to empty the virtual cheat table and add individual scripts. Finally, the Memory scanner API is used to access the scanned memory. This library enables users to create their own software with advanced features such as auto-assembly, DLL injection, memory scanner, and it supports the three programming languages C#, C++, and Delphi.

Each API contains no more than ten functions that can be called. Each function has a description and the required parameter, ensuring readability and naming consistency in the API design principles. The documentation mainly describes the API’s functionality and usage. Also, the API lacks meaningful error messages. The answers to common errors can be found in the GitHub issue, but there is no documentation of the error messages. This API needs to be imported as a library. It does not involve the transfer of information related to the server, so there is no need to consider the security of this API 7.


References


  1. Cheat Engine github. [Online]. Available: https://github.com/cheat-engine/cheat-engine [Accessed: 14-Mar-2022]. ↩︎

  2. Cheat Engine MainUnit. [Online]. Available: https://github.com/cheat-engine/cheat-engine/blob/master/Cheat%20Engine/MainUnit.pas [Accessed: 14-Mar-2022]. ↩︎

  3. About Cheat Engine. [Online]. Available: https://cheatengine.org/aboutce.php [Accessed: 14-Mar-2022]. ↩︎

  4. About Cheat Engine Plugins. [Online]. Available: https://cheatengine.org/help/index.html [Accessed: 14-Mar-2022] ↩︎

  5. About Cheat Engine Scripts tutorial. [Online]. Available: https://wiki.cheatengine.org/index.php?title=Tutorials [Accessed: 14-Mar-2022] ↩︎

  6. About Cheat Engine Versions on Windows and Mac Os [Online]. Available: https://www.cheatengine.org/ [Accessed: 14-Mar-2022] ↩︎

  7. About Cheat Engine Library and API. [Online]. Available: https://github.com/fenix01/cheatengine-library/wiki/Guideline [Accessed: 14-Mar-2022] ↩︎