www.xbdev.net xbdev - software development
Thursday February 23, 2017
home | about | contact | Donations

     
 
XBE - XBOX Executable Structure     
 

XBE File Format...

by bkenwright@xbdev.net

 

So you want to know how it ticks, whats inside the xbe file....how to create your own XBOX executable do you?  Well it can't be more simple...well as long as you stick with me, I'm sure you'll pick it up really easy.

 

 

Following on...feedback is always appreciated.....also donations to xbdev are always welcome - as it at least pays for the web server :)

 

The best way I think of doing this, is to get yourself an xbe, you can use any xbe you find.  There is a tool that will rip an xbe and contents of a game cd you have, then you can get the xbe from it.  Or compile an xbe using the OpenXDK?...its entirely upto you.

 

Where going to explore the xbe file format using C code...which means we'll read in the file using fopen(..), fread(..) etc, and examine the data that we read in...byte by byte.

 

 

 

 

As the first few hundred bytes will only define the details of the xbe, things like offsets to the code, the entry point to the program...when it was compiled etc.  We'll expand on this simple diagram later....just remember that all our xbe is, is code and data....its got a nice wrapper around it, which is the header...and may be broken up into sections...which we'll dig out of this xbe.

 

Some interesting things we do discover from the xbe as well, is the internal kernel bios api...and how they are used in the code.

 

code:  main.cpp

//Program Entry Point

void main(int argc, char* argv[])

{

 

      }

 

This is our starting point...I think its better to use, 'argv and argc' as we can later on pass the xbe filename in to our program, and possibly some additional information if we need to.

 

What where going to do, is write our output to a text file...we'll call it output.txt for now...we could change this later on, and pass an output filename.  So lets add our output code:

 

 

code: main.cpp

#include <stdio.h>                     // Need this for fopen(..) fwrite(..) etc

 

// We use this simple function to write data to our file.

void fileout(char *str)

{

      FILE *fp;

      fp=fopen("output.txt", "a+");    // a+ -> if file doesn't exist create it, else

      fprintf(fp, "%s", str);          //       append to the end of the file

      fclose(fp);

}// End fileout(..)

 

//Program Entry Point

void main(int argc, char* argv[])

{

      fileout("start of our xbe exploration");

      }// End main(..)

 

 

There you go!  Run that little program up, and we can output information using 'fileout(..)' whenever we want :)  So now if you run that code you should get a txt file called 'output.txt' in the same directory as your exe that you compiled.  Open it up in notepad, and we find!..."start of our xbe exploration".

 

I was originally just going to allocate a big area of memory....a couple of megs and just read in the whole xbe into it.  But a better idea is to write a small function to see how big our xbe is...it really makes our code much more flexible, and its not to complex.

 

code: main.cpp

#include <stdio.h>                     // Need this for fopen(..) fwrite(..) etc

 

// We use this simple function to write data to our file.

void fileout(char *str)

{

      FILE *fp;

      fp=fopen("output.txt", "a+");   // a+ -> if file doesn't exist create it, else

      fprintf(fp, "%s", str);         //       append to the end of the file

      fclose(fp);

}// End fileout(..)

 

long FileSize( char * szFileName )

{

      FILE *file = fopen(szFileName, "rb"); // Open the file

      fseek(file, 0, SEEK_END);             // Seek to the end

      long file_size = ftell(file);         // Get the current position

      fclose(file);

     

      return file_size;                     // We have the file size, so return it

}// End FileSize(..)

 

//Program Entry Point

void main(int argc, char* argv[])

{

      char szFileName[] = "default.xbe";    // Our input xbe

      char buf[500];                        // Large temp char buffer

 

      long iFileSize = FileSize(szFileName);

 

      sprintf(buf, "xbe filesize: %d bytes", iFileSize);

      fileout(buf);

}// End main(..)

 

Notice I've hard coded the filename of the xbe...its usually called 'default.xbe' anyhow...and you need to keep it in the directory with the exe.

 

xbe filesize: 1633220 bytes

 

Thats the output I get if I run the code on an xbe I had laying around on my xbox :).....its not much yet...but our journey has to start at the beginning.  If you right click on the xbe in windows, and look at the details...which are the actual file size in bytes...you'll see its the same as what is written to your txt file.  Where going slowly, so you can take it all in bit by bit.  Now go and get yourself a coffee, like I'm going to...before we start onto the next part...reading some bytes from the xbe.

 

Lets allocate memory for our whole xbe, and read it in...the whole thing...and we can write code to pick on it.

 

code: main.cpp

#include <stdio.h>                     // Need this for fopen(..) fwrite(..) etc

 

// We use this simple function to write data to our file.

void fileout(char *str)

{

...

}// End fileout(..)

 

long FileSize( char * szFileName )

{

...

}// End FileSize(..)

 

 

//Program Entry Point

void main(int argc, char* argv[])

{

      char szFileName[] = "default.xbe";    // Our input xbe

      char buf[500];                        // Large temp char buffer

 

      long iFileSize = FileSize(szFileName);

 

      sprintf(buf, "File: %s - xbe filesize: %d bytes\n", szFileName, iFileSize);

      fileout(buf);

 

      // Lets allocate enough memory for the whole xbe and read it all in

      unsigned char * pXBE = new unsigned char[iFileSize];

 

      // Open our xbe file

      FILE* fp = fopen( szFileName, "r" );

      fseek(fp, 0, SEEK_SET);

 

      // Read all the contents into our allocated memory

      /*

      do

      {

            fread(pXBE, 1, 1, fp);

      } while (!feof(fp));

      */

     

      // For some reason I had a problem with the above do/while loop..as it

      // didn't seem to read in all the data?  ..hmmm or was aligned wrong

      // so for a quick fix, I chose this instead :)

      fread(pXBE, iFileSize, 1, fp);

 

      // Close our file.

      fclose( fp );

 

 

      // Write out the first 3 char's of the xbe to see what they are

      sprintf(buf, "pXBE[0..3] = %c%c%c%c", pXBE[0], pXBE[1], pXBE[2], pXBE[3] );

      fileout(buf);

 

      // Remember, before exiting the program, release the memory we allcoated for

      // the xbe data we read in

      delete[] pXBE;

 

}// End main(..)

 

 

