I am attempting to amend some strings in an old DOS game (FIFA International Soccer), specifically the names of players.

In the past it hasn't been too hard to pull off such a task on post-DOS games as either the strings are easy to track down in the executable or a data file. However, for this DOS game I am stuck.

I have scanned through the game files and the executable with a hex-editor but cannot find the strings. The game includes a english.dat file which does contain readable localised strings. However, this file only contains names for in game headers and menu options as opposed to the player names I am seeking. The other files consist of localised strings for other languages, graphics and sounds.

I have used a debug build of DOSBOX to perform a memory dump using the following command:

memdumpbin 180:0 1000000

I got the command from here: http://www.vogons.org/viewtopic.php?t=9635

In the memory dump I can find the strings I wish to change.

What is the best way of linking this information to find the source of the strings? I assume that these strings are somehow encrypted or compressed in the executable, although it could be in another obscure game file. If the strings are compressed/encrypted perhaps there is a common method for pulling this data out of the executable.

I have IDA5 (free version) installed and would be happy using this as part of process. My operating system is Windows 8 / 64 bit.

Also, to be clear - I want to modify the strings at source (i.e. in the file) and not in memory.

  • 1
    I mention several strategies at the top of reverseengineering.stackexchange.com/a/3004/2959 - may work for common obfuscation/encoding, but not for compression. – Jongware Mar 7 '15 at 11:23
  • 1
    Just checked; the executable file is compressed or encrypted, and I cannot find any of the data file names. (It does contain the string "Hello EA" at what appears to be the end of the 'main' code.) – Jongware Mar 7 '15 at 14:17

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.>..-.A@I.7P........h4..a"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=@GOLD=@mvsound.sys@DEVICEdevice@@@
000C0 : @@@@@@@@@@@.......ULTRAMID@@@@@@@@@@@@@@@ ..@.@@@@@@.........@.@
00100 : .@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@@.@@@@..@@...@@@..@@......&...
00140 : ./....8....C....N....X...@c.@@ m....y...................C.......

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 : B.@.L.@^W.@.a.@^l.@.v.@^..@...@ @ @ @ @ @ @ @~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

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 : ..@...@,..@..Algeria@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 

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:

  1. Read up on PharLap's sections.
  2. Decrypt each section individually.
  3. Write all into a new file.
  4. Adjust what you want.
  5. Encrypt the sections again (it's a XOR key, so this uses the exact same algorithm).
  6. 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)

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

  2. 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);
        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;
  • That was a lot more than I expected. Thanks you have saved me many hours. Along my travels I did come across the possibility that the executable was packed with something like pklite, and oddly enough after recently messing around with Scumm games I have seen XOR as an effective and efficient encryption mechanism. – user11826 Mar 7 '15 at 16:44
  • 3
    You're welcome! Examining old games with the powerful computers of today (and lots of hindsight) is almost as much fun as it was to play them. I'll take a closer peek at PharLap's headers, it's been quite a while since I last saw them. – Jongware Mar 7 '15 at 17:11
  • @Jongware: Did you have the game stashed away somewhere, or is there a place to download it? Reading your post made me wonder if the decryption takes place within the pharlab loader, or the game itself, and if it's possible to decrypt the 32 bit sections and nop out the XOR-ing part of the loader. – Guntram Blohm Mar 7 '15 at 19:19
  • @GuntramBlohm: It seems classified as "abandonware" and easily found when including "1993" (but I wonder if EA agrees. Nevertheless, I'm on a Mac so it's useless for me anyway.) The Pharlap header is entirely valid but the XOR scheme does not follow sections. I think it's a scheme where the first word indicates the number of encrypted bytes in each successive block. – Jongware Mar 7 '15 at 20:34
  • 1
    .. My last comment on this: The decoder is at the very end of the PharLap executable and starts at the EIP in its header. But it's obfuscated as well and cannot readibly be disassembled. Fortunately, to get hold of just the players' names, you don't have to. However, it's not easy to get a hold of the list of name pointers (it could be the data right after). Another problem may be that the PharLap header contains a checksum, and changing data may invalidate the executable. – Jongware Mar 11 '15 at 23:34

There's an ida plugin that connects to (a patched version of) DosBox and allows you to debug DosBox games from ida. However, i doubt you'd get that to work with the free version of ida.

The fact that your memdumpbin uses the address 180:0 hints that the game uses a dos extender, so the "real" program is a 32 bit program which runs in protected mode - 180 is the segment value that dos extenders typically use for their 32 bit segment. Which poses another problem - IDA 5 free can read the dos part of a 16 bit .exe (which is basically the extender), but not the 32 bit part (which has a format known as LE).

However, the even older IDA Free 4.1 was able to read LE files, and it's still floating around on the internet (search for idafre41.zip). And there's a free dos extender DOS32A which includes a tool (you need to download the source code) that separates the 32 bit LE file from the 16 bit extender. When i wanted to debug a similar program a year ago, i was able to use DOS32A to get the LE file, load that into IDA 4.1 to get an IDA database, and open that database in IDA free 5.0. (I bought IDA by now, which made my life much easier; i'd recommend this to anybody despite the price which is a bit steep for a hobbyist)

You might be able to match the address where you found the player names to an XREF in IDA, then check the functions that access that address for what writes to it, then use the debugger integrated in DOS32A to debug your program and verify where the player names get written, and finally trace their source and modify this. However, if the names are really compressed in the original .exe file, you'd have to identify the compression scheme from the assembly text (which may or may not be a standard one), then find out how to compress your modified strings according to that scheme, and hope the new strings will fit into the memory that the old compressed strings use.

So, your task isn't going to be easy, but you'll learn a lot in the process - good luck!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy