iPhone space invaders
Jan 26
This article covers the saves signing process used by the original Xbox. Only the standard signing process is discussed, the “noroam” signatures are not covered. All code is based on my own work or derivative work of others. The language used in examples is Delphi and assumes a familiarity with programming concepts such as the use of records or structures for reading in data.
The reason I’ve never previously discussed the Xbox save signing procedure in public is concern that by doing so would negatively impact Xbox Live. Now that the original Xbox and it’s games are unable to access the Xbox Live service the following information is relatively harmless.
A brief history
The original Xbox used digital signatures to validate not only executables it was loading but save data, with this it was impossible to change or tamper with a save without updating the file’s digital signature using the correct key. This key was generated using data contained within the default.xbe and a key used by the Xbox Operating System. The save data was then run through a SHA1 HMAC routine and the 20 byte result appended to the save data to confirm integrity. This result, or digest, was usually found at the beginning or the end of a file although it can potentially be located anywhere in the file if the developer was feeling adventurous.
Obtaining the XBE key
Each Xbox game has a default.xbe file, this is the executable that is loaded whenever you start a game. It contains various information including the title ID, age rating, game region and the all important signature key. Don’t get confused, this is not the key used to sign the saves but is an essential piece in the process.
The first step is to obtain the key directly from the XBE. This is stored in a certificate area of the file, the address of which is located in the XBE header. Before we can retrieve the key we need to define the structure of the XBE Header and the Certificate data we will be retrieving:
// XBE sections unit xbestruct; interface type ByteArray = array[0..15] of byte; type TxbeHeader = record m_magic : cardinal; // magic number [should be "XBEH"] m_digsig : array[0..255] of char; // digital signature m_base: cardinal; // base address m_sizeof_headers : cardinal; // size of headers m_sizeof_image : cardinal; // size of image m_sizeof_image_header : cardinal; // size of image header m_timedate : cardinal; // timedate stamp m_certificate_addr : cardinal; // certificate address m_sections : cardinal; // number of sections m_section_headers_addr : cardinal; // section headers address m_init_flags : cardinal; m_entry : cardinal; // entry point address m_tls_addr : cardinal; // thread local storage directory address m_pe_stack_commit : cardinal; // size of stack commit m_pe_heap_reserve : cardinal; // size of heap reserve m_pe_heap_commit : cardinal; // size of heap commit m_pe_base_addr : cardinal; // original base address m_pe_sizeof_image : cardinal; // size of original image m_pe_checksum : cardinal; // original checksum m_pe_timedate : cardinal; // original timedate stamp m_debug_pathname_addr : cardinal; // debug pathname address m_debug_filename_addr : cardinal; // debug filename address m_debug_unicode_filename_addr : cardinal; // debug unicode filename address m_kernel_image_thunk_addr : cardinal; // kernel image thunk address m_nonkernel_import_dir_addr : cardinal; // non kernel import directory address m_library_versions : cardinal; // number of library versions m_library_versions_addr : cardinal; // library versions address m_kernel_library_version_addr : cardinal; // kernel library version address m_xapi_library_version_addr : cardinal; // xapi library version address m_logo_bitmap_addr : cardinal; // logo bitmap address m_logo_bitmap_size : cardinal; // logo bitmap size end; pTXbeHeader = ^TXbeHeader; type TxbeCertificate = record m_size : cardinal; // size of certificate m_timedate : cardinal; // timedate stamp m_titleid : cardinal; // title id m_title_name : array[0..63] of widechar; // title name (unicode) m_alt_title_id : byteArray; // alternate title ids m_allowed_media : cardinal; // allowed media types m_game_region : cardinal; // game region m_game_ratings : cardinal; // game ratings m_disk_number : cardinal; // disk number m_version : cardinal; // version m_lan_key : byteArray; // lan key m_sig_key : byteArray; // signature key m_title_alt_sig_key : array[0..15, 0..15] of byte; // alternate signature keys end; pTxbeCertificate = ^ TxbeCertificate; implementation end. |
The TxbeHeader record contains a lot of data but the two most important sections are m_certificate_addr, and m_base. These two values give us the address of the certificate.
The following code retrieves the key from an XBE file:
var MS : TMemoryStream; xbeHeader : pTxbeHeader; xbeCert : pTxbeCertificate; begin MS := TmemoryStream.Create; MS.LoadFromFile(FileName); new(xbeHeader); new(xbeCert); //Read in header and certificate MS.Read(xbeHeader^, sizeof(xbeHeader^)); MS.Position := xbeHeader^.m_certificate_addr - xbeHeader^.m_base; MS.Read(xbeCert^, sizeof(xbeCert^)); |
We can now access the sig_key directly
xbeCert^.m_sig_key |
Generating the signing key
The key we retrieved from the XBE is not used to directly sign save data, instead it is used in a SHA1 HMAC with the Xbox key to produce the actual key we need. Thankfully we don’t need to determine the Xbox key every time we need to sign or verify a save as it is a constant.
The following is a textual representation of this key, to use it you must first convert it to a 16 byte array:
5C0733AE0401F7E8BA7993FDCD2F1FE0
Once you have this key in a byte array simply run both the XBE key and the Xbox key through a SHA1 HMAC (referring to the documentation for your SHA1 HMAC function as to whether you need to pass data or memory addresses to the function). The output should be a 160bit digest truncated to 16 bytes (the last 4 bytes are not required and should not be used or present).
var digest : T160BitDigest; begin digest := CalcHMAC_SHA1(addr(xboxKey), 16, xbeCert^.m_sig_key, 16); |
Determining the data to process
As noted before the digital signature is usually found before or after the actual save data. It is entirely possible to store this signature at any location in the file and treat this location as all 0′s during the HMAC process. Since we cannot rely on all developers using the same location for the signature we must determine the location ourselves.
The fastest and easiest way to do this is to start a new game and make a save at the earliest opportunity. If the game saves any options you change this is ideal, otherwise start playing and save as soon as you can. Copy this save to you PC and label is SAVE A.
Load the game again and create another save with as small a difference as you can but ensuring something is different. As before, if the game saves option changes you should change only a single item and re-save. Copy this save to your PC and label it SAVE B.
Open both saves in a hex editor and visually compare the two, with little differences between them you should easily spot the 20 byte digital signature either at the start or the end of the file. You can use an automatic file comparison if your editor supports it but in my experience I can find the sig faster by eye. The signature in both files should be wildly different with the actual save data very consistent, aside from the minor differences you saved earlier.
Once you have found the sig location you should exclude this from the HMAC routines. If the sig is at the end of the file you should HMAC all data up to the last 20 bytes, if it is at the beginning then all data after the first 20 bytes should be processed. If the signature is located at another location in the file then you will need to experiment as to what data to HMAC. A common trick when generating checksum’s is to treat the area containing the result of the checksum as all 0′s during the processing stage and write the result back to this location once complete. Please note, it is rare to see this, most saves store their signature at the beginning or end of the file.
Generating the signature
Now you have the correct key and the data to process you can generate or check signatures for that particular games save files. Similar to how the key was generated, the actual signature generation is a SHA1 HMAC of the save data (excluding the existing signature) with the key.
In the following example the save signature is located at the end of the file and the data is copied to a different memory stream for processing. The previously generated key is stored in a 16 byte array named sigKey
mem := TMemoryStream.Create; mem2 := TMemoryStream.Create; mem.LoadFromFile(FileName); mem.Position := 0; mem2.CopyFrom(mem, mem.Size - 20); digest := CalcHMAC_SHA1(addr(sigKey), 16, mem2.memory^, mem2.size); |
Before making any changes to a save you should confirm that the signature you generate matches that already present on an unaltered save.
It’s worth bearing in mind that a lot of saves contain a checksum as well as a digital signature. The most common being a CRC32 or a simple addition of bytes, you must recalculate this before the digital signature should you make any changes to the file.
Conclusion
The process of creating a digital signature for Xbox saves is fairly simple and can be summarized as
- SHA1 HMAC “XBE key” using “Xbox key”
- Truncate resulting 160 bit digest to 16 bytes to create the “signature key”
- SHA1 HMAC Save data using ”signature key”
- Compare resulting 160 bit digest to existing signature or write back to save file.
If you use the information above to create anything or produce a unique save, drop me a line in the comments, sometimes the most interesting time in a games life is long after it was released..
Gelatin
Apr 10
This image disturbs me.

