carter jones

Halo Hacking

This blog post will demonstrate how to go about reverse engineering structures and functions of interest in a binary. Once this has been accomplished, I will show how to modify the program at runtime to alter the program flow in our favor.


For our example, we will be using Microsoft's Halo for the PC, using build number Because this is a first person shooter, classic examples of functions of interest would be the ammo counter, health counter, etc. If we are able to modify those functions' functionality, we would be able to gain unlimited ammo, health, and more.

finding the target instruction

Using a program like Cheat Engine, we can find the functions of interest. The basic process involves searching the program's memory for a value, such as the current ammunition count, then changing the target value (in our case by using up some ammo), searching for the new value out of the current set of previous matching addresses, and so on. Eventually, this process can be used to narrow down where the value is currently stored.

Once we have the target value's address, we can modify it for the current session all we want it, but it will possibly change the next time the program is run. So what we need to do is find out what value writes to the target value's address. We would attach a debugger and set a break on write breakpoint. Cheat engine provides this functionality, as well as WinDbg.

Using Cheat Engine, I have found that for this instantiation, the amount of ammo is stored at address 0x401001F4. Using WinDbg, we can verify this:

0:013> d 0x401001f4
401001f4  3b 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  :...............
40100204  00 00 00 00 ff ff ff ff-a9 2b 00 00 00 00 00 00  .........+......
40100214  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
40100224  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
40100234  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
40100244  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
40100254  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
40100264  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

I currently have 58 in my ammo counter and according we can see the same value in WinDbg (0x3a in hex = 58 in decimal). To see what instruction writes to this address, I'll set a breakpoint on 0x401001f4 and fire the weapon to decrease the ammo count:

0:014> ba w 1 0x401001f4 "r;g"

The "r;g" tells the debugger to print out the registers and then go (continue) each time the breakpoint is hit. When the breakpoint is hit, WinDbg prints out the following line:

004c269a 7f0c            jg      halo+0xc26a8 (004c26a8)                 [br=1]

This is the line that follows the instruction that wrote to our target value. To provide a little context, we look a few bytes back and see the instruction that writes to our target address:

0:004> u 004c269a-4
004c2696 66894608        mov     word ptr [esi+8],ax
004c269a 7f0c            jg      halo+0xc26a8 (004c26a8)

If we set a breakpoint on that address so it will stop before it is hit, then we can examine what esi's value is. We expect esi to be 0x401001f4-8 (0x401001ec), and we expect ax to be the current ammo count.

0:004> bp 004c2696 "r"
0:004> g
eax=40100037 ebx=00000000 ecx=4078e354 edx=4078d78c esi=401001ec edi=400fff3c
eip=004c2696 esp=0018d998 ebp=4078e3e0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
004c2696 66894608        mov     word ptr [esi+8],ax      ds:002b:401001f4=0038

Sure enough, esi's value is 0x401001ec as expected and the lower byte of eax is 0x37, which is our current ammo count. At this point, we have successfully found the instruction that modifies the ammo count of the player.

modifying the target process

We can use a memory analysis framework like nouzuru to patch the program at runtime:

For values like ammo, grenades, and flashlight power, we can stop decrementing their value by simply overriding the modifying instruction with nop instructions (the "no operation" instruction). However, for values such as invisibility or shields, simply noping out instructions won't suffice.

In the case of invisibility, we want to be able to enable invisibility at all times, so simply nullifying the invisibility decrementer won't do the trick. In the case of shields, if we nullify the shield decrementer, then other characters like Elites, who also have shields, will never have their shields decremented. That would end up in an endless firefight, because we have enabled unlimited ammo.

Therefore, I reverse engineered the Master Chief structure so that these values could be set at specific values. To keep the values at a certain setting, I used a value freezing routine:

This routine gets the player object's current base address, calculates the invisibility and shield offsets, and sets them to the desired values. Then, it sleeps for a pre-determined amount of time and repeats. This allows the player's shields to be set high enough for no damage to be done.

master chief structure

For reference, the results of my reverse engineering efforts revealed the following important members of the Master Chief data structure:

0x000: float XAxisPosition;   // The X axis of the character's in-game position.
0x004: float YAxisPosition;   // The Y axis of the character's in-game position.
0x008: float ZAxisPosition;   // The Z axis of the character's in-game position.
0x00c: float XAxisVelocity;   // The velocity the player is traveling along the X axis.
0x010: float YAxisVelocity;   // The velocity the player is traveling along the Y axis.
0x014: float ZAxisVelocity;   // The velocity the player is traveling along the Z axis.
0x01c: float DirectionFacing; // The direction that the player is facing.
0x088: float Shields;         // The amount of shields the player has.
0x1a8: byte  Invisibility;    // A byte indicating the state of invisibility for the player. 0x51 indicates that the player is currently invisible. 0x41 indicates that the player is currently visible.
0x2c2: byte  GrenadeFrags;    // The number of frag grenades that the player currently has.
0x2c3: byte  GrenadePlasmas;  // The number of plasma grenades that the player currently has.

