[UE4] Mortal online 2 .db file. please help

Extraction and unpacking of game archives and compression, encryption, obfuscation, decoding of unknown files
froggins
Posts: 3
Joined: Thu Apr 29, 2021 7:04 pm

[UE4] Mortal online 2 .db file. please help

Post by froggins »

there is a .db file that seems to contain the things i am looking for. however it seems to be encrypted and im not sure if its an sqlite database or something else just stored with the extension. if anyone is willing to help me figure out how its encrypted or how to open it it would be much appreciated

also game is MO 2 its for PC and built using UE4

the .db: https://drive.google.com/file/d/1MxvHmA ... sp=sharing
the games shipping exe: https://drive.google.com/file/d/1C9Q7Dz ... sp=sharing

any help would be really appreciated spent awhile trying to figure this out.
Last edited by froggins on Wed May 05, 2021 3:01 am, edited 1 time in total.
spiritovod
Posts: 719
Joined: Sat Sep 28, 2019 7:00 pm

Re: [UE4] Mortal online 2 .db file. please help

Post by spiritovod »

I'm more into debugging stuff at runtime, but since the game is 75GB (which I'm not going to download), here is function, which most likely read such db files, which is indeed encrypted sql database, judging by relevant functions.

Deobfuscating part is "do ... while ( v9 < v16 )", where byte_14AF73848 is following byte[8] array: "2, 3, 5, 7, 0Bh, 0Dh, 11h, 13h" - but I'm not sure what exactly algorithm it is, maybe simple rotation with xoring.
froggins
Posts: 3
Joined: Thu Apr 29, 2021 7:04 pm

Re: [UE4] Mortal online 2 .db file. please help

Post by froggins »

spiritovod wrote:I'm more into debugging stuff at runtime, but since the game is 75GB (which I'm not going to download), here is function, which most likely read such db files, which is indeed encrypted sql database, judging by relevant functions.

Deobfuscating part is "do ... while ( v9 < v16 )", where byte_14AF73848 is following byte[8] array: "2, 3, 5, 7, 0Bh, 0Dh, 11h, 13h" - but I'm not sure what exactly algorithm it is, maybe simple rotation with xoring.

thank you for the help im going to try to figure that out.

needa do some research to understand a couple parts of that because i dont have much experience with C++


also while byte_14BD466A1 and byte_14BD466A0 have values
byte_14AF73848 is only mentioned once and is an array. how would i go about finding its value? (edit) actually i see in your response you described what this is sorry about that)

with v8 = *(_QWORD *)v7; im not quite sure what QWORD is. is this a variable or is it just giving a type to v7 which v8 then equals? feel like im looking at this part completly wrong.

when v15[0] is set to = 0i64 this means its equal to 0 but stored as a 64bit int right? so then when v7 = v15[0] it would also equal 0?

sorry for all the random questions just trying to understand the code more since i am not used to how C++ does things(signing up for a class on it during summer for sure)
spiritovod
Posts: 719
Joined: Sat Sep 28, 2019 7:00 pm

Re: [UE4] Mortal online 2 .db file. please help

Post by spiritovod »

@froggins: Well, I don't quite understand it either, it's much easier to observe such things at runtime for better understanding. You can do AOB search for the db header, 1D 17 DA EF, it will land you right at the spot. Also, I don't think you need variables outside mentioned cycle, the rest of function is for reading that file in blocks.
atom0s
Posts: 250
Joined: Sat Dec 27, 2014 8:49 pm

Re: [UE4] Mortal online 2 .db file. please help

Post by atom0s »

spiritovod has the right function, it's just a matter of following what it does in the proper order. This does use SQLite3 (specifically revision hash: 042a1abb030a0711386add7eb6e10832cc8b0f57) but is custom encrypted as SQLite3 does not give you encryption by default unless you add on SQLiteCypher which this doesn't use. Instead, they implemented their own on-open encryption.

Here's a rundown of what the function is doing:

