So since quitting the game Blade & Soul I have decided to do a write up about my methodology that I used in finding the final end-point function for handling skill global cool time or Global-Cool-Down (GCD) for skills. I stepped on someones toes when I released this free to the public and have been accused of ripping them off and “copy pasting”. If only it was as simple as that, the truth is I had GCD months prior to their “private” paid cheat/tool/whatever was released. Only at that time it was in the form of editing the games core configuration file (datafile.bin) located in xml[bit].dat, I myself personally have no use for GCD edits and never bothered with it till after deciding to put a stop of the “private” version being abused in 6s and other things and give everyone an equal opportunity to be on the same footing.
The xml[bit].dat is what you would call a main compression file containing tons upon tons of configuration options for various things and function within the game. Inside that file is another compressed file known as datafile.bin, a more binarized version of xml.dat which contains the really juicy stuff for core configuration of the game.
Now reading the datafile.bin is not as easy as decompressing the main archive, it’s as it suggests, binarized. It was a low-effort deterrent to keep public eyes from seeing its content while also compressing the archive down even more than it already is.
Once de-binarize and decompressed, it will reveal all of it’s dirty secrets. I won’t go into explaining how to do that because I myself didn’t do it personally but I knew people that had more interest in that file. I’m a pretty lazy person and couldn’t be bothered to go through that process. I am more interested in what’s happening inside the engine it’s self.
Moving on, inside the datafile.bin contains tables for all skills in the game including AI/NPC/Boss skills. In there it has various fields and flags set telling the client what the skill is, what animation to call, class it belongs to, cast time, cast delay etc etc on top of Global cool-time between other skills. Most skills in the game share a group with other skills and this group is controlled by each skills global cool-time between next execution or consecutive use.
If skill 1 is cast, skill 2 cannot be used for 400 milliseconds. If skill 2 is used then skill 1 cannot be used for 1,000 milliseconds or 1 second. If skill 1 is cast but cooldown is 0, still prevent consecutive use for 400 milliseconds. Skill 3 is not affected by Skill 1 or 2 because it does not share the same group, therefor it can be activated at the same time as either Skill 1 or Skill 2.
Now with this knowledge, we know this table is going to be parsed and stored in the games memory, because why keep pooling I/O requests when you can keep the data parsed in the memory. So how did I go about finding this table in memory? Well this table is containing information about what the particular skill is and all functionality for the skill. So with that it should be obvious that the skill ID will be used to link it all together because that is how ID based key systems work. So I peaked around a few skill ID’s that were fairly unique and by unique I mean not something you would see a billion entries for when searching the memory. I eventually located a skill that had roughly 30ish results for the exact integer I was searching for.
Once narrowing the list down we start to inspect the memory region, having a rough idea how the table looked I kept going down the list till I encountered a sector that resembled what would be the table I am looking for, went down a bit in the section and found the GCD value along with group ID and variant-id. So what do we do now? Well it should be obvious that the game will have to do a read access at the address of the GCD value to know what the delay is so I just checked what was reading the address and started to cast the targeted skill.
Eventually got a hit and inspected the function that was doing a read. At this point I arrived at a massive routine/function that was loading the 4-byte integer and converting it to a floating point (400 / 1000 = 0.400000). From there on it’s a matter of booting up IDA pro and generating some pseudo C code to get a better understanding of what is going on in this routine. Eventually it led me to a call which was taking (x64) 4 arguments, three of which were the registers (variables) for skill ID, group and GCD.
Now I could go back to what was originally reading the GCD value and make changes there before it converts it into a floating point, which I did.. at first. Or.. with the curious mind that I have see where that call takes me and investigate what it is doing, which is what I eventually did. So same deal as before go to routine/function in IDA pro, generate some pseudo C code and get a better idea of what is going on. Inside this function I seen even more math and conversions being done to the GCD value which was sent as a floating point of 0.400000, making my way down the function I eventually realize what was going on and put two and two together.
Where I had ended was a final end point in the clients side where the skill is parsed, delay is calculated based off response time of server/=client. Now if changing the value of the skill from 400 to 200 in the bin was good enough then why not change the floating point value sent here from 0.4 to 0.2 before it starts doing its calculations? I mean this is a function taking an argument, why not just hook and change the argument before it starts doing stuff with it.
Yes, after intercepting and changing the arguments passed it worked. Here is the final result of what I do with this function, using MS Detours to detour the function to my own function and doing a bit of manipulation with the data then proceeding to continue with execution flow so that everything functions as normal and we don’t crash the client.
I use pugixml to read an xml document that I have stored in the memory which contains various configuration options and lastly custom GCD for skills. Now here is the pattern I used for finding this function to hook on the 64-bit client. This pattern is a valid working pattern for NCWEST Patch 156-173 (173 being current as of the time of this post). This function has received no alterations to its code and the compiler has optimized & compiled it the exact same way for the last 20 or so patches, quite possibly since the creation of this function when they refined the GCD handling in the game years back.
48 8B C4 48 89 58 08 55 56 57 41 54 41 55 48 81 EC C0 00 00 00 0F 29 70 C8 0F 29 78 B8 8B 01 85 C0 41 8B F0 4C 8B EA
Then it was just a matter of repeating the same process on x86 (32-bit client) to get it working for that client version. Anyone experienced with C++ can tell that this is done in a very lazy way and again I am a lazy person, I am also not proficient in C++ and this is the first time using the language since 2013. It’s definitely not the most efficient but it is fast enough too\ keep up and have not noticed or heard issues of a degrade in performance when casting skills. It could be cleaned up to take the parsed XML data from pugi and storing it in its own table/vector and running checks against that for pulling the custom GCD table.
The claims of “ripping off” or “copy pasting” is quite ridiculous. At the end of the day it comes down to pettiness because I made it free for everyone and people just want to back up their king. It’s fine if you want to support someone anyway you can, I’m all for that and I have always said support those that make stuff you enjoy, but don’t go blindly slinging your shit around like a monkey, we’ve evolved past that phase thousands of years ago. Educate yourself and have all the facts. GCD in BnS is not special nor that difficult to find if you have an idea of what you’re doing and what to look for. A lot of the things done in BnS via plugins, cheat tables or just cheats in general can all be traced back to the datafile, nothing is entitled to a single person, it’s all fair game.
I’ve been around the block more then I want to admit, I’ve seen and experienced pretty much all of it so I only offer my wisdom onto you, the reader. You can be the first person to do something but don’t ever think you’re the only person that can do it, there is always someone else. The faster you learn that the better off you’ll be for future endeavors, let no stone go unturned, take every opportunity life throws at you, don’t waste it like I have. Enjoy what you do, take pride in it but don’t get lost in it. Your ego and overall attitude can be the downfall of you.