And of course the file output is:

 

XBEFile: default.xbe - xbe filesize: 1633220 bytes
pXBE[0..3] = XBEH

 

Now the first 4 bytes of the xbe are known as the signature - they are always 'XBEH'....you can use this to test if you have a valid xbe on your hands if you add in debugging code.

 

Next 0x100 or 256 bytes are an authentication signature, which is used by MS to make sure that the game was authorised by them.  This is one of the main reasons you need a mod chip to run compiled xbe's - as when the xbe boots up, it checks this signature to make sure its a valid xbe, if so it does some other checks and runs.  Of course we don't need to worry about this, so we will can just think of this as all 0's and it will still work :)

 

code E : main.cpp

 

 

#include <stdio.h>                     // Need this for fopen(..) fwrite(..) etc

 

// We use this simple function to write data to our file.

void fileout(char *str)

{

...

}// End fileout(..)

 

long FileSize( char * szFileName )

{

...

}// End FileSize(..)

 

 

//Program Entry Point

void main(int argc, char* argv[])

{

      char szFileName[] = "default.xbe";    // Our input xbe

      char buf[500];                        // Large temp char buffer

 

      long iFileSize = FileSize(szFileName);

 

      sprintf(buf, "File: %s - xbe filesize: %d bytes\n\n", szFileName, iFileSize);

      fileout(buf);

 

      // Lets allocate enough memory for the whole xbe and read it all in

      unsigned char * pXBE = new unsigned char[iFileSize];

 

      // Open our xbe file

      FILE* fp = fopen( szFileName, "r" );

      fseek(fp, 0, SEEK_SET);

 

      // Read all the contents into our allocated memory

      fread(pXBE, iFileSize, 1, fp);

 

      // Close our file.

      fclose( fp );

 

      int iOffset = 0;

      //------------------------Our XBE Analysis Code-------------------------//

 

      // Write out the first 3 char's of the xbe to see what they are

      sprintf(buf, "Sig (0x4) pXBE[0..3] = %c%c%c%c\n",

                pXBE[iOffset+0], pXBE[iOffset+1], pXBE[iOffset+2], pXBE[iOffset+3] );

      fileout(buf);

      iOffset += 4; // Move along 4 bytes.

 

      // Skip the 0x100 bytes that are the authentication signature

      fileout("Authentication Signature (0x100)\n");

      iOffset +=0x100;

 

      // Lets take a looksy at what the next piece of juicy info is...the base

      // address

      // We need this next line, as if we just use pXBE[iOffset] it will give us

      // a byte...big endian...so our 0x0...if we have 0x00100000...so if we

      // cast the pointer to an unsigned int, we get a 4 bytes...which is what we want

      unsigned int * ptr = (unsigned int*)&pXBE[iOffset];

      sprintf(buf, "Base Address (0x4) :  0x%08X\n", *ptr);

      fileout(buf);

      iOffset += 0x4;

 

      // Goodbye

 

      // Remember, before exiting the program, release the memory we allcoated for

      // the xbe data we read in

      delete[] pXBE;

 

}// End main(..)

 

 

And our output is as follows:

 

File: default.xbe - xbe filesize: 1633220 bytes

Sig (0x4) pXBE[0..3] = XBEH
Authentication Signature (0x100)
Base Address (0x4) : 0x00010000

 

The base address is the memory location our xbe will be placed at when the xbe runs.  Well thats what the xbe wants, it puts this information here to request it...it usually is given what it wants to keep it happy.

Now just remember that this offset base address is used as a reference later on, so when it says offset 100 bytes for some code..it means from this base address.  I'll point this out to you later on so you don't forget.

 

So skipping ahead, I've put a function together which will write the values for all the parts of the xbe main header to the file for us, so we can take a looksy at them...here is the code and output text file data:

 

code F: main.cpp

 

 

#include <stdio.h>                     // Need this for fopen(..) fwrite(..) etc

 

// We use this simple function to write data to our file.

void fileout(char *str)

{

    ...

}// End fileout(..)

 

long FileSize( char * szFileName )

    ...

}// End FileSize(..)

 

 

void ProcessXBE( unsigned char *pXBE, long iSizeXBE )

