Digimon Online (also known as Digimon Online World or Digimon Battle Server in Japan) was a 2000 online fighting game developed by CCR, a South Korean company. The game was released for Windows.

The goal of this blog posts is trying to explain how I would achieve a basic server emulator for the game, as well as trying to decrypt the game data.

Tools used:

  • x64dbg (Debugger)
  • Ghidra (Static binary analyzer)

First, let's see the content of the game directory.

We can notice an IO driver called X2PIO.sys, the PDB references "C:\NTDDK\Devel\X2PIO\i386\X2PIO.pdb" which imports "ntoskrnl.exe" and "HAL.dll".

Two other interesting files are ABuse.lib and BlkBoxBaseA.lib, by checking them with 7-Zip they confirm to be COFF archives (.lib) and they can be opened with any ZIP archiver, we can confirm that this file was used to link the DLL in the application, quite strange to see this files being distributed.

The file jptest1.dat just reports "cantplayanymorea", the file "ticker.txt" just contains some korean text, while "digifix.dat" just some binary data.

The file AutoPatch/filelist.dat contains the patch information for all the files.

The format is decribed as follows:

MD5 Hash[TAB]File size[TAB]File name
for example:

The most interesting file of all is "srv.info" that contains all the IPs and the Server names for all the game, nice.

The "kor" directory includes all the content of the game, from what I can suppose:

  • DMS format looks to be their sprite or image format.
  • PAL format might reference to the image's palettes.
  • WAV files are used for SFX.
  • MIDI files are used for BGM.
  • DAT files seems to be configuration files.
  • MAP files might be their format for map definitions, it seems that it can include different objects.
  • I have to clue about MAN, DFS, SDS, SMP found in maps and TPD, TLV found in public.

The filelist reports "digimon.dll" but we only have "digimon.exe" in the files.

If we run Digimon.exe normally it just crashes, while running it with Windows 95/98 we can get an error message:

First, we open the process with x32dbg and we scan for "All intermodular calls", so we can intercept any MessageBoxA or MessageBoxW in case of an Unicode application. The client only uses a bunch of ANSI Messageboxes.

We set a breakpoint to the first istruction of MessageBoxA like this:

We let the application run until the precedent breakpoint and check the call stack in x64dbg:

As you can see, 0x0055C76E is where this MessageBox is being called.

Let's fire Ghidra and see what this function looks like...

Based from the call, we can already assume that "DAT_0058C140" is either a "const char*" (4 bytes pointer to an array) or a char[] (array of 1 byte each).

There is no reference of DAT_0058C1400, so it might be a global variable.

Saving this characters in a text file and converting them to Codepage 949 confirms the error text, so not only we are in the right position, but this message's size is 32 byte.

The error is thrown if local_14, which is a string, is different than 1.

local_14 is populated by sscanf, which takes DAT_005D76CC + 4 as input, suggesting that DAT_005D76CC could be either a struct or a string.

Two different functions gets executed if DAT_005D76C8 is 6, 7 or anything else.

By double clicking on DAT_005D76CC we can find it's referenced in __setargv function.

This function is internally used by Microsoft Visual C++ compiler to create both "argc" and "argv" variables.

Microsoft DOES provide C source code for their implementation for reference, as this executable is compiled with Microsoft Visual C 6.0, I don't have it installed in my machine, but I was able to find this page that documents what we need.

Thanks to the null termination initialization in the line "_pgmname[MAX_PATH] = 0", we know the type of DAT_005D78E8 which is char[MAX_PATH+1] or 261 bytes.

NOTE: Make sure to undefine DAT_:005D79EC as Ghidra has not analyzed it with the now discovered array.

DAT_005D76C8 is __argc and DAT_005D76CC is __argv.

Here's how the reconstructed function looks like:

Going back to our previous function, we can finally understand the origin of the error:

We must have two integers as our arguments, and the second argument must be 1 otherwise the game will not start.

By passing the arguments "0 1", the game still doesn't start, and that's impossible. If we disable Windows 95/98 compatibility mode we get a full-screen window and an error, Thanks Windows -.-''

If we try to run the game with 6 arguments nothing seems to change, while running it with "0 1" 0 0 0 0 /gkstkdal prompts us another error:

which is easily fixable by setting the Color mode to 16bit, now we have access to the windowed mode, which makes debugging easier now, yay!

The next error message appears:

By doing the same technique as before with x64dbg, let's see where it points us now with Ghidra:

The if that generates our issue seems to be a callback, which we can only trace it with x64dbg in this particular call:

which points to the following function:

which in Ghidra in looks like this:

As this function is pretty big (we can also see that it loads svr.info), let's keep tracing with x64dbg until we get an error.

This is what the problematic function looks like:

We can assume that the program is trying to load a registry subkey called "Software\X2Online\Digimon Online V1.5" and if it's not found it returns an error.

The function Open is imported from BlkBoxBaseA and it's contained in the CRegistry class, time to see the function and understand what key is it calling (HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER and so on).

This is the open function:

Our key is passed as parameter two, which means:

Bingo! HKLM.

Once our key has been opened, the next function FUN_0055CD80 starts, and we need to make sure that this function does not fail.

The PATH of the game seems to be defined as SZ_STRING value in the subkey, this might be why the game was not starting (sigh, why this method was overused for old games)

Ghidra gets a little messed up in this function (even by changing the return type)

The program might exit if local_11c is different than 0 or "IVar1 + iVar2" is smaller than 0x100.

Let's make a registry key that points to our digimon.exe directory.

The path for x86 systems is:
While for x86_64 systems is:
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\X2Online\Digimon Online V1.5

We got the game working!

This concludes the first of episode of this reversing, happy hacking ^^