To find the base address of the Master Chief, we need to find the base address of the current level. To find the base level, look at the two bytes at the static address 0x4000000b. These can be used to identify which level the player is on. With this information, we can calculate the base address of the player using the following level offset dictionary:

Using these offsets, we simply read them depending on which level the player is on and add 0x5C to get the Master Chief's base structure address:


try it yourself

If you would like to try out my Halo trainer, feel free to clone it from github at

git clone

If you would just like the precompiled binaries, you can download them from here.

If you would like to mess around with nouzuru, the platform I wrote for making memory analysis/modification/debugging software, feel free to check it out at Or to get started right away, just issue this command:

git clone


CVE-2014-0301 Analysis

This blog post will demonstrate how to trigger a vulnerability with only the public information available provided by the vendor during a security patch release cycle. For this analysis, I will be using CVE-2014-0301 that was patched in MS14-013.

casing the target

Firstly, I gathered as much information about the target vulnerability as possible. By simply looking at the bulletin, it can quickly be seen what files were patched. For Windows 7 (32-bit), the new file information includes qedit.dll, with a file modification date of 2013-06-03.

Next, I made a backup copy of the old versions of the files, installed the update, and made a backup copy of the new versions of the files. At this point I had both a patched and unpatched copy of the qedit.dll file.

finding the patched function

For this example, I will be focusing on the Windows 7 version of these files. The first thing to do is to diff the files and see what changed. I did this with DarunGrim3, an automated binary diffing tool. Here are the results of the diff on the patched and unpatched file, sorted by security implication score. (Note: any mis-matched functions have been removed from the listing, so that only functions with matching names between patched and unpatched versions were kept in the list.)

The security implication score assigned by DarunGrim3 is calculated based on several heuristics which are applied to any code added including:

  • Addition of a cmp / test instruction.
  • Presence of 0xFFFFFFFF anywhere.
  • Call to a member of the strlen() family, which generally can be used to patch buffer overflows.
  • Call to StringCchCopy() family, which is used to patch buffer overflows.
  • Call to ULongLongToULong() which is used to patch integer overflows.

Here are the results of the diff on each file, sorted by security implication score (the mis-matched functions have been removed from the listing):

A diff of the Windows XP versions of qedit.dll was also performed to help narrow down the patched functions. Here are the patched functions:

We can see that the functions patched in both Windows XP and Windows 7 are the following:

  • LoadJPEGImageNewBuffer
  • OpenTGAFile
  • OpenDIBFile
  • CGenStilVid

Searching for these functions in the popular disassembler, IDA, gives us the following function definitions:

LoadJPEGImageNewBuffer(unsigned short *, class CMediaType *, unsigned char * *)
OpenTGAFile(void *,uchar * *,CMediaType *,uchar *)
OpenDIBFile(void *,uchar * *,CMediaType *,uchar *)

~CGenStilVid() diff

The first changed function looks to be a destructor, which might be interesting, but it turns out that there are no real changes, with the exception of changes to file offsets. This makes sense since its implication score was 0. It is safe to rule out this function at this point.

LoadJPEGImageNewBuffer() diff

Most of the changes in this function are caused by what are likely compiler optimizations having to do with comparisons. However, one change is significant. After calling LoadJPEGImage(), the function continues on to a block that deletes a pointer. A new mov [esi], ebx instruction is added. In the patched version of the function, ebx maintains a value of 0, so this new instruction simply clears out the value stored at esi.

At first glance, this is indicative of a patch to a dangling pointer, because the patch is to explicitly clear out a reference to the deleted object, so that it won't be re-used. In the case of the unpatched CGenStilVid object, it is deleted twice, as we will soon see.

OpenTGAFile() diff

A couple registers (eax and ebx) swapped purpose in the last part of this function. This was because the instruction to assign the value at arg_4 to a register was moved further on down in the function. Additionally, at the original place of instantiation, the register is instead set to 0, as can be shown by this diff:

That's the extent of the meaningful changes in this function.

OpenDIBFile() diff

The changes in this function are similar to OpenTGAFile(). A newly added internal variable is set to zero. Then, some other changes in register/variable usage occurs, but the semantics of the function remain the same.

analyzing the changes

It is safe to assume that the changes to OpenTGAFile() and OpenDIBFile() were the result of compiler optimizations, since they did not have any significant bearing on the semantics of either function.

