Target

Since we are shifting to a new dimension, we have to shift to a new target. Several of the following lessons will be targeting Urban Terror 4.3.4. This game is an FPS based on the Quake engine.

Like Wesnoth, this game is open-source and has no integrated anti-cheat. It also runs well on low-spec hardware. Unlike Wesnoth, the Chocolatey package is broken. Due to this, the best way to install the game is to download and run the installer from the site.

You will need to enable 3D acceleration in VirtualBox for the game to function. Depending on your computer’s hardware, it may not be possible for your machine to run a 3D game inside a VM. In this case, you have several options. Some are better than others:

  1. Explore another hypervisor, like VMWare or Hyper-V.
  2. Use another machine as a dedicated hacking computer and isolate it from your home network.
  3. Find another target game with even fewer requirements and follow along with the concepts of the following lessons.
  4. Partition your hard-drive and dual-boot. Even if you encrypt your personal drive, it is possible for malicious tools to access your personal data.
  5. Run the target and tools on your personal machine and hope that nothing malicious happens.

Identify

Our goal in this lesson is to create a wallhack, a type of hack that allows us to see other players through walls. We will not modify any of the graphics functions of the game. Instead, we will use the game’s rendering logic and modify sections of the game’s memory.

Understand

In 3D games, depth testing is used to determine when an item should be visible in the player’s viewport. For example, if a player is behind a wall, depth testing will tell the rendering logic of the game to not draw the player.

All wallhacks operate on the principle of disabling depth testing. One way to do this is by hooking the graphics library of the game and disabling depth testing through library functions. We will cover this approach in the next lesson. In this lesson, we will rely on the game’s built-in rendering logic to achieve our goal.

Games have to draw many dynamic objects, including players, weapons, and map assets like doors. These objects are normally referred to as entities. To simplify development and increase performance, games will often use the same function for drawing all of these entities.

However, these entities often have different rendering considerations. A game may want to draw shadows on characters, but not on static entities like doors that can be opened. Games will often have structures for each entity and store these rendering considerations in the entity’s structure. When the entity is rendered, the game will check this member and render the entity according to it.

For some entities, like puddles of water or glass, games will want to disable depth testing. Because of this, the render member in the entity class will have a disabled depth testing value. If we can locate the function responsible for drawing entities and then modify all entities to contain this disabled depth testing value, players will appear through walls.

Target Setup

All games that are based on the Quake engine have a console. This console can be accessed by hitting the tilde (~) key while in game. This console allows you to run commands, such as moving the player or changing a map. These commands typically start with a backslash (\) and can be auto-completed by hitting tab. Some helpful commands for our purposes are:

  • \devmap abbey - start the map Abbey with cheats enabled
  • \g_gametype 0 - set the default game mode to deathmatch
  • \bot_enable 1 - enable bots to join a game
  • \reload - restart the current map
  • \addbot boa 1 - add a bot

In addition to these commands, we can easily switch the game to a windowed mode by hitting Alt+Enter.

Locating Draw Entities

By exploring the commands available to us, we can find several drawing commands under \r_:

Urban Terror Console Commands

The most important command to us is r_drawentities. By setting this value to 0, entities are not drawn in the game, including our player:

Player not being drawn

We can assume that the game’s code looks something like:

if(r_drawentities == 1) {
  draw_entities();    
}

To find this code, we will use Cheat Engine to find the address of the variable holding the r_drawentities value. We can switch the value of r_drawentities in the console from 0 to 1 to narrow this value down. Then, we can use a breakpoint on access in x64dbg to locate the code that accesses this value. The breakpoint should pop at the following code:

Urban Terror r_drawentities

We can see that the value of r_drawentities is loaded into ecx and then tested. Testing a register against itself compares that register’s value to 0. If the value is equal to 0, the game jumps over the call at 0x52F71F. This call is most likely responsible for drawing entities in the game. We can confirm this by nop’ing out this call. When it is nop‘d, the game will not draw any entities.

Entities and Rendering

If we step inside the call at 0x52F71F, we see the following code:

Urban Terror Entity Rendering Code

We can see in the second highlighted block that values are loaded into several registers and compared to certain values. If these values are equal, the game jumps to different locations and executes different rendering code. If we look closely, we can see that the registers are based on values of the address held in ebx. If we look up at the first highlighted block, we find the closest location in which ebx is set. We now know that at address 0x52D2FD, ebx contains what is most likely the current entity to render. If we set a breakpoint at this address and observe ebx’s address in the dump, we see a chunk of data:

Urban Terror Entity Structure

Since this chunk of data is isolated from other data and in one continuous section, we can assume that it represents a structure of some type. For example, it might look something like:

struct entity {
  int type;
  int render_type;
  float location[3];
  ...
}

