RCE Endeavors 😅

February 2, 2011

Game Hacking: Age of Empires II (Part 1/?)

Filed under: Game Hacking,Reverse Engineering — admin @ 7:06 AM

This article will be one of an ongoing series — as I have time to write them. I’ve spent the better part of this past weekend and this week focusing on game hacking. The target was an old classic, Age of Empires II: The Conquerors.

It is an old 2D RTS game where you build up an empire, build armies, make/break alliances, and so on. For a much more in-depth explanation than I care to provide see the Wikipedia article on its predecessor. One of the interesting things that I discovered while messing around with this game is that the statistics (all resource/villager/military/… counts, current age, population, etc) counts are stored in a structure that is indexed into for the appropriate player. This wouldn’t be too interesting as a single player game, but Age of Empires is a multiplayer game, with a semi-big community of around 1200 active players. The same base classes/structures are shared between AI bots and active players, meaning there are no modifications to be made between single and multiplayer, in terms of hack development.

Finding the stat structure started off normally, by using Cheat Engine to search for the type of variable the player’s resources were stored in, and what was writing to it. Doing the usual things like building/destroying buildings, collecting resources, and making units yielded a wide variety of results. In the end, there were multiple candidates for where values are being written from, but they were narrowed down to a very good function at .text:00555470.

This is good candidate because the floating point stack is being utilized, and it was the only one like it that popped up when dropping resources off at the town center. Looking actively into this function yields clues about how it works. The important parts are reproduced below.

.text:0055544F                 mov     eax, [ecx+0A4h]
.text:00555455                 movsx   edx, si
.text:00555458                 cmp     edx, eax
.text:0055545A                 jge     loc_5554FE
.text:00555460                 mov     eax, [ecx+0A8h]
.text:00555466                 fld     [esp+4+arg_4]
.text:0055546A                 fadd    dword ptr [eax+edx*4]
.text:0055546D                 lea     eax, [eax+edx*4]
.text:00555470                 fstp    dword ptr [eax]
.text:00555472                 mov     eax, [ecx+4]
.text:00555475                 test    eax, eax
.text:00555477                 jz      loc_5554FE

This function is very interesting because it is a __thiscall and does not set up any sort of bp-based stack frame. ECX here is used without being initialized (hint for __thiscall) and the arguments are also referenced directly through the stack pointer. This is where the static code analysis ends though, and a debugger needs to be attached to follow exactly how everything behaves. OllyDbg happens to be one of the best for live code analysis.

Breakpoints set, everything can be observed as this function gets called. When I created a unit, the following values were in the registers when the first breakpoint hit.

Here ECX is the “this” pointer. It points to the base of the calling class. When the EIP is 0x0055546A, the application had the following state

1.0f was loaded into ST0. However, when I dequeued a unit, -1.0f was loaded into ST0. The second parameter is therefore some sort of flag for whether a unit is being queued or dequeued. This value is added to [EAX+EDX*4], and the result stored in the address that EAX points to. Looking at what is inside [EAX] then yields what is more or less a holy grail.

While the values may look nonsensical at first, this is actually a layout of a structure that holds a ton of statistics about a player.

The first four members of this structure correlate to the players food, wood, stone, and gold. Then comes the population left before hitting the cap, an unknown value, the current age, and so on (see player_stats.h for my listings). These values can continue to be found by setting memory breakpoints on an individual one or a region, followed by performing an action in the game and observing what breakpoints are triggered. Since the base of this structure is a member of the calling class, it can always be found at (base address of class + 0xA8), as evidenced in the disassembly of the function. Just looking at the base address of the class yields some interesting information.

In addition to having pointers to some ridiculously large structures (possibly covered in the future), the player’s name is also listed at (base address of class + 0xA8). Sidenote: The address pointing to it is different since I started another game instance between taking the two screenshots. This provides identifying information for each class that calls .text:00555470. Then the theory is that if I hook .text:00555470, I can store all of the pointers to every player in the game. This is true because this function is called by every player on the games start, and also throughout the game. Using Microsoft’s Detours library makes this extremely easy. The hook function looks as follows

__declspec(naked) void resources_changed_hook(short int res_type, float usage_type, int unused) {
	__asm {
		mov eax, temp_pointer
		mov dword ptr[eax], ecx         //temp_pointer->base_pointer points to calling class
    temp_pointer->player_name = (char*)(*(temp_pointer->base_pointer + (0x98 / sizeof(DWORD_PTR))));
    temp_pointer->player_stat = (player_stats*)(*(temp_pointer->base_pointer + (0xA8 / sizeof(DWORD_PTR))));
    if(insert(&base_pointers, temp_pointer) == true)
        temp_pointer = (item_set*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(item_set));
    __asm {
		jmp resources_changed

The stack is preserved, the address of the calling class is stored, the offsets calculated and set, and the calling function inserted into a set (no repetitions allowed).

The actual structure is a shell of the calling class and stores only the class pointer, the statistics structure, and the players name

typedef struct ITEM_SET {
	int* base_pointer;
        char* player_name;
	player_stats* player_stat;
	ITEM_SET *next;
} item_set, *pitem_set;

This can/will all be expanded and redone as more of the calling class is reverse engineered. The stored items can then be simply traversed like a list and the data for each player printed out.

void print(item_set** head) {
	if(*head == NULL)
	item_set* node_ptr = *head;
	while(node_ptr != NULL) {
        printf("Player: %s -- Wood: %1.0f - Food %1.0f - Gold: %1.0f - Stone: %1.0f\n", node_ptr->player_name,
            node_ptr->player_stat->wood, node_ptr->player_stat->food, node_ptr->player_stat->gold, node_ptr->player_stat->stone);
		node_ptr = node_ptr->next;

Here is an example of it in action:

Using this, a player is able to modify their own stats in addition to reading the stats of others. Doing something like

while(node_ptr != NULL) {
    if(strcmp("qwerty", node_ptr->player_name) == 0) {
        node_ptr->player_stat->food = 10000.0f;
    node_ptr = node_ptr->next;

Would work fine on single player. However, on multiplayer this would cause an out of sync error. Since each player keeps a copy of the other players information in their game, any unwarranted change would cause the game to go out of sync. However, simply reading memory will still be fine. The player_stats structure consists of 198 members with roughly half of them documented by me. Additional help is welcome.

A screenshot from a CBA game at Voobly — technique still holds.

Warning: The version posted here will result in a ban from Voobly. Their anti-cheat checks functions for hooks and will ban any user found to be modifying the functions in memory. This is still extremely easy to get around using hardware breakpoints and a different DLL injection technique with no keyboard hook — but that might be another part of this series. The risk takers who are uninitiated with those topics may want to try to attach the hooks prior to an in-game lobby launch and detach immediately upon entering the game to beat the anti-cheat scan. This is definitely not recommended however. Also, the fact that it is in a separate window makes this more of a proof of concept than an actual functional hack until further work/posts are done on the game.

Possible future articles as time permits:

Hooking internal drawing functions or DirectDraw functions to draw text on the screen

Reversing the protocol and packet structure

Bypassing Voobly’s anti-cheat system (large hints given)

The source code to the hooking DLL can be found here.

A downloadable PDF of this post can be found here.

1 Comment »

  1. good info..thanks

    Comment by yakasir — May 15, 2022 @ 9:25 PM

RSS feed for comments on this post. TrackBack URL

Leave a comment


Powered by WordPress