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  
|
|