This program uses the PharLap DOS extender, as can be seen in its MZ header. The 32-bit executable program starts at offset 18A0
, per "offset within header of relocation table" (see http://www.program-transformation.org/Transform/PcExeFormat), and at that position you can see the correct signature P3
. According to the header info, the executable's length is 0x95851
, which is another hint this is correct. Near the end of this part, starting at 18A0
, you can see a text string "Hello EA", and at the next 32-byte "page" the signature MZ
that indicates another executable is embedded. So this large part must contain the main executable.
Browsing the file with a simple hex editor at my preferred width of 16 hex characters, I noticed a recurring pattern when doing page-downs (a good way to get a 'sense' of what sort of data a file contains). I saw the pattern repeated every 2 lines, and when I set the display width to 32, the pattern was evident. Executable formats always start with a fixed header, and are usually followed by lots of zeroes for padding, so I suspected the repeating pattern may be the XOR key. A simple C program confirmed this; I did not know where to start with decoding but the first non-all-zeroes multiple of 32 seemed a good guess: offset 0x1AA0
.
Decoding from there proved the hunch to be correct:
00000 : Y...r9..n3.>[email protected]"1.P(s.......x. rG..f...X.+..
00040 : ..a|D.P(.b..A...x......f3..F..h4....a.P(...........o7..f3..F2...
00080 : .@@@@@@...@@BLASTER=@ULTRASND=@[email protected]@DEVICEdevice@@@
000C0 : @@@@@@@@@@@.......ULTRAMID@@@@@@@@@@@@@@@ ..@.@@@@@@.........@.@
00100 : .@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@@.@@@@..@@...@@@..@@......&...
00140 : ./....8....C....N....X...@c.@@ m....y...................C.......
00180 : [email protected]@[email protected]@[email protected]@S
001C0 : [email protected]@[email protected]@[email protected]@
00200 : [email protected]@SBAWE32.ADV@@@@[email protected]@SBDI
00240 : [email protected]@[email protected]@PASDIG.ADV@@GF1DIGI.ADV@C
(etc.)
so the next step was scroll down to near the end of this part and see what was there. Disaster! Rather than readable texts, all I saw was random data -- yet still with clear patterns.
But 'an executable' is not one contiguous long chunk of data. It's common to see it divided up into separate sections for "executable code", "initialized data", "uninitialized data", "relocations" and so on. The sections all start at an aligned address when loaded into memory, but not necessarily in the file itself, or with the same 'memory page' size. Therefore, it may be possible that the XOR encryption restarts at the start of new section. The PharLap header should contain information on where each section starts and ends (and if you are going to attempt to adjust the program, you should look into this), but to confirm the XOR key is the same all I had to do is adjust the starting position. Starting one position further, no success, but 2 positions further on I noticed this piece of data:
890C0 : [email protected].@^[email protected].@^[email protected].@^..@...@ @ @ @ @ @ @ @~FIFA International
89100 : Soccer@ @PC Version by@~The Creative Assembly@ @~Lead Programme
89140 : r@ @Tim Ansell@ @ @ @~Programmers@ @Adrian Panton@Clive Gratton@
89180 : @ @~Lead Artist@ @Will Hallsworth@ @ @ @~Additional Artwork@ @A
891C0 : lan Ansell@ @ @ @~Original Music@Composed, Produced@and Performe
89200 : d by@Ray Deefholts@for ~HFC Music@ @Additional Drum@Programming
89240 : and@Assistance@ @Tim Ansell@ @~Sound Effects@ @Bill Lusty@ @ @ @
89280 : ~Producer@ @Kevin Buckner@ @ @ @~Associate Producer@ @Nick Golds
(etc.)
That was the proof I needed: the data section does use the same XOR key. Next: testing all possibilities from 0 to 31 and see if something turns up. Only at +30 that turned out to work, just as I was going to give up:
782C0 : ..@...@,[email protected]@Ali Mehdaoui Igail@Mohammed Said@Abdel Dahb
78300 : i@Hamid Ahkmar@Nagar Baltuni@Omar Mahjabi@Ali Cherif@Hamar Mahbo
78340 : ud@Khered Adjali@Imahd Tasfarouk@Alamar Sahid@Mahmar Ahboud@Akha
78380 : r Binnet@Mouhrad Dahlib@Mahied Amruk@Lakhar Diziri@Amaar Azir@Mu
783C0 : stafa Farai@Akmar Bahoud@Ahmad Said@Taraki Aziz@Argentina@Alfio
(etc.)
So each individual section in the executable is encrypted with a 32-byte XOR key; this XOR key is the same for all sections; it starts a-new per section.
The C program below will decrypt the entire file and you have to adjust the starting position manually. To edit the file, you have to:
- Read up on PharLap's sections.
- Decrypt each section individually.
- Write all into a new file.
- Adjust what you want.
- Encrypt the sections again (it's a XOR key, so this uses the exact same algorithm).
- Copy the encrypted file back into the main executable.
A note on #4: you mentioned changing the names of the players. Since it's a zero-terminated list of names, you can assume there is a list of pointers to these names somewhere else. That means you can only change the individual characters of a name -- not make it longer. If you want to adjust all names freely, you must find the list of pointers and adjust that as well.
(Preliminary updates)
The XOR encoding does not use sections. Instead, it seems like every block starts with a word determining its length, and possibly 1 or 2 next words (possibly (again) to set the XOR key starting position). Not conclusive so far.
Executables are abundant with zeroes. If you count the number of zeroes in each 32-byte chunk, XORed against all 32 possible positions, and print out the XOR position with the highest number of them, you can see successive lists of the same 'best' guess. That shows there are longer and shorter sections XORed with the same key and may help determining the length algorithm.
#include <stdio.h>
#include <stdlib.h>
unsigned char encrypt[32] = {
0x23, 0x91, 0xC8, 0xE4, 0x72, 0x39, 0x9C, 0xCE,
0x67, 0x33, 0x99, 0xCC, 0xE6, 0x73, 0xB9, 0x5C,
0x2E, 0x17, 0x8B, 0x45, 0xA2, 0x51, 0xA8, 0x54,
0x2A, 0x95, 0xCA, 0x65, 0x32, 0x19, 0x8C, 0x46
};
int main(int argc, char *argv[])
{
FILE *f;
int i, c, d = 0;
f = fopen ("../Downloads/fifa/fifa.exe", "rb");
if (!f)
{
printf ("yeah no such file\n");
return 0;
}
/* reasonable assumption for start: */
fseek (f, 0x1aa0, SEEK_SET);
/* adjust per section! this position is valid for the names only */
fseek (f, 30, SEEK_CUR);
c = 0;
printf ("%05X : ", d);
do
{
d++;
i = fgetc (f);
if (i == EOF) break;
i ^= encrypt[c & 31];
if (i >= ' ' && i <= '~') putchar (i); else if (i) putchar ('.'); else putchar ('@');
if (++c >= 64)
{
c = 0;
printf ("\n");
printf ("%05X : ", d);
}
} while (d < 0x95851);
fclose (f);
return 0;
}