{

      char buf[500];                        // Large temp char buffer

      int iOffset = 0;

      unsigned int * ptr = (unsigned int*)pXBE;

 

      // Write out the first 3 char's of the xbe to see what they are

      sprintf(buf, "Sig (0x4) pXBE[0..3] = %c%c%c%c\n",

                pXBE[0], pXBE[1], pXBE[2], pXBE[3] );

      fileout(buf);

 

      // Skip the 0x100 bytes that are the authentication signature

      fileout("Authentication Signature (0x100) (skipped)\n");

       //Remember 256 bytes is 64 dwords, and the 1 dword which is the XBEH at the start

      ptr +=0x41;

 

      sprintf(buf, "Base Address                        (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Size of Headers                     (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "Size of Image                       (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Size of Image Header                (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "Time&Date Stamp                     (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Certificate Address                 (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Number of Sections                  (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "Section Headers Address             (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Initialization Flags                (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Entry Point                         (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "TLS Address                         (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "PE Stack Commit                     (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "PE Heap Reserve                     (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "PE Heap Commit                      (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "PE Base Address                     (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "PE Size of Image                    (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "PE Checksum                         (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "PE TimeDate                         (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "Debug PathName Address              (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Debug FileName Address              (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Debug Unicode FileName Address      (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Kernel Image Thunk Address          (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "Non-Kernel Import Directory Address (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "Number of Library Versions          (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "Library Versions Address            (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "Kernel Library Version Address      (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

      sprintf(buf, "XAPI Library Version Address        (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Logo Bitmap Address                 (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

 

      sprintf(buf, "Logo Bitmap Size                    (0x4) :  0x%08X\n", *ptr++);

      fileout(buf);

     

 

}// End ProcessXBE(..)

 

//Program Entry Point

void main(int argc, char* argv[])

{

      char szFileName[] = "default.xbe";    // Our input xbe

      char buf[500];                        // Large temp char buffer

 

      long iFileSize = FileSize(szFileName);

 

      sprintf(buf, "File: %s - xbe filesize: %d bytes\n\n", szFileName, iFileSize);

      fileout(buf);

 

      // Lets allocate enough memory for the whole xbe and read it all in

      unsigned char * pXBE = new unsigned char[iFileSize];

 

      // Open our xbe file

      FILE* fp = fopen( szFileName, "r" );

      fseek(fp, 0, SEEK_SET);

 

      // Read all the contents into our allocated memory

      fread(pXBE, iFileSize, 1, fp);

 

      // Close our file.

      fclose( fp );

 

      //------------------------Our XBE Analysis Code-------------------------//

 

      ProcessXBE( pXBE, iFileSize );

     

      //------------------------End of XBE Analysis---------------------------//

 

      // Goodbye

 

      // Remember, before exiting the program, release the memory we allcoated for

      // the xbe data we read in

      delete[] pXBE;

 

}// End main(..)

 

 

And here is our output data from the txt file:

 

File: default.xbe - xbe filesize: 1633220 bytes

Sig (0x4) pXBE[0..3] = XBEH
Authentication Signature (0x100) (skipped)
Base Address (0x4) : 0x00010000
Size of Headers (0x4) : 0x00002000
Size of Image (0x4) : 0x0019E240
Size of Image Header (0x4) : 0x00000178
Time&Date Stamp (0x4) : 0x3D5CE207
Certificate Address (0x4) : 0x00010178
Number of Sections (0x4) : 0x00000018
Section Headers Address (0x4) : 0x00010364

Initialization Flags (0x4) : 0x0000000C
Entry Point (0x4) : 0xA8F93C11
TLS Address (0x4) : 0x0001D8EC
PE Stack Commit (0x4) : 0x00010000
PE Heap Reserve (0x4) : 0x00100000
PE Heap Commit (0x4) : 0x00001000
PE Base Address (0x4) : 0x00011B40
PE Size of Image (0x4) : 0x0015A960
PE Checksum (0x4) : 0x0016A024
PE TimeDate (0x4) : 0x3D5CE202
Debug PathName Address (0x4) : 0x00010A7C
Debug FileName Address (0x4) : 0x00010AA0
Debug Unicode FileName Address (0x4) : 0x00010A58
Kernel Image Thunk Address (0x4) : 0x5B6C60B6
Non-Kernel Import Directory Address (0x4) : 0x00000000
Number of Library Versions (0x4) : 0x0000000B
Library Versions Address (0x4) : 0x000109A8
Kernel Library Version Address (0x4) : 0x000109E8
XAPI Library Version Address (0x4) : 0x000109A8
Logo Bitmap Address (0x4) : 0x00010AB4
Logo Bitmap Size (0x4) : 0x000002B2

Now I've made the most important parts bold so you can see.  I'll explain why I've made them bold here - I made the XBEH bold so we know we have an xbox executable file...I just like seeing the xbe..hehe....makes me know I'm getting it right.  Next the base address, which is 0x10000....we use this a lot through out our other couple of headers and so we try to keep it at the back of our mind.

Then we have Sections!  Take a deep breath, there usually isn't 0x18 sections..hehe...just must be this xbe I'm working with...I would usually expect 3-4 sections...this is a big xbe.  The sections are where our code and data is held.  You could take each code section...write it to a raw binary file...and use a disassembler to look at the actual code.  We'll do more on that later though.

 

Entry point - this should have a name which is self explanatory.  Its the starting point of our code!  Yup...its all processed loaded into memory and your xbox is ready to start, so it jumps to this point in memory, which hopefully is where one of your sections will have been loaded.

 

Then finally we have the Thunk's.  What on earth is a thunk :-/  Well when I first started reading about xbe and exe formats....these thinks really stumbled me... they don't tell you much.  But all it is, is an array of pointers.  Each pointer points to a kernel bios function.  As when your xbe is loaded, these values are filled in...and when our program wants to do things like read from the disk or get the tick count of the cpu, which are the responsiblity of the cpu...then the function is mapped to there.  As there are 256 functions which are put there.  An example of some of the bios function calls are as follows:

 

...

MmAllocateContiguousMemory  (165)

MmAllocateContiguousMemoryEx (166)

MmAllocateSystemMemory (167)

MmClaimGpuInstanceMemory (168)

MmCreateKernelStack (169)

      ...

 

Where the number to the right, is the offset value, e.g. 165.  This value is usually added to (0x80000000) then inserted into the array....on loading, its value is changed for the actual memory offset to the bios function.

 

 

Now to keep things tidy...and to allow us to build our code larger, I thought I'd make it a little more structured.  So I created a structure for the Image Header...and called it stImageHeader....I can then load it in one go, and reference any part of it when I need.  Here is the slightly changed code.

 

 

code G: main.cpp

 

#include <stdio.h>                     // Need this for fopen(..) fwrite(..) etc

#include <string.h>                    // memcpy(..)

 

// We use this simple function to write data to our file.

void fileout(char *str)

{

      ...

}// End fileout(..)

 

long FileSize( char * szFileName )

{

      ...

}// End FileSize(..)

 

 

// Remember that we use 'pragma pack(1)' so our data in the stucture is packed nice

// and tight using a 1 byte alignment.

#pragma pack(1)

struct stImageHeader

{

      char          sig[4];

      unsigned char auth_sig[0x100];

      unsigned int  base_address;

      unsigned int  size_header;

      unsigned int  size_image;

      unsigned int  size_image_header;

      unsigned int  time_date;

      unsigned int  cert_addr;

      unsigned int  num_sections;

      unsigned int  sect_addr;

      unsigned int  init_flags;

#define MountUtilityDrive    0x00000001

#define FormatUtilityDrive   0x00000002

#define Limit64Megabytes     0x00000004

#define DontSetupHarddisk    0x00000008

      unsigned int  entry_point;

#define XOR_ENTRY_DEBUG      0x94859D4B

#define XOR_ENTRY_RETAIL     0xA8FC57AB

      unsigned int  tls_addr;

      unsigned int  pls_stack_commit;

      unsigned int  pe_heap_reserv;

      unsigned int  pe_heap_commit;

      unsigned int  pe_base_addr;

      unsigned int  pe_size_image;

      unsigned int  pe_checksum;

      unsigned int  pe_timedata;

      unsigned int  pathname_addr;

      unsigned int  filename_addr;

      unsigned int  unicode_filename_addr;

      unsigned int  kernel_thunk_addr;

#define XOR_KERNEL_DEBUG     0xEFB1F152

#define XOR_KERNEL_RETAIL    0x5B6D40B6

      unsigned int  non_kernel_dir_addr;

      unsigned int  num_lib_versions;

      unsigned int  lib_vers_addr;

      unsigned int  kernel_lib_vers_addr;

      unsigned int  xapi_lib_vers_addr;

      unsigned int  logo_bitmap_addr;

      unsigned int  logo_bitmap_size;

};

#pragma pack()

 

 

 

void ProcessXBE( unsigned char *pXBE, long iSizeXBE )

{

 

      stImageHeader IH;

      memcpy( &IH, pXBE, sizeof(stImageHeader) );

 

      // Large temp char buffer

      char buf[500];          

 

      int iOffset = 0;

      unsigned int * ptr = (unsigned int*)pXBE;

 

      // Write out the first 3 char's of the xbe to see what they are

      sprintf(buf, "Sig (0x4) (XBEH) = %c%c%c%c\n",

                IH.sig[0], IH.sig[1], IH.sig[2], IH.sig[3] );

      fileout(buf);

 

      sprintf(buf, "Base Address: 0x%08X\n", IH.base_address );

      fileout(buf);

 

      sprintf(buf, "Number Sections: 0x%08X\n", IH.num_sections );

      fileout(buf);

 

      sprintf(buf, "Section Address: 0x%08X\n", IH.sect_addr );

      fileout(buf);

 

      sprintf(buf, "Entry Point: 0x%08X (Decodes to: 0x%08X)\n",

                               IH.entry_point, 

                                       IH.entry_point^XOR_ENTRY_RETAIL);

      fileout(buf);

 

      sprintf(buf, "Kernel Thunk Address: 0x%08X (Decodes to: 0x%08X)\n",

                              IH.kernel_thunk_addr,

                                      IH.kernel_thunk_addr^XOR_KERNEL_RETAIL );

      fileout(buf);

 

}// End ProcessXBE(..)

 

//Program Entry Point

void main(int argc, char* argv[])

{

      ...

}// End main(..)

 

 

And our wonderful output again looks like this:

File: default.xbe - xbe filesize: 1633220 bytes

Sig (0x4) (XBEH) = XBEH
Base Address: 0x00010000
Number Sections: 0x00000018
Section Address: 0x00010364
Entry Point: 0xA8F93C11 (Decodes to: 0x00056BBA)
Kernel Thunk Address: 0x5B6C60B6 (Decodes to: 0x00012000)

Notice I've decoded the program entry point..  As ms didn't want it to be so easy for us to create xbe's...they put loads of little special things in there...this is one of them.  As you have to XOR the given entry point with a Debug or Retail value....I've included them in the structure as #define's.  Most of the time we'll be dealing with release xbe's....you could probably tell you've got a debug xbe if the decoded value is a way off value...like 0xf030d0d0 etc.

 

Let me go and get some coffee and rest at this point....its heavy stuff all this code and hex....

 

 

On we go......more code....and more coffee....

 

Well we have 4 more headers that have data for us....these headers are shown to us from the stImageHeader we just read in, like the Section details where our code is located....  So I decided to put together some structure definitions for the other 4 header types....stSection, stCertificate, stLibrary and finally stTLS.  You shouldn't be worried, as the biggest one was the one we've just done...these are baby's compared to that one :)

 

Here is what the structures look like...now I must admit they look scary!  They even look messy :-/  But 90% of the stuff in them is just useless...we can throw it away or just pad it with 0's and our xbe would still work.

 

 

code H: main.cpp
 

//-----------------------------stImageHeader Structure------------------------------//

// Remember that we use 'pragma pack(1)' so our data in the stucture is packed nice

// and tight using a 1 byte alignment.

#pragma pack(1)

struct stImageHeader

{

      char          sig[4];

      unsigned char auth_sig[0x100];

      unsigned int  base_address;

      unsigned int  size_header;

      unsigned int  size_image;

      unsigned int  size_image_header;

      unsigned int  time_date;

      unsigned int  cert_addr;

      unsigned int  num_sections;

      unsigned int  sect_addr;

      unsigned int  init_flags;

#define MountUtilityDrive                        0x00000001

#define FormatUtilityDrive                       0x00000002

#define Limit64Megabytes                         0x00000004

#define DontSetupHarddisk                        0x00000008

      unsigned int  entry_point;

#define XOR_ENTRY_DEBUG                          0x94859D4B

#define XOR_ENTRY_RETAIL                         0xA8FC57AB

      unsigned int  tls_addr;

      unsigned int  pls_stack_commit;

      unsigned int  pe_heap_reserv;

      unsigned int  pe_heap_commit;

      unsigned int  pe_base_addr;

      unsigned int  pe_size_image;

      unsigned int  pe_checksum;

      unsigned int  pe_timedata;

      unsigned int  pathname_addr;

      unsigned int  filename_addr;

      unsigned int  unicode_filename_addr;

      unsigned int  kernel_thunk_addr;

#define XOR_KERNEL_DEBUG                         0xEFB1F152

#define XOR_KERNEL_RETAIL                        0x5B6D40B6

      unsigned int  non_kernel_dir_addr;

      unsigned int  num_lib_versions;

      unsigned int  lib_vers_addr;

      unsigned int  kernel_lib_vers_addr;

      unsigned int  xapi_lib_vers_addr;

      unsigned int  logo_bitmap_addr;

      unsigned int  logo_bitmap_size;

};

#pragma pack()

 

//-----------------------------stCertificate Structure------------------------------//

#pragma pack(1)

struct stCertificate

{

      unsigned int  sizeCertStruc;

      unsigned int  timedate;

      unsigned int  titleID;

      wchar_t       title[0x28];         // or 0x50 bytes - unicode title

      unsigned int  alt_titleIDs[0x16];  // 0x40 bytes

      unsigned int  media_flags;

#define XBEIMAGE_MEDIA_TYPE_HARD_DISK           0x00000001

#define XBEIMAGE_MEDIA_TYPE_DVD_X2              0x00000002

#define XBEIMAGE_MEDIA_TYPE_DVD_CD              0x00000004

#define XBEIMAGE_MEDIA_TYPE_CD                  0x00000008

#define XBEIMAGE_MEDIA_TYPE_DVD_5_RO            0x00000010

#define XBEIMAGE_MEDIA_TYPE_DVD_9_RO            0x00000020

#define XBEIMAGE_MEDIA_TYPE_DVD_5_RW            0x00000040

#define XBEIMAGE_MEDIA_TYPE_DVD_9_RW            0x00000080

#define XBEIMAGE_MEDIA_TYPE_DONGLE              0x00000100

#define XBEIMAGE_MEDIA_TYPE_MEDIA_BOARD         0x00000200

#define XBEIMAGE_MEDIA_TYPE_NONSECURE_HARD_DISK 0x40000000

#define XBEIMAGE_MEDIA_TYPE_NONSECURE_MODE      0x80000000

#define XBEIMAGE_MEDIA_TYPE_MEDIA_MASK          0x00FFFFFF

      unsigned int  game_region;

#define XBEIMAGE_GAME_REGION_NA                 0x00000001

#define XBEIMAGE_GAME_REGION_JAPAN              0x00000002

#define XBEIMAGE_GAME_REGION_RESTOFWORLD        0x00000004

#define XBEIMAGE_GAME_REGION_MANUFACTURING      0x80000000

      unsigned int  game_ratings;

      unsigned int  disk_number;

      unsigned int  cert_version;

      unsigned char lan_key[0x10];

      unsigned char signature_key[0x10];

      unsigned char alt_signature_key[0x10 * 0x10];

};

#pragma pack()

 

//-------------------------------stSection Structure--------------------------------//

#pragma pack(1)

struct stSection

{

      unsigned int  flags;

#define SECTION_WRITABLE                         0x00000001

#define SECTION_PRELOAD                          0x00000002

#define SECTION_EXECUTABLE                       0x00000004

#define SECTION_INSERTED_FILE                    0x00000008

#define SECTION_HEAD_PAGE_READ_ONLY              0x00000010

#define SECTION_TAIL_PAGE_READ_ONLY              0x00000020

      unsigned int  virt_addr;

      unsigned int  virt_size;

      unsigned int  raw_addr;

      unsigned int  raw_size;

      unsigned int  section_name_addr;

      unsigned int  section_name_ref_count;

      unsigned int  head_shared_page_ref_count_addr;

      unsigned int  tail_shared_page_ref_count_addr;

      unsigned char section_digest[0x14];

};

#pragma pack()

 

 

//-------------------------------stLibrary Structure--------------------------------//

#pragma pack(1)

struct stLibrary

{

      char            library_name[8];

      unsigned short  majour_version;

      unsigned short  minor_version;

      unsigned short  build_version;

      unsigned int    flags;

#define LIB_QFEVERSION                            0x1FFF  // 13-Bit Mask

#define LIB_APPROVED                              0x6000  // 02-Bit Mask

#define LIB_DEBUG_BUILD                           0x8000  // 01-Bit Mask

};

#pragma pack()

 

//---------------------------------stTLS Structure----------------------------------//

#pragma pack(1)

struct stTLS

{

      unsigned int  data_start_addr;

      unsigned int  data_end_addr;

      unsigned int  tls_index_addr;

      unsigned int  tls_callback_addr;

      unsigned int  size_zero_fill;

      unsigned int  characteristics;

};

#pragma pack()

 

 

I've put the relevant '#defines' inside the structures...so you know what each one is used with... you could of course put them at the start of the structure or at the top of the file....different people do it different ways depending on there coding style.

 

*Drinks some of his coffee*  ....that was refreshing... shall we put all the data from the whole xbe we read in, into its correct places.  Put the data into the structues?  Yup, we'll have a go...and display some of the useful information as well.

 

code I : main.cpp

 

#include <stdio.h>                     // Need this for fopen(..) fwrite(..) etc

#include <string.h>                    // memcpy(..)

 

// We use this simple function to write data to our file.

void fileout(char *str)

{

      ...

}// End fileout(..)

 

long FileSize( char * szFileName )

{

      ...

}// End FileSize(..)

 

 

struct stImageHeader

...

 

 

struct stCertificate

...

struct stSection

...

struct stLibrary

...

struct stTLS

...

 

 

//-------------------------------ProcessXBE Function--------------------------------//

 

void ProcessXBE( unsigned char *pXBE, long iSizeXBE )

{

 

      // Lets fill in our stImageHead Structure

      stImageHeader IH;

      memcpy( &IH, pXBE, sizeof(stImageHeader) );

 

 

      // Well if we look at what value we have in our stImageHeader for

      // Certificate Address (0x4) : 0x00010178... we can see the value is

      // a bit high...this is because its using that base address value.

      // which is 0x10000.

      // So if we take that 0x10000, we are left with something like 0x178

      // which seems pretty reasonable, as the stImageHeader is 0x178 bytes in

      // size...which leads us to beleive its located straight after the szImageHeader

      // structure.

      stCertificate Cert;

      int offset = IH.cert_addr - IH.base_address;

      memcpy( &Cert, pXBE + offset, sizeof(stCertificate) );

 

      // Our Sections..this is where the meat and potatoes of the code is!  Yup

      // this is where all our code and data is located.

      // Now I just allocate space for 50 sections...didn't want to start allocating memory

      // etc....but if its got more than 50 sections its one mutant super xbe we have here!

      stSection pSections[50];

      int iNumSections = IH.num_sections;

      offset = IH.sect_addr - IH.base_address;

 

      // Now the address offset points to the top of the sections...as the sections are

      // stored as an array...one after the other

      for(int i=0; i<iNumSections; i++)

      {

            memcpy( &pSections[i], pXBE + offset, sizeof(stSection) );

            offset += sizeof(stSection);

      }//End for loop

 

      // Where nearly there now...not much more to go!  We'll get through this

      // TLS Data

      stTLS tls;

      offset = IH.tls_addr - IH.base_address;

      memcpy( &tls, pXBE + offset, sizeof(stTLS) );

 

 

      // Lets get all the library versions finally.

      // Now I've just put a static array together for now...I can't see an xbe having more

      // than 50 librarys...

      stLibrary xbe_standard_libs[50];

      int iNumLibs = IH.num_lib_versions;

      offset = IH.lib_vers_addr - IH.base_address;

      for(int j=0; i<iNumLibs; j++)

      {

            memcpy( &xbe_standard_libs[j], pXBE + offset, sizeof(stLibrary) );

            offset += sizeof(stLibrary);

      }//End for loop

 

 

      // Hmm...well we should really load the last couple of library's...just so we

      // can show whats in them

      // Kernel Library

      stLibrary xbe_kernel_lib;

      offset = IH.kernel_lib_vers_addr - IH.base_address;

      memcpy( &xbe_kernel_lib, pXBE + offset, sizeof(stLibrary) );

 

      // XAPI Library

      stLibrary xbe_xapi_lib;

      offset = IH.xapi_lib_vers_addr - IH.base_address;

      memcpy( &xbe_xapi_lib, pXBE + offset, sizeof(stLibrary) );

 

      //------------------------Do some file feedback on test values----------------------//

      // all over and done with :)...lets get some juicy data, and write it to our file

 

      // Large temp char buffer

      char buf[500];          

 

      int iOffset = 0;

      unsigned int * ptr = (unsigned int*)pXBE;

 

      // Write out the first 3 char's of the xbe to see what they are

      sprintf(buf, "Sig (0x4) (XBEH) = %c%c%c%c\n", IH.sig[0], IH.sig[1], IH.sig[2], IH.sig[3] );

      fileout(buf);

 

      sprintf(buf, "Base Address: 0x%08X\n", IH.base_address );

      fileout(buf);

 

      sprintf(buf, "Number Sections: 0x%08X\n", IH.num_sections );

      fileout(buf);

 

      sprintf(buf, "Section Address: 0x%08X\n", IH.sect_addr );

      fileout(buf);

 

      sprintf(buf, "Entry Point: 0x%08X (Decodes to: 0x%08X)\n",

                               IH.entry_point, 

                                       IH.entry_point^XOR_ENTRY_RETAIL);

      fileout(buf);

 

      sprintf(buf, "Kernel Thunk Address: 0x%08X (Decodes to: 0x%08X)\n\n",

                              IH.kernel_thunk_addr,

                                      IH.kernel_thunk_addr^XOR_KERNEL_RETAIL );

      fileout(buf);

 

      // Display some data about our sections

      iNumSections = IH.num_sections;

      for( i=0; i<iNumSections; i++)

      {

            offset = pSections[i].section_name_addr - IH.base_address;

            sprintf( buf, "Section[%d] Name: %s\n", i, pXBE+offset );

            fileout(buf);

      }// End for loop

 

}// End ProcessXBE(..)

 

 

//------------------------------Program Entry Point---------------------------------//

//----------------------------------------------------------------------------------//

void main(int argc, char* argv[])

{

      ...

}// End main(..)

 

 

 

And our output looks like this...it will look slightly different for different xbe's...as different xbe's have different sections...depending on the one your using as your project.

 

output.txt

File: default.xbe - xbe filesize: 1633220 bytes

 

Sig (0x4) (XBEH) = XBEH

Base Address: 0x00010000

Number Sections: 0x00000018

Section Address: 0x00010364

Entry Point: 0xA8F93C11 (Decodes to: 0x00056BBA)

Kernel Thunk Address: 0x5B6C60B6 (Decodes to: 0x00012000)

 

Section[0] Name: .text

Section[1] Name: D3D

Section[2] Name: D3DX

Section[3] Name: XGRPH

Section[4] Name: DSOUND

Section[5] Name: WMADEC

Section[6] Name: XONLINE

Section[7] Name: XNET

Section[8] Name: XPP

Section[9] Name: .data

Section[10] Name: DOLBY

Section[11] Name: XON_RD

Section[12] Name: DVDTHUNK

Section[13] Name: .data1

Section[14] Name: XIPS

Section[15] Name: EnglishXlate

Section[16] Name: JapaneseXlate

Section[17] Name: GermanXlate

Section[18] Name: FrenchXlate

Section[19] Name: SpanishXlate

Section[20] Name: ItalianXlate

Section[21] Name: KoreanXlate

Section[22] Name: TChineseXlate

      Section[23] Name: PortugueseXlate

 

That's a real monster of an xbe I'm using... But we can see where getting some juicy info from our xbe.  Notice that a lot of the sections are for different languages...we could cut them out if we wanted and the English version would still run.

 

Also, I've created structures of the different types of headers, and copied the data into them.  We could always use pointers instead and offset them into the main memory of our xbe that we've got.

 

Sitting here drinking my coffee, I can see we have a ton of information on this xbe...but how should we break it down...well I think a diagram would be best... bring our first simple diagram up to date.

 

 

 

Well its a massive sketch isn't it?....its sure grown from that simple version we had at the top...heheh  Well its still more or less the same.  And we'll go about producing an output file which can be recompiled using nasm to produce the original one :)

 

I think a useful app that we could use now, is one that dumps all the contents of the whole xbe...all the headers anyway...so we can take a looksy at all the values.  We know most of them we don't really care about...but it might be a useful app that we can use from time to time to examine cool xbe's...like the halo one..hehe

 

 

*Bug Fix*

When I was writing the complete dump xbe info code...as I just write out all the information from all the sections....to a file...I noticed that stLibrary should only be 16bytes...so I had to change one line in the structure from earlier...other than that..its all good to go.

 

I did this code in a simple way...we call a sub function..which will write all the information to the file...then we do this for all the headers....

 

void DumpImageHeaderInfo(unsigned char * pXBE, stImageHeader *pImageHeader);

void DumpCertHeaderInfo( unsigned char * pXBE, stCertificate *pCertHeader );

      ...etc

 

 

code J : main.cpp

 

#include <stdio.h>                     // Need this for fopen(..) fwrite(..) etc

#include <string.h>                    // memcpy(..)

 

// We use this simple function to write data to our file.

void fileout(char *str)

{

      ...

}// End fileout(..)

 

long FileSize( char * szFileName )

{

      ...

}// End FileSize(..)

 

 

//-----------------------------stImageHeader Structure------------------------------//

// Remember that we use 'pragma pack(1)' so our data in the stucture is packed nice

// and tight using a 1 byte alignment.

#pragma pack(1)

struct stImageHeader

{

      ...

};

#pragma pack()

 

//-----------------------------stCertificate Structure------------------------------//

#pragma pack(1)

struct stCertificate

{

      ...

};

#pragma pack()

 

//-------------------------------stSection Structure--------------------------------//

#pragma pack(1)

struct stSection

{

      ...

};

#pragma pack()

 

 

//-------------------------------stLibrary Structure--------------------------------//

#pragma pack(1)

struct stLibrary   // size 16 bytes

{

      ...

};

#pragma pack()

 

//---------------------------------stTLS Structure----------------------------------//

#pragma pack(1)

struct stTLS

{

      ...

};

#pragma pack()

 

 

//---------------------------Dump All ImageHeader Details---------------------------//

 

void DumpImageHeaderInfo(unsigned char * pXBE, stImageHeader *pImageHeader)

{

      ...

}//End DumpImageHeaderInfo(..)

 

void DumpCertHeaderInfo( unsigned char * pXBE, stCertificate *pCertHeader )

{

      ...

}// End DumpCertHeaderInfo(..)

 

void DumpSectionHeaderInfo( unsigned char * pXBE, unsigned int iBaseAddr,

                                        stSection * pSectHeaders, int iNumSect)

{

      ...

}// End DumpSectionHeaderInfo(..)

 

void DumpTLSHeaderInfo( unsigned char * pXBE, stTLS * pTLS )

{

      ...

}// End DumpTLSHeaderInfo(..)

 

void DumpLibraryHeader( unsigned char * pXBE, stLibrary *pLibs, int iNumLibs)

{

      ...

}// End DumpLibraryHeader(..)

 

//-------------------------------ProcessXBE Function--------------------------------//

 

void ProcessXBE( unsigned char *pXBE, long iSizeXBE )

{

 

      char buf[500];

 

      // Lets fill in our stImageHead Structure

      stImageHeader IH;

      memcpy( &IH, pXBE, sizeof(stImageHeader) );

      DumpImageHeaderInfo( pXBE, &IH );

 

 

 

      // Well if we look at what value we have in our stImageHeader for

      // Certificate Address (0x4) : 0x00010178... we can see the value is

      // a bit high...this is because its using that base address value.

      // which is 0x10000.

      // So if we take that 0x10000, we are left with something like 0x178

      // which seems pretty reasonable, as the stImageHeader is 0x178 bytes in

      // size...which leads us to beleive its located straight after the szImageHeader

      // structure.

      stCertificate Cert;

      int offset = IH.cert_addr - IH.base_address;

      memcpy( &Cert, pXBE + offset, sizeof(stCertificate) );

      sprintf(buf, "{Offset: 0x%08X}\n", offset);

      fileout(buf);

      DumpCertHeaderInfo(pXBE, &Cert);

 

      // Our Sections..this is where the meat and potatoes of the code is!  Yup

      // this is where all our code and data is located.

      // Now I just allocate space for 50 sections...didn't want to start allocating memory

      // etc....but if its got more than 50 sections its one mutant super xbe we have here!

      stSection pSections[50];

      int iNumSections = IH.num_sections;

      offset = IH.sect_addr - IH.base_address;

 

      sprintf(buf, "{Offset: 0x%08X}\n", offset);

      fileout(buf);

 

      // Now the address offset points to the top of the sections...as the sections are

      // stored as an array...one after the other

      for(int i=0; i<iNumSections; i++)

      {

            memcpy( &pSections[i], pXBE + offset, sizeof(stSection) );

            offset += sizeof(stSection);

      }//End for loop

      DumpSectionHeaderInfo(pXBE, IH.base_address,

                              pSections, iNumSections);

 

      // Where nearly there now...not much more to go!  We'll get through this

      // TLS Data

      stTLS tls;

      offset = IH.tls_addr - IH.base_address;

      memcpy( &tls, pXBE + offset, sizeof(stTLS) );

      sprintf(buf, "{Offset: 0x%08X}\n", offset);

      fileout(buf);

      DumpTLSHeaderInfo(pXBE, &tls);

 

      // Lets get all the library versions finally.

      // Now I've just put a static array together for now...I can't see an xbe having more

      // than 50 librarys...

      stLibrary xbe_standard_libs[0x20];

      int iNumLibs = IH.num_lib_versions;

      offset = IH.lib_vers_addr - IH.base_address;

 

      sprintf(buf, "{Offset: 0x%08X}\n", offset);

      fileout(buf);

 

      // I Can't see why...guess I'm getting sloppy..but this loop was bad!  Looks okay

      // to me...must be missing something here.  If anyone can see why this loop

      // wouldn't work give me some feedback on this...many thanx :)

      //

      // for(int j=0; i<iNumLibs; j++)

      // {

      //    memcpy( &xbe_standard_libs[j], pXBE + offset, sizeof(stLibrary) );

      //    offset += sizeof(stLibrary);

      // }//End for loop

 

      memcpy( xbe_standard_libs, pXBE + offset, iNumLibs * sizeof(stLibrary) );

      DumpLibraryHeader(pXBE, &xbe_standard_libs[0], iNumLibs);

 

 

      // Hmm...well we should really load the last couple of library's...just so we

      // can show whats in them

      // Kernel Library

      if( IH.kernel_lib_vers_addr != 0 )

      {

            stLibrary xbe_kernel_lib;

            offset = IH.kernel_lib_vers_addr - IH.base_address;

            sprintf(buf, "{Offset: 0x%08X}\n", offset);

            fileout(buf);

 

            memcpy( &xbe_kernel_lib, pXBE + offset, sizeof(stLibrary) );

            DumpLibraryHeader(pXBE, &xbe_kernel_lib, 1);

      }// End if

 

      // XAPI Library

      if( IH.xapi_lib_vers_addr != 0 )

      {

            stLibrary xbe_xapi_lib;

            offset = IH.xapi_lib_vers_addr - IH.base_address;

            sprintf(buf, "{Offset: 0x%08X}\n", offset);

            fileout(buf);

 

            memcpy( &xbe_xapi_lib, pXBE + offset, sizeof(stLibrary) );

            DumpLibraryHeader(pXBE, &xbe_xapi_lib, 1);

      }

 

}// End ProcessXBE(..)

 

 

//------------------------------Program Entry Point---------------------------------//

//----------------------------------------------------------------------------------//

void main(int argc, char* argv[])

{

      ...

}// End main(..)

 

 

And now for the output!  Its a lot...and thats only because I'm working with a monster of an xbe..heheh....have to drink a lot of coffee to break this down :)  Eeekkk... there so much output from my xbe...that I thought I'd just put most of the stuff you need to see there...and you can click the link to see the whole output if you want to take a looksy :)  Most of its just boring data...that we could just put nulls in if we where creating our own xbe.  But some of it is useful...like the program entry point :)...which we'll get to soon..so we can take a look at code.

 

OutputFile J : output.txt
 

File: default.xbe - xbe filesize: 1633220 bytes

 

--ImageHeader (0x178 bytes)----------------------------

Sig (0x4) (XBEH) = XBEH

Digital Signature (0x100 bytes) Skip

Base Address : 0x00010000

Size Header : 0x00002000

Size Image : 0x0019E240

Size Image Header : 0x00000178

Date & Time : 0x3D5CE207

Certificate Address : 0x00010178        (0x00000178)

Num Sections : 0x00000018

Sections Start Address : 0x00010364   (0x00000364)

Init Flags : 0x0000000C

             Limit64Megabytes

             DontSetupHarddisk

Program Entry Point : 0xA8F93C11     (0x00056BBA)

TLS Header Address : 0x0001D8EC   (0x0000D8EC)

PLS Stack Commit : 0x00010000

PE Stack Reserved : 0x00100000

PE Heap Committed : 0x00001000

PE Base Address : 0x00011B40

PE Size Image : 0x0015A960

PE Checksum : 0x0016A024

PE Date & Time : 0x3D5CE202

PathName Address : 0x00010A7C      (0x00000A7C)

            (d:\xboxret\private\ui\xapp\obj\i386\xboxdash_2002.exe)

FileName Address : 0x00010AA0        (0x00000AA0)

            (xboxdash_2002.exe)

Unicode FileName Address : 0x00010A58       (0x00000A58)

            (xboxdash_2002.exe)

Kernel Thunk Address : 0x5B6C60B6             (0x5B6B60B6)

Kernel Thunk Address : 0x5B6C60B6   

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.