To determine what location of the structure holds the render type, we must reverse this structure.

Reversing the Entity Structure

There are many ways to reverse an unknown structure in a game. One way is to build up a dataset of valid values and then make inferences based on these values. For example, if all the structures contain one member that constantly increases, we can assume that this member is being used as a counter of some type.

In this case, our goal is not to fully reverse the entity structure, but to only reverse enough to find the render type variable. Since we have located the code responsible for drawing entities, we can set a breakpoint in that code and observe entity structures. Like we discussed in the last section, at address 0x52D2FD, ebx holds the address of the current entity to render.

You will notice that each time our breakpoint is hit, ebx contains a different value. While we could manually follow ebx in the dump each time the breakpoint is hit, a more convenient way is to use the Watch feature of x64dbg. Adding an expression to the Watch panel allows us to observe it independently of the dump. In this case, we can watch the expression [ebx] and always view the current value of the address in ebx.

To add a value to watch, open up the Watch panel (near the dumps), right-click, and choose Add:

Watch panel

In the modal that appears, type your expression. In this case, we want to start with just [ebx]:

Watch panel

We want to also observe the first chunk of the entity structure. For now, we will assume that all these values are 4 bytes long. Add watches for [ebx+4] through [ebx+2C]. After you are finished, the Watch panel should look like:

Watch panel

With all of this set up, disable your breakpoint and load into a map with water. The map Abbey has a fountain near the top-left of the map. Make sure you are facing the water and can see the ground beneath it.

Abbey pond location

With all of this set up, re-enable your breakpoint at 0x52D2FD and it should pop instantly. After observing the value of the watch panel, continue execution. After observing many iterations, you should start to notice some trends.

Watch panel

Watch panel

Watch panel

The value of [ebx] (red) always appears to be 0. The value of [ebx+4] (blue) appears to alternate between 0xD, 0x40, 0x82, and 0x83. The value of [ebx+8] (white) appears to increase consistently, from 0x79 to 0x80 to 0x81, and so on. The values highlighted in pink appear to alternate between seemingly random values and 0. Likewise, the values highlighted in yellow appear to be random, yet consistently tied to [ebx+8].

All this data can be overwhelming, but we can make sense of it by eliminating values we do not care about. We know that we have at least three entities on the screen: our player model, our weapon, and the water. We can assume there are probably other entities, such as doors, as well. Since most of these entities share many similarities, we want to look for data that is relatively consistent between at least two entities. However, we also know that some of the entities should not share this value.

With this model, we can eliminate [ebx] (red), since it is always 0. We can also eliminate [ebx+8] (white), since it is unique for each entity. Both the values in pink and yellow appear unique for each object. This leaves us with [ebx+4] (blue), which alternates between 0xD, 0x40, 0x82, and 0x83. For now, we will guess that this is our rendering value and investigate each value.

Modifying Rendering Value

If we set the value of [ebx+4] for each entity, it will be overwritten the next time the draw entities function is called. It appears that the entity is being loaded into ebx from another location. Therefore, the easiest way for us to explore our assumed rendering value is by hooking the location 0x52D2FD and setting [ebx+4] for every entity. We could create this code cave in x64dbg, but to make it easier for us to test multiple values, we will create our hook in a DLL.

We will use the same hooking structure discussed in the Code Caves & DLL’s lesson. Our hook will be at 0x52D2FD, since we know ebx will contain the correct value at that point. Our hook itself will be relatively simple: we will save the registers, set the value of [ebx+4], restore the registers, and then execute the original mov instruction:

DWORD ret_address = 0x0052D303;

__declspec(naked) void codecave() {
  __asm {
    pushad
    mov dword ptr ds:[ebx+4], ??? 
    popad
    mov dword ptr ds:[0x102AE98], ebx

    jmp ret_address
  }
}

For our first value, let’s start on the highest end and try 0x83:

mov dword ptr ds:[ebx+4], 0x83

Once the DLL is injected and you are back in the game, you will notice that nothing appears to change. Likewise, if you try 0x40, you might notice that some shadows seem different, but everything looks pretty similar. Next, let’s try 0xD:

mov dword ptr ds:[ebx+4], 0xD

Immediately, you should notice that your character’s model now appears see-through in front of the camera:

Disabled depth testing

This is a good sign that depth testing may have been disabled. Next, switch to third-person mode (cg_thirdperson) and add some bots. As you move around, you should notice that you can now see all bots through walls:

Disabled depth testing

With this, we have successfully set the rendering value for all entities to disable depth testing. We can see that other entities, such as guns and stairs, appear through walls as well.

One improvement is to re-enable depth testing for our player model so that first-person mode is not corrupted. To do this, you will need to identify the player structure and your current player.

The full source code for this hack is available on github for comparison.

 

results matching ""

    No results matching ""