Code: Select all

  result = fopen(a1, "rb");
  v2 = result;
  if ( result )
  {
    if ( (unsigned int)fread(&v18, 4i64, 1i64, result) == 1
      && v18 == -270919907
      && (unsigned int)fread(&v17, 4i64, 1i64, v2)
      && v17 != 1766609235
      && (fread(&v16, 4i64, 1i64, v2),
          sub_1435B4550(":memory:", v15, 6i64),
          v3 = v15[0],
          v4 = **(_QWORD **)(*(_QWORD *)(*(_QWORD *)(v15[0] + 16) + 8i64) + 8i64),
          (v5 = (char *)malloc(v16)) != 0i64) )
    {


First, the file is being opened and validated based on their own custom header setup. This expects their custom header otherwise it's simply closed and ignored.

The first 4 bytes are checked for: 0xEFDA171D
The next 4 bytes are checked for: 0x694C5153 (This is SQLites default header being checked, to see if this is encrypted.)

Based on that info, it does look like they have intentions to support unencrypted database files with their custom header prefixing the file later on.

Next, a blank database instance is opened in memory via the sqlite3_openDatabase call here:

Code: Select all

sub_1435B4550(":memory:", v15, 6i64),


Following that, they grab the block of memory the database is at and allocate the initial page of memory to be used while walking the file to decrypt it:

Code: Select all

          v4 = **(_QWORD **)(*(_QWORD *)(*(_QWORD *)(v15[0] + 16) + 8i64) + 8i64),
          (v5 = (char *)malloc(v16)) != 0i64)


The header of the file is in the following format if the 2nd uint32_t is not the normal SQLite3 header:

Code: Select all

struct header_t
{
    uint32_t Signature;
    uint32_t Parts;
    uint32_t PartSize;
};


Parts is the number of 'chunks' inside of the file to decrypt individually, while PartSize is the size of each chunk.
In the example you gave, the file is:
- Parts: 5444
- PartSize: 1024

This info can also be used to validate the file.
Take (Parts * PartsSize) + 0x0C and it should equal the total file size.

v15 at this point is pointing to an empty initialized instance of the sqlite3 object structure. You can see that here:
https://github.com/sqlite/sqlite/blob/m ... nt.h#L1493

(Note, this is not the same revision so it may not align to the one in this program.)

Next:

Code: Select all

      v6 = 0;
      byte_14BD466A1 = 97;
      byte_14BD466A0 = 37;


v6 is the parts step count while the other two byte values here are the xor keys initial values.

Code: Select all

        while ( 1 )
        {
          ++v6;
          v15[0] = 0i64;
          sub_1435D7010(v4, (unsigned int)v6, v15, 1);
          v7 = v15[0];
          sub_1435D8890(v15[0]);
          ++*(_WORD *)(v7 + 42);
          if ( fread(v5, v16, 1i64, v2) != 1 )
            break;


This is where the game is allocating memory and updating the initialized database object returned in v15. They are creating pages of memory from the file based on the Parts and PartSize information as they step over each part. These are populating the sqlite3 structures 'pVdbe' variable information from what it looks like as its stepping through each part and updating the memory blocks.

The initial block of memory used each time we read in a new part is first read into v5, which was allocated when the file header was validated.

Then that data is copied into in-memory database information as its processed with 'sub_1435D7010' and 'sub_1435D8890'.

The fread here is reading the current part from the file. (v16 being 1024 from the header's PartSize value.)

The next part is where the actual decryption and processing happens:

Code: Select all

          v8 = *(_QWORD *)v7;
          v9 = 0;
          if ( v16 > 0 )
          {
            v10 = &v5[-v8];
            do
            {
              ++v9;
              v11 = v10[v8++];
              v12 = byte_14BD466A0 & 7;
              v13 = byte_14BD466A1 ^ byte_14BD466A0++ ^ v11;
              byte_14BD466A1 += byte_14AF73848[v12] + 1;
              *(_BYTE *)(v8 - 1) = v13;
            }
            while ( v9 < v16 );
          }
          v14 = *(_QWORD *)(v7 + 32);
          sub_1435D91F0(v7);
          sub_1435B52C0(v14);
          if ( v6 >= v17 )
            goto LABEL_13;


The first part is preparing the in-memory database page to align to the part being processed so that when completed, the pages are all prepared and ready for use as an actual decrypted database.


Code: Select all

            v10 = &v5[-v8];
            do
            {
              ++v9;
              v11 = v10[v8++];
              v12 = byte_14BD466A0 & 7;
              v13 = byte_14BD466A1 ^ byte_14BD466A0++ ^ v11;
              byte_14BD466A1 += byte_14AF73848[v12] + 1;
              *(_BYTE *)(v8 - 1) = v13;
            }
            while ( v9 < v16 );


This is the actual decryption part and is walking over a set of 3 keys to mutate the xor as each byte is processed.

The first value is taken from the encrypted part read from the file, as it steps each byte from this read block. It then does some basic xoring while mutating the keys and then writes the xor'd value to the current pages memory for the in-memory database.

The keys are:
- byte_14BD466A0: 0x25
- byte_14BD466A1: 0x61
- byte_14AF73848: { 0x02, 0x03, 0x05, 0x07, 0x0B, 0x0D, 0x11, 0x13 }

These keys ARE NOT reset when stepping to the next part of the file, it is kept from the previous mutation.

The last part during the loop is:

Code: Select all

          v14 = *(_QWORD *)(v7 + 32);
          sub_1435D91F0(v7);
          sub_1435B52C0(v14);
          if ( v6 >= v17 )
            goto LABEL_13;


This is cleaning up the temp information, adjusting the linked list of the in-memory database pages and checking if we've hit the part count to stop iterating and close the file.

When completed, the database is decrypted and rebuilt, part by part, in-memory to be used by the game to avoid any on-disk resources, I/O or potential information leak. Based on how this works, you can dump the database from memory as well right after it's done looping the pages. Simply walk the database objects pages that are exposed inside of the virtual link list being used in the 'pVdbe' field and you should be able to see all of it decrypted too.

Attached is the decrypted database. If you want to donate since you put a bounty on this, my Paypal is:
https://paypal.me/atom0s

You can use the latest version of SQLite Browser to open and view it.
atom0s
Posts: 250
Joined: Sat Dec 27, 2014 8:49 pm

Re: [UE4] Mortal online 2 .db file. please help

Post by atom0s »

Since I didn't really look at the db after decrypting it, it was brought to my attention that the data inside of it is also encrypted. The inner encryption is done differently with a different mutation/key setup. I won't post about that for now since the game is new and I'm sure a bunch of people are going to want to just try to sell information for the game. However, I will post a fully decrypted version of the db.

The database attached is ~1MB smaller than the previous due to being imported into a newer SQLite version and dumped with that newer version. So there's a lot of overhead from the old version removed. (The game uses 3.7.x while the latest is 3.35.5)

The latest version of SQLite Browser should be able to open and view the file without issue. The file is rebuilt via a tool I wrote to dump, decrypt and rebuild the original db with. Both the table creation and indexes should align properly etc. Just the fields are decrypted in this version.
aluigi
Site Admin
Posts: 12984
Joined: Wed Jul 30, 2014 9:32 pm

Re: [UE4] Mortal online 2 .db file. please help

Post by aluigi »

@froggins
Please don't change the title of the topic.
If it's solved for you, it isn't for others who are searching the same.
This is a forum.
Thanks
underPorpoise
Posts: 1
Joined: Tue Aug 23, 2022 9:56 pm

Re: [UE4] Mortal online 2 .db file. please help

Post by underPorpoise »

atom0s wrote:Since I didn't really look at the db after decrypting it, it was brought to my attention that the data inside of it is also encrypted. The inner encryption is done differently with a different mutation/key setup. I won't post about that for now since the game is new and I'm sure a bunch of people are going to want to just try to sell information for the game. However, I will post a fully decrypted version of the db.

The database attached is ~1MB smaller than the previous due to being imported into a newer SQLite version and dumped with that newer version. So there's a lot of overhead from the old version removed. (The game uses 3.7.x while the latest is 3.35.5)

The latest version of SQLite Browser should be able to open and view the file without issue. The file is rebuilt via a tool I wrote to dump, decrypt and rebuild the original db with. Both the table creation and indexes should align properly etc. Just the fields are decrypted in this version.

I hate to necro this thread, and if it is against the rules please let me know, but I would like to add some commentary and ask some questions. Reading through this DB, it seems it is for Mortal Online 1, at least the way things are named. This either means you may have mistakenly ripped the MO1 DB, or that the Dev team is incredibly lazy in their implementations. My intial thoughts were that the former is unlikely and the the latter is VERY likely. Upon even further reading into the DB, it seems it is entirely the MO2 DB, and the devs are quite bunch.

My background is in physical simulations, and not dealing with a lot of the cool stuff here, so I am trying to follow along and dump the db myself but it is quite daunting. Very transparently, I am equally interested in the challenged and the resulting in game knowledge. In reading the database, I am still trying to decipher what game items drop from what/where, and in making my own db rip I am wondering if I would need to make use of a vm or someway to bypass EAC.

EDIT: Perhaps the age of the DB relates to its inconsistency with the current state of the game.
atom0s
Posts: 250
Joined: Sat Dec 27, 2014 8:49 pm

Re: [UE4] Mortal online 2 .db file. please help

Post by atom0s »

The above db is for MO2 very early on in the game's lifespan. They may have copied a lot of what was in the first game or whatever, but it is definitely for MO2. I still actively decrypt the database for a few users that contact me on Discord each time MO2 is updated. (I don't play the game or have any of the files, those users send me their db file and I decrypt it for them etc.)