Just what is that 1% Other?…
I’m a big fan of the community features introduced in Halo 3 and always check out the latest screen shots and videos whilst waiting for a game to start. When Halo Waypoint was released I was pleased to see a regular feature on community videos and it became a regular feature for my Wednesdays.
If, like me, you were disappointed when season 1 ended you’ll be glad to hear that it’s made it’s return today with a top 5 entitled “Welcome Aboard” focusing on the art of boarding.
The video at number 2 might look a little familiar to those that were reading (and watching) my exploits back in June 2009….
Fixing WP e-Commerce
Feb 9
I’ve been implementing the WordPress e-Commerce plug-in on another site, it’s not a bad freebie but if you’re selling anything more than a handful of items then you’ll need to let your users search your products. To add this functionality, along with a few other features, you need to purchase the $50 Gold Cart add-in.
I duly purchased and installed the update only to find that the “search” only looked at product titles, meaning that all the description text, additional description text and meta tags were being ignored. Looking around it appears I wasn’t the only person who needed this functionality and I was able to get hold of some code to change in gold_shopping_cart.php. I still didn’t get the results I was expecting so after checking out the Database table structure I went from
$category_list = $wpdb->get_col("SELECT `id` FROM `".WPSC_TABLE_PRODUCT_CATEGORIES."` WHERE `name` LIKE '".$search_string_title."'"); |
to
$category_list = $wpdb->get_col("SELECT id FROM `".WPSC_TABLE_PRODUCT_LIST."` WHERE name LIKE '".$search_string_title."' OR description LIKE '".$search_string_title."' OR additional_description LIKE '".$search_string_title."'"); |
Notice the change in the table being searched. The original code I found still looked in the WPSC_TABLE_PRODUCT_CATEGORIES table but this does not contain the additional_description field, an oversight on the original modders part.
Yet I still wasn’t getting the expected results. A quick count of the $category_list array showed I was getting results but they were getting filtered out further on in the function by this little check:
if($category_list != null) { $category_assoc_list = $wpdb->get_col("SELECT DISTINCT `product_id` FROM `".WPSC_TABLE_ITEM_CATEGORY_ASSOC."` WHERE `category_id` IN ('".implode("', '", $category_list)."')"); $category_sql = "OR `".WPSC_TABLE_PRODUCT_LIST."`.`id` IN ('".implode("', '", $category_assoc_list)."')"; } |
Remember that I’m now searching products directly and the above code is looking to match the products unique ID against a category ID, clearly this will fail on 99% of searches. A quick change to the search criteria is all that was needed to return valid results:
if($category_list != null) { $category_assoc_list = $wpdb->get_col("SELECT DISTINCT `product_id` FROM `".WPSC_TABLE_ITEM_CATEGORY_ASSOC."` WHERE `product_id` IN ('".implode("', '", $category_list)."')"); $category_sql = "OR `".WPSC_TABLE_PRODUCT_LIST."`.`id` IN ('".implode("', '", $category_assoc_list)."')"; } |
With that completed I looked at adding a separate search interface and came across WP e-Commerce Search Widget, a handy bit of code but unfortunately it doesn’t appear to be compatible with the latest version of WP e-Commerce so I had to quickly update
<form method='GET' name='product_search' id="productsearchform" action="<?php bloginfo('home'); ?>/"> |
to
<form method='GET' name='product_search' id="productsearchform" action="<?php bloginfo('home'); ?>/products-page/"> |
WP e-Commerce is a lot of work, especially considering you have to pay $50 just to get basic functionality and then hack in the desired features. Lets hope the checkout refresh bug doesn’t hit again….
The Secret Glyph Project have been hard at work and have figured out what was causing the doors in Sector 8 to disappear (see The ODST glyphs *DO NOT* unlock a door!” and Confirmed: ODST door disappearance is a glitch) and got in working in other sectors!
They’ve wrapped it up nicely in this Youtube video which servers as both a demonstration and tutorial.
For detailed information on how to repeat it and how it works check out their discussion forum and get “bumping”!
I’ve just gone back to the in-game video of me showing a friend the disappearing door in Sector 8 (see my previous post for details) and I checked out Sector 8 before and after the door disappears. This is nothing more than a repeatable glitch.
In comparison to the fully loaded sector when the door is missing the following applies:
3 areas used as the SI supply drops are now open (not just the 1 originally discovered).
The grunts and turrets are not loaded in one of the circular areas.
Adverts for cars are missing from the walls.
Various items are not loaded such as the Opticon health dispensers and stand alone terminals (such as the one near the shotgun).
So it’s a case of good work on finding a glitch, now can we do this on other sectors to access even more areas that should be blocked off?
JSM26 has uploaded an ODST video to his Bungie.net file share showing missing Opticon dispensers and de-loaded doors in the starting sector. Further evidence that the ODST modified Halo 3 engine has glitches, possibly exploitable to access other blocked off areas the designers never wanted us to get to…
There’s been quite a bit of excitement over at Bungie.net recently, someone noticed that if you look at some of the glowing Covenant glyphs in Sector 6 resulted in two doors in Sector 8 disappearing allowing access to a previously blocked off area and an empty supply drop. The Secret Glyph Project got on the wagon and produced a video demonstrating the effect and a whole slew of sub-forums were created to track this phenomenon.
I’m sad to say that the doors disappear from what appears to be a loading glitch, not from looking at any glyphs. Here’s how I determined this, I encourage you to try this to replicate the results:
Having completed the game I loaded up New Mombasa Streets, immediately got myself a mongoose and headed to Sector 8 to check on the door. At this point I should point out that at no time after getting the mongoose did I have the visor on. The door was closed as expected.
I then drove over to Sector 6 and without turning on my visor walked up to the covenant sniper rifle next to the Elites, picked it up and got back on my mongoose (parked near to the Sector exit). I then drove immediately back to the door which was now open with no glyphs sighted.If you look at a map you’ll see that Sector 8 is actually a twisted mirror of Sector 3 and if you look closely you’ll see that Sector 3 also has a room with funny tiles on it matching the location and design of the one in Sector 8. If you go to this area you’ll immediately feel at home and easily spot the Security door as placed in a mirror fashion to the one in Sector 8. This door opens…
So, it’s my belief that this is just fall out to the way the game engine loads the levels and nothing at all to do with the glyphs, it’s just coincidence that they are located in the area you need to go to de-load the doors.
Certain glyphs lead you to keys areas so for me it’s pretty clear that the glyphs are there for player guidance and to add an air of mystery. Give it it a go, try the experiment above and let me know your results.
Halo 3 Asset-O-Matic R.I.P?
Sep 17
As expected, the redesign of Bungie.net; has stopped the Halo 3 Asset-O-Matic from working.
I’m not sure if I will be updating the app to support the new layout, I’ll have to see how inspired I am with the release of ODST…;)
New 360 dash preview
Jul 29
So, I booted up my 360 this morning so I could experience the wonders of the new dash…
Avatar props:
They’ve missed the crucial factor here, impulse purchasing. Every thing’s priced too high, prices should range from 20G to 100G, if it were I’d have probably spent 800G this morning alone, but as it is I’ve bought nothing. I’m not spending 320G on a remote control warthog that can only be seen on the dash, that’s almost half the price of a game!
Stan’s Outfit should be 1 purchase, you shouldn’t have to buy the hat separately.
I’ll be sticking to the freebies from games for a while until something that really appeals to my impulsive side turns up.
Dash speed:
Yup, it’s true, the dash feels almost as responsive as the USA one did! I was able to queue up 4 episodes of the guild in the time it would have previously taken to queue up a single episode. I’m hoping that this responsiveness continues into the guide when you’re in game, I’ve nearly thrown my controller around the room before whilst waiting over 2 minutes for the guide to load up at a crucial part of an online game
The other stuff:
It’s all fluff apart from that. I don’t need to tweak my display options, date stamps on saves are very useful but I rarely do anything with my saves as it is. The change to my gamercard is very nice but I can live without it, as can most others I expect. The achievement browser is a useful indication of how few games I have 100% whored, I wish they’d taken this further showing games with easy achievements to reach, links to guides and strategies and the like. Could have been a great cross promotional effort with IGN, as it is it’s just a nice bit of fluff.
It’s good, but it doesn’t have any of the exciting features such as Last.fm, Sky integration and the other cool features we’ve been promised at E3. Still, nice to be on the first wave I guess
