I’ve been meaning to write about this hack for a long time (roughly three four years, but I’ve been crazy busy. Like brah, you don’t even.) So some background information about myself. I’m a Full Stack Web Developer, and I consider myself a technology enthusiast. I’m also a gamer, although I play more casually nowadays. There’s a MMO-style game that I’ve been playing on and off over the years called Dungeon Fighter Online or Dungeon & Fighter in South Korea (its country of origin).
It’s a beat em up styled game that just happens to have a fairly well implemented PVP system ( that the population at large doesn’t play but I’m not going to rant about that here. ) So anyway, in 2016 ( I think ), the game released a PVP ranking system with an in game ranking board.
Now I just happen to be messing around with Wireshark and I happened to have it on while looking at the ranking system in game. I looked at the logs later and I discovered that they were sending a JSON payload of information about the players to the game client out in the open, unencrypted.
In fact, there were a series of end points that you could access via url to get the AWS server the db holding the pvp information to respond with specific information. Upon discovering this, I thought to myself ” I could probably recreate the ranking board system outside of the game and have it update daily using a simple cron job.” I took the time to look over the json payload from each of the end points and started to get to work. Normally I wouldn’t expose the endpoint that I accessed to get the data but I don’t really know if it even works anymore so I’ll even talk about accessing the endpoint.
The base domain for accessing the pvp information happens to be a staging server for the games website. The website in question is dfoneople.com, and the staging url is static.dfoneople.com (which isn’t a big secret if you go to the sub reddit for this game.) The endpoint rank/cain/rank/rank_data_[8 digit date here]_[class_id].json (e.g. static.dfoneople.com/rank/cain/rank/rank_data_20200112_1.json.) The 8 digit date is formated as follows (“yyyymmdd”). You can also get the data for all class at once by using the word ‘all’ in place of a class id. If you enter that url pattern into your browser you will get a json payload for all the game’s pvp ranking data for that date.
Since this all accessible outside of the game and I was invested into pvp at the time I wanted to create a interface that allowed people to view the pvp data outside of the game. Plus it seemed like fun figuring out what the hell this json data actually meant! I know, I’m a huge nerd.
Here’s a sample of the pvp json payload, using the very same date I just used for the example link above.
In that pastebin you will see hundreds of records of different pvp players and their current placement in the pvp system. A player’s entry is represented as the following structure:
{ “rank”: 1, “job_day”: “2020-01-12”, “server_info”: “1”, “m_id”: “131352”, “charac_no”: “1268545”, “charac_name”: “Axtea”, “job”: “11”, “grow_type”: “33”, “normal_rating”: “1519”, “pvp_grade”: “6”, “pvp_grade_ext”: “9”, “last”: 1 }I’ll try to give an explanation of what this structure means:
- Rank
- This is the rank that this character has in the ranking system, we are currently looking at rank 1 overall.
- Job Day
- This is the date the information was recorded, think of it as “datetime”
- Server Info
- This is the id of the server the information was recorded on, in the version of Dungeon Fighter Online that I play, there is only one server for the entire playbase. So this value isn’t really important for us.
- “M” Id
- I honestly don’t remember what this relates to, I’m certain I knew what this was years ago but I can’t recall right now.
- EDIT: 8/25/2021. Finally figured it out. “M” Id is the id of the account that this character is under. So you can find out how many players are actually the same person in pvp.
- Character No
- This is the global id for the character in the game. This is never shown in game, it’s also never shown outside of the game. It’s data hidden from the user at all times.
- Character Name
- This is the name of the character in game.
- Job
- This number refers to the job class (or more likely job subclass) the character has progressed to. This is tied to the grow_type attribute we will talk about next
- Grow Type
- This number refers to the grouping of global sub-classes. It’s not obvious when you look at the data at first. There are 4 grow types: 33, 34, 35, 36. 33 is tied to every 1st subclass in the game, 34 is tied to every 2nd subclass, etc and so forth.
- Normal Rating
- Normal Rating is just the rating associated with the character is ranked pvp. Rating in this game is used in the match making in order to pair you with people who should, in theory, be on the same skill level as you.
- PVP Grade:
- This refers to your pvp experience rank, each rank generally has a name and graphical icon associated with it. I’ll take about this later but to give you a brief idea as to what this would look like: Grade 1 would be Bronze 1. Bronze 1 is associated with 1 bronze medal. Bronze 4 has 4 bronze medals.
- PVP Grade Ext:
- This refers to your grade the prior day, if you weren’t on the list yesterday then this would show up as 0. 0 is the same as the Novice grade.
- Last:
- Your previous pvp rank from yesterday
Once I figured out most of the data I was able to write a small database to normalize the json structures.
There were a couple of fields I had to add to the database in order to better simulate the ui in game (mainly getting the icons for the ranks and classes ) and I created a key value pair relationship for the grow-type: job ids and made small json structures for each corresponding sub class type (at the time, there are at least twice as many classes in this game now! )
Now that I had a db schema in order to hold the data, I could now start working on the fun part. So in order to pull the data from the server I had to write a script in php that would be able to be called from the command line interface. In the folder that I start to collect the json files for pvp records I store the following script:
I know the code itself is not clean looking, I wrote this as a quick and dirty script that I want to go back and refactor later but it gets the job done. Let’s talk about the script real quick:
In the section above I’m setting the timezone to EST because the script itself is depended on being in the right time zone in order to pull down the json files. I have the variable $currentDate set to the todays state using the DateTime function built into PHP.
The $url variable is set to the url used to obtain the pvp json data exposed by the static.dfoneople.com domain. $initialYear is set up to create a date object to get the difference between the $currentDate and today’s date (this is because $currentDate can actually be anyday set by the script as seen by some committed lines.) $formatedDate is the $initalYear date formated for file name creation later on in the script.
The above code potion of the code loops through the different between $currentDate and $initalYear that is stored into $dateInt and $testNum. I’m doing a comparison between $testNum, that starts at 1 from the previous section I discussed and $dateInt + 1. I add the plus +1 because $testNum starts a 1, I could just have $testNum start at 0 and remove the +1 but as I said before this was quick and dirty and some parts about this script I just wasn’t thinking about. So, within that for loop I check to see if the file we want currently exists.
If it does, then we don’t process all the associated files for that day. I mean, why run a process twice? Afterwards, within the if statement I grab the file contents from the static.dfoneople.com site for the _all.json file for that day, write it to the /json folder and then close the file. Afterwards I do a for loop for the related subclasses in the game. within that for loop I go through the same process I did with the the all.json file. The only thing I don’t do there is check for the existance of the sub class file on the server already. Chances are if I don’t have the _all.json file for today then I don’t have the subclass files.
After the loop, I increase the $initalYear date by 1 day and save it ot the $formattedDate variable. Now within this script this step doesn’t do anything, but this is actually a holdover from a more general script I wrote to grab all files starting a particular date, remmates can be seen of this script through some of the comments at the top.
I set this script to run around 12pm or so via a cron job, after it runs it triggers another script to insert them into the database. That script is the following:
So this script inserts into the db schema that I created earlier in the post. Let’s take a look at the script in greater detail:
The top portion of this script allows the errors to be displayed for debugging purposes and set’s the timezone. Afterwards I establish the database connection using the mysqli api built into PHP. From there I create a date object based on today’s date and save it to the $currentDate variable.
Then I set up $testNum and $idx and incremental variables, and $formatedDate being the string formatted $currentDate representation. I then grab the all.json file we created in the previous script, convert it to json and then print it out as confirmation for the cron output.
So this portion of the script inserts information from the _all.json file into the database. After the entire file is read, I commit the data into the database and then I begin to loop through the subclass files to pick up any rankings for classes I didn’t get in the _all.json file. Then I commit that data to the database and close the database. After the data is loaded into the database I can now display the data on the front end.
So I create the front end of the application and it looks something like this:
It’s currently hosted at superarmor.net, this will change in the future mainly because I plan on using that url for something else. Like I did with the other two scripts I wrote I’m going to post the three files used to make all of this work. I’m not going to talk about them at length because the code is a mess and it makes my soul hurt. Later when I refactor this project I will go into length. For now, here is the raw code for the index and two ajax files.
You can take a look at them if you want but I’m not going to go into detail for the last three files. So yes, I did all these because I was curious as to if I could build a service out of data I discovered from packet sniffing. Some people go out on Friday nights, I code for some reason. :/