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);
fclose(fp);
}
// 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);
CloseHandle(h);
h=NULL;
h=CreateFile(
"pcode.xbe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
dwSize2 = GetFileSize( h, NULL);
CloseHandle(h);
char sz1[400];
sprintf(sz1, "FileSize ncode.xbe: %u \t FileSize pcode.xbe: %u", dwSize1,
dwSize2);
debug(sz1);
//-----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("\n");
debug("offset\t non-patched\t patched\n");
while (e)
{
e = fread(&buf1, 1, 1, fp1);
e = fread(&buf2, 1, 1, fp2);
offset++;
if( buf1 != buf2)
{
char sz2[200];
// offset before
after
sprintf(sz2, "0x%.4X\t 0x%.4X\t 0x%.4X", offset, buf1,
buf2);
debug(sz2);
}
}
fclose(fp1);
fclose(fp2);
} |
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
then
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 )
return;
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 )
break;
if( offset >= 0x0128 && offset
<= (0x0128+3))
{
static
int cc = 3;
dwbuf = dwbuf ^ dwDebugKeyA[cc];
dwbuf = dwbuf ^ dwRetailKeyA[cc];
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];
bb--;
fwrite(&dwbuf, 1, 1, fp2);
}
else
{
fwrite(&dwbuf, 1, 1, fp2);
}
offset++;
}
fclose(fp1);
fclose(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.
|