To be able to do this you must know how to walk
executables (in this case an xbe file).
The XBE header is documented quite well at caustik's
web site at the following address: 'http://www.caustik.com/xbox/download/xbe.htm'.
Kudos to caustik for his fantastic work.
The following definition of the header that is found in the xbe,
is almost an exact copy of what you can find at caustik's site:
| CODE |
// Note: This source is compatible with MSVC 6.0 or higher, and is public domain. // ****************************************************************** // * standard typedefs // ****************************************************************** typedef signed int sint; typedef unsigned int uint; typedef char int08; typedef short int16; typedef long int32; typedef unsigned char uint08; typedef unsigned short uint16; typedef unsigned long uint32; typedef signed char sint08; typedef signed short sint16; typedef signed long sint32; typedef struct _xbe_info_ { #pragma pack( push, before_header ) #pragma pack(1) struct header { uint32 magic; // magic number [should be "XBEH"] uint08 digsig[256]; // digital signature uint32 base; // base address uint32 sizeof_headers; // size of headers uint32 sizeof_image; // size of image uint32 sizeof_image_header; // size of image header uint32 timedate; // timedate stamp uint32 certificate_addr; // certificate address uint32 sections; // number of sections uint32 section_headers_addr; // section headers address struct init_flags { uint mount_utility_drive : 1; // mount utility drive flag uint format_utility_drive : 1; // format utility drive flag uint limit_64mb : 1; // limit development kit run time memory to 64mb flag uint dont_setup_harddisk : 1; // don't setup hard disk flag uint unused : 4; // unused (or unknown) uint unused_b1 : 8; // unused (or unknown) uint unused_b2 : 8; // unused (or unknown) uint unused_b3 : 8; // unused (or unknown) } init_flags; uint32 entry; // entry point address uint32 tls_addr; // thread local storage directory address uint32 pe_stack_commit; // size of stack commit uint32 pe_heap_reserve; // size of heap reserve uint32 pe_heap_commit; // size of heap commit uint32 pe_base_addr; // original base address uint32 pe_sizeof_image; // size of original image uint32 pe_checksum; // original checksum uint32 pe_timedate; // original timedate stamp uint32 debug_pathname_addr; // debug pathname address uint32 debug_filename_addr; // debug filename address uint32 debug_unicode_filename_addr; // debug unicode filename address uint32 kernel_image_thunk_addr; // kernel image thunk address uint32 nonkernel_import_dir_addr; // non kernel import directory address uint32 library_versions; // number of library versions uint32 library_versions_addr; // library versions address uint32 kernel_library_version_addr; // kernel library version address uint32 xapi_library_version_addr; // xapi library version address uint32 logo_bitmap_addr; // logo bitmap address uint32 logo_bitmap_size; // logo bitmap size } Header; struct certificate { uint32 size; // size of certificate uint32 timedate; // timedate stamp uint32 titleid; // title id uint16 title_name[40]; // title name (unicode) uint32 alt_title_id[0x10]; // alternate title ids uint32 allowed_media; // allowed media types uint32 game_region; // game region uint32 game_ratings; // game ratings uint32 disk_number; // disk number uint32 version; // version uint08 lan_key[16]; // lan key uint08 sig_key[16]; // signature key uint08 title_alt_sig_key[16][16]; // alternate signature keys } Certificate; struct section_header { struct flags // flags { uint writable : 1; // writable flag uint preload : 1; // preload flag uint executable : 1; // executable flag uint inserted_file : 1; // inserted file flag uint head_page_ro : 1; // head page read only flag uint tail_page_ro : 1; // tail page read only flag uint unused_a1 : 1; // unused (or unknown) uint unused_a2 : 1; // unused (or unknown) uint unused_b1 : 8; // unused (or unknown) uint unused_b2 : 8; // unused (or unknown) uint unused_b3 : 8; // unused (or unknown) } Flags; uint32 virtual_addr; // virtual address uint32 virtual_size; // virtual size uint32 raw_addr; // file offset to raw data uint32 sizeof_raw; // size of raw data uint32 section_name_addr; // section name addr uint32 section_reference_count; // section reference count uint16 *head_shared_ref_count_addr; // head shared page reference count address uint16 *tail_shared_ref_count_addr; // tail shared page reference count address uint08 section_digest[20]; // section digest } * pSection_Header; struct library_version { char name[8]; // library name uint16 major_version; // major version uint16 minor_version; // minor version uint16 build_version; // build version struct flags // flags { uint16 qfe_version : 13; // QFE Version uint16 approved : 2; // Approved? (0:no, 1:possibly, 2:yes) uint16 debug_build : 1; // Is this a debug build? } Flags; } * Library_Version, * Kernel_Version, * XAPI_Version; struct tls // thread local storage { uint32 data_start_addr; // raw start address uint32 data_end_addr; // raw end address uint32 tls_index_addr; // tls index address uint32 tls_callback_addr; // tls callback address uint32 sizeof_zero_fill; // size of zero fill uint32 characteristics; // characteristics } * TLS; #pragma pack( pop, before_header ) } XBE_INFO, * PXBE_INFO, ** PPXBE_INFO; |
You need to read this header from the beginning of the xbe file to so that you
know where to start looking for stuff since this header contains all the necessary
information for where to start looking for stuff.
The following example shows how to e.g. get information about the certificate of the xbe:
| CODE |
XBE_INFO m_XBEInfo; int m_iHeaderSize; char * m_pHeader; . . . // Assume 256K is enough for header information m_iHeaderSize = ( 256 * 1024 ); m_pHeader = ( char * )NULL; . . . // Do we have a header memory area? if ( m_pHeader == ( char * )NULL ) { // No header area. Allocate memory for header if ( ( m_pHeader = ( char * )malloc( m_iHeaderSize ) ) == ( char * )NULL ) { // Unable to allocate memory. Return not OK return ( int )RET_ERR; } } . . . HANDLE hFile; // Open the local file hFile = CreateFile( szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); // Did we manage to open the file? if ( hFile == ( HANDLE )INVALID_HANDLE_VALUE ) { // Unable to open file. Return not OK return ( int )RET_ERR; } // Read the header if ( ::ReadFile( hFile, m_pHeader, min( m_iHeaderSize, ( int )GetFileSize( hFile, ( LPDWORD )NULL ) ), &dwRead, NULL ) == ( BOOL )TRUE ) { // Header read. Copy information about header memcpy( &m_XBEInfo.Header, m_pHeader, sizeof( m_XBEInfo.Header ) ); } // Header read. Copy information about certificate memcpy( &m_XBEInfo.Certificate, ( m_pHeader + m_XBEInfo.Header.sizeof_image_header ), sizeof( m_XBEInfo.Certificate ) ); // Title id. (remember to convert it to a hex value before using it as a part of any file names) // m_XBEInfo.Certificate.titleid; // Media flag // m_XBEInfo.Certificate.allowed_media; // Game region code // m_XBEInfo.Certificate.game_region; |
The next section shows how to identify certain sections of the xbe file,
since the images that we're looking are placed in sections with certain names.
| CODE |
// XPR stuff #ifndef XBR_HEADER typedef struct _XPR_File_Header_ { union _ImageType_ { DWORD dwXPRMagic; WORD wBitmapMagic; } Type; DWORD dwTotalSize; DWORD dwHeaderSize; DWORD dwTextureCommon; DWORD dwTextureData; DWORD dwTextureLock; BYTE btTextureMisc1; BYTE btTextureFormat; BYTE btTextureLevel : 4; BYTE btTextureWidth : 4; BYTE btTextureHeight : 4; BYTE btTextureMisc2 : 4; DWORD dwTextureSize; DWORD dwEndOfHeader; // 0xFFFFFFFF } XPR_FILE_HEADER, * PXPR_FILE_HEADER, ** PPXPR_FILE_HEADER; #endif int m_iImageSize; char * m_pImage; int iNumSections; char * pszSectionName; // Assume 256K is enough for image information m_iImageSize = ( 256 * 1024 ); m_pImage = ( char * )NULL; . . . // Initialize iNumSections = m_XBEInfo.Header.sections; // Position at the first section m_XBEInfo.pSection_Header = ( _xbe_info_::section_header * )( m_pHeader + ( m_XBEInfo.Header.section_headers_addr - m_XBEInfo.Header.base ) ); // Sections read. Parse all the section headers while ( iNumSections-- > ( int )0 ) { // Is this section an inserted file? if ( ( int )m_XBEInfo.pSection_Header->Flags.inserted_file == ( int )1 ) { // Inserted file. Position at the name of the section pszSectionName = ( m_pHeader + ( m_XBEInfo.pSection_Header->section_name_addr - m_XBEInfo.Header.base ) ); // Title image? if ( strcmp( pszSectionName, "$$XTIMAGE" ) == ( int )0 ) { // Do we have an image memory area? if ( m_pImage == ( char * )NULL ) { // No image area. Allocate memory for image if ( ( m_pImage = ( char * )malloc( m_iImageSize ) ) == ( char * )NULL ) { // Unable to allocate memory. Break out of loop break; } } // Position at 'title image' in the XBE itself if ( SetFilePointer( hFile, m_XBEInfo.pSection_Header->raw_addr, NULL, FILE_BEGIN ) == ( DWORD )0xFFFFFFFF ) { // Unable to set file position. Break out of loop break; } // Read the image if ( ::ReadFile( hFile, m_pImage, min( m_iImageSize, ( int )m_XBEInfo.pSection_Header->sizeof_raw ), &dwRead, NULL ) == ( BOOL )FALSE ) { // Image not read. Break out of loop break; } // The m_pImage variable now contains the image data itself // Most of the times this is an image described as an XPR0 image // TODO - Insert NinjaImageCodeHere( ) // Break out of loop break; } } // Position at the next section m_XBEInfo.pSection_Header++; } } |
As you can see the section to look for when you want to find the
icon that is being used as the application icon, you'll have to look for a section
named $$XTIMAGE. If you want to look for the save game image (not always present),
then you need to look for the section named $$XSIMAGE.
The above example shows you how to find the section containing the title image, the
actual image extraction is left out so that you can do this exercise for your self.
Tip: the image is an XPR0 described image - but not always though.
Happy hunting