You guys have been asking for this post, and rather than continue to be self-conscious about what to write, I'm just gonna start typing.
My personal trick is that I've learned the common patterns found in the Halo engine. Halo games, as you should be well aware of, have always used the same engine. So there is a good chance that once you've found something in one game, it will be significantly easier to track it down in another.
But of course you have to find these patterns first and try to get some kind of footing. You should also take into consideration just what you are wishing to accomplish. In hindsight the changes needed to crack a locked down beta are simple but the problem was finding them.
There are a few ways you can try to find the function you are looking for. The ones I have used/seen are:
- Bruting data references
- Checking rdata strings
- Checking import functions
The first is probably most familiar to anyone who has followed a tutorial like Chrisco's that has you dump floats and brute through them all looking for changes. But tutorials like his tutorial only stops at finding the values, rather than further exploring them.
Those tutorials also have you needlessly dumping for data values than necessary. By only searching something like ".float" you are grabbing every instance, and many of which aren't even directly referenced by the actual code. You can narrow it down by searching instead for ": .float", which the colon only appears on referenced floats, as seen here:
.data:83335AB8 flt_83335AB8: .float 0.64221829 # DATA XREF: sub_82C613F0+8D8r
.data:8337C154 .float 1.0
Though for most things, you likely won't be searching floats to find something. Bytes are much better for that, found by searching ": .byte"
Now take those bytes and set them to 1, set them to 0, set them to -1. Keep experimenting until you get a reaction. If you were to do this in a (clean) Halo 4 while sitting in the menu, setting a certain byte to 0 will make the game lock you out as though you didn't have the disc 2 content installed. Bingo. This byte is offset 0x8407FD28 in a non-updated version. Let's take a look at that line:
.data:8407FD28 byte_8407FD28: .byte 0 # DATA XREF: sub_82693A20+Co
This tells us that the instruction at 0x82693A2C (among others, but were omitted for clarity) calls this particular byte, so lets open it in a new tab by clicking that offset and pressing Alt+Enter, or right clicking it and choosing "Jump in a new window". Now we can take a look at the function and see if we can't get it to ignore that flag. This function is for the MP content.
There are a few ways you could get that done, but first let's explain some of what we are seeing it this function. Will help to turn on Auto Comments (Options>General>Auto comments).
.text:82693A2C Get the first 2 bytes of the byte's location and store it in r11
.text:82693A30 Get the last 2 bytes and assemble the offset with r11, load the byte's value into r10
.text:82693A34 Compare r10 with 0, store the result into cr6
.text:82693A38 Check cr6, and if r10 was found not equal to the given 0, then branch to 0x82693A9C (Pass, game says you have D2 MP content)
.text:82693A3C Branch off to the function at 0x82693C08 (Another function that handles Disc 2 stuff)
.text:82693A40 I still doesn't understand clrlwi, but the result gets stored in r11
.text:82693A44 Compare r11 with 0, store the result in cr6
.text:82693A48 Check cr6, and if r11 was zero, then branch to 0x82693A64 (Fail, game says you do not have D2 MP content)
.text:82693A64 Load 0 into r11 and tell you to install the D2 MP content. (You lose. Good day sir.)
While not everything, it is enough. Here are the easy ways I can think of to have my way with that function using just the above:
- Change 0x82693A30 to a li instruction, putting a value of 1 into r10, which will always make 0x82693A38 branch and pass. (in hex: 39400001)
- Make 0x82693A34 compare to another value so that the result is always found not equal, making 0x82693A38 branch and pass. (in hex: 2B0A0066, new compare value becomes x66)
- Remove the comparison check in 0x82693A38 and always branch to 0x82693A9C and pass. (in hex: 48000064)
- Go into the branched function from 0x82693A3C and try other things that will come back to stop 0x82693A48 from branching (may or may not work)
- Let it fail, but make the li at 0x82693A64 load 1 into r11 instead of 0, so you still pass anyway (in hex: 39600001)
The best of these are the first 3, because it will instantly jump to the end of the function with no chance of error. I used the last one though in my PPFs.
Another way to find a function is through strings left in the rdata section of the executable. This way may not be as fruitful unless you are looking into a debug/internal build that has a lot of juicy strings. Though there are a few strings that tend to remain even in retail builds, such as map header errors. Simply do a text search for some key words pertaining to what you are looking for and cross your fingers. If you find something that looks pretty believable you can jump to whatever function calls it and get cracking.
The last way of finding functions, and is the least fruitful/useful in many cases, is to check the imported pre-named functions from the xbox kernel. If you sort your functions sidebar by name, you'll see them at the top of the list. These will take more understanding to use, but some common ones are:
XamUserGetSigninState/j_XamUserGetSigninState - Checks your state, whether offline or connected to XBL. Checking calls to these can allow you to fool the Halo 3 Beta, Halo 3 Epsilon, and Reach Pre-Beta into thinking you are online to get past the simple blocks preventing you from starting games.
XeCrypt~/j_XeCrypt~ - Several of these exist and they are used for run-of-the-mill hashing/encryption/decryption. Checking these calls can allow you to bypass the RSA verification on various external files.
And that should be about it. Post a comment if you have anything to say/ask/make fun of
And if you wish to learn further, I highly suggest downloading a premodded xex, extracting its basefile along with the basefile of a clean xex, and doing a compare in your hex editor of choice. You can add x82000000 to the file offset of the basefile to locate the change in IDA. Try to figure out what that change did and how it does what it does.