However, the changes to LoadJPEGImageNewBuffer() actually change the purpose of the code, cleaning up what looks like a dangling pointer, so at this point, it is safe to assume that this is the function containing the security patch for CVE-2014-0301.

how to hit the patched function

I tested to see if qedit.dll was loading with common Microsoft programs such as, Excel, Outlook, PowerPoint, Word, Lync, Windows Media Player and even Adobe Acrobat but none of those targets seem to use qedit. So this might only be a vulnerability that more specifically affects 3rd party software using this Microsoft API.

One such 3rd party software, a media player known as Media Player Classic (MPC), will play lots of media types, including JPEG files. When a JPEG image is loaded MPC, it will load our target DLL, qedit.dll. The following following JPEG was used for the purposes of this analysis:

When we attach WinDbg to MPC and break on the LoadJPEGImageNewBuffer() function, we can trace execution down to a call to LoadJPEGImage(). If this returns a nonzero result in eax, then our vulnerable code path will be executed.

getting LoadJPEGImage() to fail

There are a few functions called by LoadJPEGImage() that could return error codes. Since invalid size values have been the source of many failures to parse various file formats, my first thought was to mess with the length values in the JPEG image by using a binary file editor, such as 010 Editor. By using the JPG binary template, I simply went through and tested the effects of changing any value that looked like a size value to either 0x00, 0xFF, or 0xFFFF. Most of the values that I changed caused the function to never even get hit. However, I eventually had success in hitting the LoadJPEGImage() function when setting the htInfo value of a Huffmann_Table structure to 0:

With this value set to 0, the image is parsed as usual, but fails during the call to LockBits() with the error stored in eax as 7. Parsing a valid file results with eax being set to 0, which is the Ok Status. According to the MSDN documentation for Bitmap.LockBits(), this error code translates to Win32Error as a part of the Status enumeration.

This error is passed to ResetFormatBuffer(), which is followed directly by ConvertStatustoHR(). The status saved to eax is 0x80004005. At this point, the function cleans up the stack and returns execution back to LoadJPEGImageNewBuffer().

the crash

Once execution is returned back to LoadJPEGImageNewBuffer(), it continues on to a call to delete. This is the first free that occurs.

Later on in the execution, the second free is called on the same object:

The application crashes on the call to ntdll!RtlFreeHeap() during the second call to msvcrt!free().


I hope you enjoyed this breakdown of how to diff a patch and produce a crash based off of the diff. I leave the process of creating an exploit based on the crash as an exercise to the reader.

Special thanks to Bill Finlayson for his sage like wisdom and all of his help during the analysis of this vulnerability.

DarunGrim with Symbols

DarunGrim is a patch diffing utility written by Matt Oh. It uses IDA Pro and a few open source python libraries to perform patch diffs. At the time of this writing, DarunGrim 3.12 Beta is the most recent release. It can be obtained from Github. If that link goes down, you can get it from this mirror. This page tells you which prerequisites are needed, where to get them, and how to install them. Alternatively, you can use this zip file that contains all the prerequisites in one place and save yourself some from having to hunt them down (DarunGrim uses some pretty old versions of the required libraries).

If you're using a recent version of IDA Pro (I'm using 6.3), you'll notice that after performing a diff, sometimes the symbols used by IDA (and therefore DarunGrim) will not be accurate. For instance, consider some of the results of a diff performed on d2d1.dll:

Doesn't provide a lot of information about what each function does right off the bat, does it?

To get symbols listed in the diff results, you need to use a plugin called pdbext. You can read about what it does here. Just extract the plw file to you IDA/plugins directory in %programfiles%. Simply put, pdbext loads the proper symbols into the IDA database. Even though IDA will ask you if you want to use the Microsoft symbol server, it does not always use it.

Open the IDA databases that DarunGrim produced during its analysis. For instance, the folders containing the files I am analyzing are in DarunGrim folder\Binaries\Microsoft Corporation\dll_name\version\dll_name.idb. If this is the first time opening this database since installing pdbext, it will ask you what remote and local sources you want to use for obtaining symbols. I use the default settings:

remote: srv**

Once these values have been set, select Edit -> Plugins -> Load PDB file (dbghelp 4.1+). This will load the symbols into the IDA database file. Close the database. When it asks how to save the changes to disk, select "Pack Database (Store)". Do this for all of the binaries that were analyzed by DarunGrim.

Once all the necessary IDA database files have been updated with symbols, go back to DarunGrim, open the previous diffing analysis results and hit Reanalyze. This will perform the diff again, but this time, it will use the IDA databases that contain the proper symbols.

Obviously, the diff is a very different result. Hopefully, it is more useful to you.