Joined: 29 Jun 2010
|Posted: Sat Jul 17, 2010 6:40 pm Post subject: walkthrough: build speed hack for Age of Empires II
I've seen a few posts lately asking about AoE hacks, and I decided to load up my dusky old game CDs to check the game out. The first conclusion I came to is that this is still a VERY fun game! The process of getting a rough, but working, hack took about three hours. While working, I used the awesome "table comments" feature in CE to document my steps and thought processes. For what it's worth, I'm happy to share them.
The first thing I did was search for and modify my resource holdings. Although it wasn't directly necessary to do so, I figured that I'd be building many structures for test purposes and doing so requires wood/stone/food/etc. This process also gave me some clues as to the game's coding - i.e., many values are coded as floats, pointers aren't nested tooooo very deeply, etc.
Building a House and Finding the Build Progress Variable:
Once I had my resources under control, I started building a house. While the house was being built, I hit f10 to pause the game and searched for the build completion percentage. CE's ability to truncate digits was wonderful here, as having to enter ranges or worry about rounding precision was basically rendered unnecessary. My first searches were for values of the float datatype, given my experience w/ the resources. I didn't find anything. My next attempt was to search for double precision numbers. I didn't find anything. My next and last attempt was to search for bytes - since I now assumed the percentages ranged from 0 to 100 and were not stored in an encoded format (float or double, at least), a byte would surely pick them up. In point of fact, it did.
The scan for the build percentage converged quickly on four addresses, three of which were "near" to each other and the base of memory and one that was stored in a much higher address. I arbitrarily chose to focus on the one with the high address and asked CE to show me what writes to it. CE quickly popped up a single piece of code that modified the value I was interested in.
I asked CE to show me the disassembly view and examined the area in the immediate vicinity of the code that wrote to the build percentage variable while looking for anything that incremented or decremented the variable. I did not find what I was looking for, but there was a function call just a little bit before that I decided to check out. After selecting the call instruction and pressing the space bar, CE took me to the function's body. Here, I found that the value I was looking at was the result of a few floating point operations. This was a good sign, indeed. At this point, I decided that the values I had seen changing were probably the display values and didn't actually affect build-speed - I needed to "get at" those floating-point values.
Continuing to examine the code in the function I had stumbled upon, I saw that - in summary - a value was being loaded from memory, multiplied by 100 and divided by 25. Aha! Now I knew what to search for! Going back to the main CE interface, I tried searching for float values that were approximately 1/4 of the build percentage - i.e., if the game showed 20% build completion, I'd search for 5 (which, with CE's truncate functions will grab anything from 5.0ish to 6.0ish). Voila - I was now able to find the completion percentage variable for houses that I was building! I could verify my success by altering the values or freezing them - and I assure you, I could sympathize with the poor villagers working on a house that was never finished!
Who Really Owns Your House?
At this point, I had identified the code that updated build progress (for player-owned houses, at least) and I understood which variable held the current progress. But to make a hack that would only benefit myself, I needed to figure out whether the code in question was operating on one of my properties on any given iteration. I have recently seen this process called "stapling an intersection", and it is a rather fitting moniker.
To staple the intersection, I turned my attention to the data structures involved. By asking CE to tell me what accessed one of the float-type build progress variables I had isolated, I saw that it was being referenced at all times as [someregister+0x220]. This told me that the base of the data structure that held the build progress probably started 544 bytes before the address that held the progress variable and that the structure was probably at least 544+4 bytes long. Armed with this knowledge, I fired up the CE data dissector (memory-view -> ctrl+d).
The data dissector immediately made some obviously interesting deductions. For example, there was a pointer to a string containing the type of building I was working on, a string containing the sound effects associated with the building-type, etc. I slapped some descriptive labels on anything that I thought I could identify, then started using the same template to view new buildings whose addresses I'd found in the same fashion. In the process, I looked for other identifiers related to buildings that I could search for - health, appearance, etc. This allowed me to identify and label more offsets, giving me a better mental picture of the data structures at work. My ultimate goal, however, was to identify structure members which were invariant for my own buildings and exclusive to those buildings that owned. After comparing a few such structures, it seemed that there was a pointer which was always null for AI buildings and non-null for those that I owned! This was just what I needed.
Upon learning which code modified build progress, which variable stored it, an offset from the progress to the health, and figuring out a way to determine building owners, I was ready to make a hack. After my experimentation, I knew that the game would allow me to enter arbitrarily large numbers for health and build progress - progress over 100% simply renders the building completed while health bleeds over. Knowing this, and being lazy, I decided to just increment both by a large amount. A more elegant hack would determine the proper "max" values and set them appropriately, but this would work well enough. The hack could also be trivially extended to slow build progress for opponents, make buildings indestructible, make opponents' buildings take extra damage from attacks, etc. Someone interested in using this hack for online play, too, would presumably need to endeavor to dissect the data structure further. However, the same reasoning and methodology can be used to extend the hack for these purposes or others (i.e., speeding up unit builds in addition to building builts).
For the sake of completion, I present the script I ended up with. I apologize for the poor coding, as I am most definitely not an assembly coder by nature. Hopefully it makes up with comments what it loses in style.
|Auto-assemble script for AoE Conq build speed hack.
|| aoe build speed hack (aobscan).CEA
|| 1.51 KB
|| 2682 Time(s)