xbdev - software development
Tuesday April 16, 2024
home | contact | Support | XBOX Programming More than just a hobby...


XBOX Programming

More than just a hobby...


Patched XBE? #



Simply put...what patching an xbe does, it convert a debug xbox executable (.xbe) to a retail xbe, that can be run on a standard retail xbox..


First things first....we have 2 identical files...but one is patched and the other isn't....we'll create a short program to see what in essence patching an xbe changes...which offsets.


Size's are identical...e.g if one is 10.01kb, then the other is 10.01kb as well.


Anyhow, I wrote two programs so you could see what is actually happening, it will one improve your coding skills and two help you two see how simple it is to create an xbe patch program.


The first program below, will open two xbe files, called 'pcode.xbe' (e.g. patched xbe) and a non-patched xbe file called 'ncode.xbe'.  It first checks the file sizes, and writes the file size of both file to a text file called info.txt...which will be created in the same directory as the main.exe program. 

Following that, we'll compare byte by byte of each program and write out any differences and there location to the same text file.


// File: main.cpp

// Author: bkenwright@xbdev.net


// compare patched and non-patched xbe's


#include <stdio.h>


void debug(char *str)


      FILE *fp;

      fp=fopen("info.txt", "a+");

      fprintf(fp, "%s\n", str);




// patched xbe is      "pcode.xbe"

// non-patched xbe is  "ncode.xbe"


#include <windows.h>

#pragma comment(lib, "Kernel32.lib")


int __stdcall WinMain(HINSTANCE, HINSTANCE, char*, int)


      //------These few lines just confirm that the two files are identical sizes-----

      HANDLE h;

      DWORD dwSize1, dwSize2;


      h=CreateFile( "ncode.xbe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );

      dwSize1 = GetFileSize( h, NULL);




      h=CreateFile( "pcode.xbe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );

      dwSize2 = GetFileSize( h, NULL);



      char sz1[400];

      sprintf(sz1, "FileSize ncode.xbe: %u \t FileSize pcode.xbe: %u", dwSize1, dwSize2);



      //-----Compare both files and show where they differ-----


      FILE *fp1 = fopen( "ncode.xbe", "rb" );

      FILE *fp2 = fopen( "pcode.xbe", "rb" );


      int e = 1;

      unsigned char buf1, buf2;


      unsigned int offset = 0;



      debug("offset\t non-patched\t patched\n");


      while (e)


            e = fread(&buf1, 1, 1, fp1);

            e = fread(&buf2, 1, 1, fp2);




            if( buf1 != buf2)


                  char sz2[200];

                  //            offset   before   after

                  sprintf(sz2, "0x%.4X\t 0x%.4X\t 0x%.4X", offset, buf1, buf2);












Output text file: (info.txt)


FileSize ncode.xbe: 126976 FileSize pcode.xbe: 126976

offset       non-patched       patched

0x0129   0x0045               0x00A5
0x012A  0x008C               0x0046
0x012B  0x0084               0x00FD
0x012C  0x0094               0x00A8

0x0159  0x00D2               0x0036
0x015A  0x0072               0x00C3
0x015B  0x00B3               0x006F
0x015C  0x00EF               0x005B


You can easily see the two main parts that the patcher changes...these are two DWORD values at offset 0x0128 and 0x158.





Debug Release
0x0128 0 x 45 8C 84 94 -> 0 x A5 46 FD A8
KEYS (XOR) 0 x 4B 9D 85 94 -> 0 x AB 57 FC A8




Debug Release
0x0158 0 x D2 72 B3 EF -> 0 x 36 C3 6F 5B
KEYS (XOR) 0 x 52 F1 B1 EF -> 0 x 5B 6D 40 B6



How to convert from debug to release?...lets take 0x128 as an example...an I'll do it byte by byte so you don't get mixed up.


94  XOR 94 = 0 .... 0   XOR A8 = A8

84  XOR 85 = 1....  1   XOR FC = FD

8C XOR 9D = 11.. 11 XOR 57  = 46

45  XOR 4B = E... .E  XOR AB = A5


so its like this:


dwDebugValue  XOR  dwDebugKey = dwTemp


dwTemp XOR dwReleaseKey = dwReleaseValue



You would do the same with offset 0x0158.



{NOTE}.  Its worth looking at the xbe file format specification to see what these offsets mean, first is for the entry point of our program, the second for the kernal table.



// File: main.cpp

// Author: bkenwright@xbdev.net


// convert a debug xbe to a retail xbe (patched).


#include <stdio.h>

#include <string.h>



// IMPORTANT, there is no error checking in this code, as you should check that

// the parameters passed are valid etc.


void __cdecl main(int argc, char** argv)


      if( argc < 2 )



      char szOldFile[256];

      char szNewFile[256];


      strcpy( szOldFile, "bak_" );

      strcat( szOldFile, argv[1] );


      strcpy( szNewFile, argv[1] );


      // Make a backup of our file

      rename( argv[1], szOldFile  );


      unsigned char dwDebugKeyA [4]  = {0x94, 0x85, 0x9D, 0x4B };

      unsigned char dwRetailKeyA[4]  = {0xA8, 0xFC, 0x57, 0xAB };


      unsigned char dwDebugKeyB [4]  = {0xEF,0xB1,0xF1,0x52};

      unsigned char dwRetailKeyB[4]  = {0x5B,0x6D,0x40,0xB6};



      //-----Compare both files and show where they differ-----


      FILE *fp1 = fopen( szOldFile, "rb" );

      FILE *fp2 = fopen( szNewFile, "wb+" );


      int e = 1;

      unsigned char dwbuf;


      unsigned int offset = 0;


      while (e)


            e = fread(&dwbuf, 1, 1, fp1);


            if( e == 0 )



            if( offset >= 0x0128 && offset <= (0x0128+3))


                  static int cc = 3;


                  dwbuf = dwbuf ^ dwDebugKeyA[cc];

                  dwbuf = dwbuf ^ dwRetailKeyA[cc];




                  fwrite(&dwbuf, 1, 1, fp2);


            else if( offset >= 0x158 && offset <= (0x158+3))


                  static int bb = 3;


                  dwbuf = dwbuf ^ dwDebugKeyB[bb];

                  dwbuf = dwbuf ^ dwRetailKeyB[bb];




                  fwrite(&dwbuf, 1, 1, fp2);




                  fwrite(&dwbuf, 1, 1, fp2);











With a few minor improvements you can create a simple un-patcher, and also better error checking, in-case the file can't be found etc.








Advert (Support Website)

Copyright (c) 2002-2024 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.