Results 1 to 9 of 9
  1. #1
    VXP's Avatar
    Join Date
    Apr 2012
    Gender
    male
    Posts
    11
    Reputation
    18
    Thanks
    3
    My Mood
    Breezy

    Wink Official King of Kings III Reverse Engineering Thread

    Welcome!

    To my official King of Kings III reverse engineering thread!



    So a little backstory to this project. A long time ago, in a virtual galaxy far, far away, an MMORPG was released, developed by Lager Interactive (Thai) and published by GamigoAG.
    When the game first released, the level cap was 50. When I started playing, the cap was 140. In the end, the game died with a cap of 215, with a rumored Thai only version cap of 260 including more items, gear, and zones to explore.
    Of course, as with any game, I wanted to hack it or cheat in some way. I was never successful while the game was still available.
    However, I have a copy of the most recently updated game files, luckily I still had a copy on an old hard drive. I even have a copy of the 140 cap version of the game! When I first started playing, the community was amazing, staff were amazing, and even though the visual aesthetics weren't flashy-flashy, it was the community and content that made it one of a kind. As the years went by, staff began to neglect the game, the developers over-developed content and updates, and eventually the community started to evaporate, leaving behind only the most dedicated and hardcore fans. I was one of them. As the final month of the game's life approached, the publisher decided to have a "farewell" party for all the remaining players. Free in-game currency, massive level boosts and plentiful events made for one hell of a time, it also solidified a very fond spot in my heart as I was ultimately forced to accept the game's death as I was disconnected from the server due to it being shut down. They warned us with an in-game message box, a chilling message, "Server shutdown in 1 hour", and man did I party hard till the end. We all did. To be honest, not even breaking up with a girl hurt my heart as much as the "Failed to connect to server" message did when I tried to log in again. Ever since then, I swore I would bring the game back to life, however difficult.

    So here we are. Right here. Right now (and however long it takes ).
    I will make this known right now: I'm very determined. I have a decent knowledge of C++ and currently I'm looking in to DirectX development. I want this revamped version to support DX9, 10, and 11, along with OpenGL.

    Things to note:
    • I have access to all the game content. I've been working at this project for 2 weeks now as of this post and I have made a decent amount of progress. My biggest issues are dissecting the data files and writing the game engine.
    • I have all the time in the world, if anyone here would like to help me in this project, it would be much appreciated. I do not know how to program a game server, however, as with learning to develop with DirectX, YouTube and Google are my best friends and I'm not afraid to search thoroughly before I ask.
    • I do NOT want to make money off of this project. I want to develop a main server for everyone to connect to, along with a secondary, special "private" server application that allows people to download their character from the main server.
    • I also want to develop a single-player mode with a different story line and Easy/Medium/Hard difficulties.



    Section for Updates:

    May 22 2018 - Including all work done previously:
    • Identified file: (.dfm) "Form" file, unknown format, contains mixture of XML/HTML like structures
    • Identified file: (.nut) "Squirrel" script file, C++ like syntax, found packed in LPQ files.
    • Resources: 1 (.swf) Shockwave Flash file, I've never seen it in-game or during game operation, many (.LPQ) files (seem to be related to LagerPacketOMF.dll), many (.bhl) files (seem to relate directly to LPQ files)
    • LPQ files seem to be packed data files, unknown format, in many places the data remains uncompressed and is still readable, Squirrel script can be identified in some places along with XML/HTML-like markup.
    • If you corrupt an LPQ file and force-load WE.exe, it creates a crash report dump which reveals file names that could not be loaded. I am still working on creating dumps.
    • I have a memory dump of WE.exe where various in-game text, objects, scripts and other data can be identified
    • The string table that Cheat Engine creates of WE.exe reveals bits of strings that can be identified within LPQ files.
    • When loading WEbug.exe in IDApro and running in a disassembler, various (.nut) files can be identified by name. WEbug.exe is also the debug form of the main WE.exe
    • Within the LPQ files, if you look at the data it makes patterns within each line. Various string similarities can be seen such as:
    • WlT-µ’!
    • blTµ’!
    • UlT/µ’!
    • PlT*µ’! -- Without any data context, it's clear that these 4 distinct lines begin with similar string content (mind you there is much more to each line, those are just the first 7 characters of each line)
    • I'm confident in my ability to decode the strings manually, however help would be awesome considering all the LPQ's add up to over 1GB
    • There is also an internal "file system" so to speak, with folder roots and branches, which all have respective data, such as LIGHTS\, INTERFACE\, etc as root folders. I'm not fluent enough to understand how one might go about determining how to identify these paths.


    May 23 2018
    • Upon using PE Explorer and opening Login.exe (game client update application), I found a section identifying (.dfm) script. It seems to be line-based script, images within a (.dfm) are encoded as long strings of numbers. It's not base64 code, but I can't identify it yet. PE Explorer can view them fine and they're easily saved.
    • String map of WE.exe while loaded is very useful, however, it doesn't help to have a file name without file data or knowing where I might find the data.

    --- End Section ---

    *** THIS IS A C++ PROJECT ***

    I will be glad to share game data files/custom source files for research over some platform like Steam (preferred). PM me for my information.

    Well, I hope this project gets rolling as quick as possible. I'm utterly DYING with heartache to play this game again and I'd love to make anyone that helps part of the admin team
    Thank you for reading my project!

    *** I JUST FOUND A CHINESE WEBSITE HOSTING THE GAME FILES + SERVER + OPEN REGISTRATION ***
    *** IM SO HAPPY RIGHT NOW YOU GUYS DON'T EVEN UNDERSTAND ***

    Last edited by VXP; 05-23-2018 at 06:11 PM. Reason: Update

  2. The Following User Says Thank You to VXP For This Useful Post:

    Dama (07-01-2018)

  3. #2
    VXP's Avatar
    Join Date
    Apr 2012
    Gender
    male
    Posts
    11
    Reputation
    18
    Thanks
    3
    My Mood
    Breezy
    MAJOR BREAKTHROUGH!

    I will now be able to show some significant progress.

    LPQ files have been interpreted up to 50%, that is to say, I understand how they're structured, and how the game loads pieces of every one.

    LPQ Structure:
    LPQ files do not seem to be compressed, more like they are simple files that store data files, and have the data inside encrypted.

    Header (4bytes) 0x0+4, Magic Number (4bytes) 0x4+4 (total 8 bytes)
    Code:
    4C 50 51 1A 20 00 00 00
    ALL LPQ's MUST have 0x4+4 as
    Code:
    20 00 00 00
    Various "End of Data"-like tags

    DFM script file header:
    Code:
    {object
    DFM script first line mandatory:
    Code:
    {object "<DFM form name>" : GUId,
    All files inside LPQ file structure have unique 4 byte endings post file. Maybe file separator or key for decryption.

    config.lpq corrosponds to config\ directory.

    Unique findings:
    If the game locates a required file on disk, it will load the disk version instead of the LPQ version, and the more important finding is that when reading from disk based content, files must be in original format (ex jpeg must remain in jpeg format, not LPQ encrypted internal data).

    Currently have changed loading screen, and main login screen.

    Currently: Analyzing more DFM scripts in encrypted format to determine range of data and possibly chunk frequencies.

    Tested using non-LPQ custom written resource FormInput.dfm in Interface\VerENG_DFM\ with
    Code:
    {object "FormInput" : GUId}
    with no exception error thrown.

    If DFM files do not follow a certain format or syntax, you will get a game crash with log.

    - - - Updated - - -

    Code:
    // Currently identified file structures:
    \{l}\ //Unknown exactly
    \{l}\win_b64\
    \{l}\win_b64\code\
    \{l}\win_b64\code\bin\
    \{l}\win_b64\code\bin\sginfra.dll
    \config\ //Configuration scripts/data
    \config\chainefftbl.txt
    \config\MountBornEffect.txt
    \config\Tahoma //Unknown unique file
    \config\WebLink.txt
    \config\WebLinkConfig.txt
    \ErrorReport\ //Game engine memory dump + debug output txt dump
    \gkk2.tmp\ //Unknown game engine temp folder, btw see Sec. 2
    \Help\ //Game help dir (web browser based help, also entirely in chinese)
    \Help\index.htm
    \Help\main.htm
    \Help\menu.htm
    \Help\Images // Too many files to list
    \Interface\
    \Interface\gcGeneral\
    \Interface\VerENG_DFM\ //Too many files to list
    \Lights\
    \map\
    \map\50_2.dds
    \Mesh\
    \Mesh\Article\
    \Mesh\Building\
    \Mesh\Nature\
    \Mesh\Treasure\ //Too many files to list
    \Photo\ //Game screenshot save dir
    \Shore\
    
    + various root files
    Section 2:
    I found the very first game's installation files. It seems to be the GKK engine (G King of Kings). G is unidentified at this time.
    King of Kings 3 uses King of Kings 2's game engine, called GKK2.
    GKK and GKK2 work entirely different. GKK has all resources as resources in DLL files, where GKK2 all resources can be either referenced through their true directory location, or in the LPQ library. GKK2 will prioritize loading of true location files before the LPQ library. This allows us to create custom files and view their effect on the game.

  4. The Following User Says Thank You to VXP For This Useful Post:

    Dama (07-01-2018)

  5. #3
    VXP's Avatar
    Join Date
    Apr 2012
    Gender
    male
    Posts
    11
    Reputation
    18
    Thanks
    3
    My Mood
    Breezy
    Update 8/24/2018

    I have successfully infiltrated the game's engine. Here's a raw breakdown of what's going on:

    Game uses early 2000's level of sophistication, however they included the debug executable. Anyone remember Dead Island's issues? So what this means for us is they screwed up. Big.

    WE.exe file size: ~6MB
    WEbug.exe file size: ~14MB [8MB+ of debug information, bigger than the main executable itself!]

    So now in correlation to my previous post, -I have successfully grabbed the engine by the balls here-, and executed the reverse.

    Let me introduce you to Squirrel script injection!
    Squirrel syntax/dictionary found here: https://www.squirrel-lang.org/
    Squirrel Wiki: https://en.wikipedia.org/wiki/Squirr...ming_language)
    The game will look for content on disk first. If there is no content, it will look for the exact immediate file necessary in the local LPQ. However, if the file IS present, and the file is any file of the required type and in proper format, so mp3 must be a valid mp3 file, tga/bmp/tiff/png/dds, and of course, DFM forms and Squirrel NUT scripts - which just so happen to be plain text, hooray -, it will use it and load it.

    So, now that we can do script, music, image, form, light, mesh and text injection, we can start doing whatever we want.
    I have identified a very important instruction too, SqRunFile(string);

    By looking at configupdate.lpq you can find ScriptInit.nut at offset 15,735,785. File size in LPQ is 812 bytes.

    I've done a test, I created a ScriptInit.nut in my game's config\ folder and gave it these lines:

    ScriptInit.nut
    Code:
    // This is a comment, fuck the developer fuckers
    // Welcome to the jungle
    
    SqRunFile("config\\test.nut"); // test.nut is a custom foreign .nut file
    test.nut
    Code:
    // Scoopty woop
    //
    
    SqRunFile("config\\uiSkinFormImageSet.nut"); // This is actually found in ScriptInit.nut in the LPQ
    So if I successfully reverse out the LPQ ScriptInit.nut and populate a file on disk, I would be able to load the game as normal.
    However, by testing my files on disk as above, the game still runs, but when viewed in Process Explorer, a lot of files aren't loaded because
    our ScriptInit.nut doesn't have all the necessary information it needs. The LPQ ScriptInit.nut loads at least 20+ other .nut files, all of which don't have to have a constant name of uiSkinFormImageSet.nut, why, because we can specify a specific file in our script which could be FooBar.nut for all the engine cares and it'll load it, and FooBar could potentially do what it wants.

    It's looking better and better as time goes on.

  6. #4
    VXP's Avatar
    Join Date
    Apr 2012
    Gender
    male
    Posts
    11
    Reputation
    18
    Thanks
    3
    My Mood
    Breezy
    9/30/2018 - END OF THE MONTH PROGRESS UPDATE

    It's not because it's the end of the month, but I just so happened to make a breakthrough today.

    I can finally announce a tool that is well on its way to performing operations on LPQ files.
    However this time, I skipped the idea of DLL injection because that seemed like too much of a hassle, plus run-time and linking wise it was a nightmare.
    So what I've done now is created a tool that can actually call functions from LagerPacket.dll
    I've tested my tool with the Dragon God Resurrection and Divine Rebirth versions of the game. It seems like they didn't change LagerPacket.dll between versions so that's excellent for us.

    Ok now to the important part:

    lpq_tool.h
    Code:
    #ifndef LPQTOOL_H
    #define LPQTOOL_H 1
    #endif
    
    
    // Global Variables
    //
    struct LpqStruct
    {
        std::string header, buffer, data;
        const char* fname;
        char* cdata;
        bool validlpq;
        
        long read, fsize, offset;
        
        std::fstream* f_plpqfile;
    };
    
    
    // Function Prototypes
    //
    bool GetRightOf(std::string, std::string);
    
    
    // Function Definitions
    //
    bool GetRightOf(std::string o, std::string s)
    {
        int len = s.length();
        std::string a = o.substr(o.length() - len, len);
        
        if(a != ".lpq")
        {
            std::cout << " >>";
            return false;
        }
        
        std::cout << "\n";
        return true;
    }
    lpq_extractor.cpp
    Code:
    #include <iostream>
    #include <string>
    #include <fstream>
    #include <windows.h>
    
    #include "lpq_tool.h"
    
    // Prototype our class functions for later use...
    // -- NEEDS TO BE POPULATED MORE --
    class ILagerPacket
    {
        public:
            ILagerPacket(){};
            ~ILagerPacket(){};
    };
    class ILpqSystem
    {
        public:
            ILpqSystem();
            ~ILpqSystem();
    };
    class ILpqPatchBuilder
    {
        public:
            ILpqPatchBuilder();
            ~ILpqPatchBuilder();
    };
    class CLpqPatcher
    {
        public:
            CLpqPatcher(){};
            ~CLpqPatcher(){};
    };
    
    // Create function pointer references for later use...
    typedef unsigned __int32 (__cdecl *ICalcLpqCheckSum)(void*, unsigned __int32);
    typedef int (__thiscall *CCreatePatchFile)(void*);
    
    int main()
    {
        using namespace std;
        
        /*  ~~ For later use... ~~
        ILagerPacket LagerPacket;
        ILpqSystem LpqSystem;
        ILpqPatchBuilder LpqPatchBuilder;
        CLpqPatcher LpqPatcher;
        */
        
        // Load LagerPacket.dll
        HINSTANCE hLagerDLL = LoadLibrary("LagerPacket.dll");
        // If that failed..
        if(!hLagerDLL) {
            cout << "Could not find or load LagerPacket.dll\n"
                 << "Maybe LPQ_Extractor.exe isn't in your game's path?";
            system("PAUSE");
            return 0;
        }
        
        // Create CalcLpqCheckSum from DLL using our prototype function pointer from earlier...
        ICalcLpqCheckSum CalcLpqCheckSum = (ICalcLpqCheckSum)GetProcAddress(hLagerDLL, "?CalcLpqCheckSum@@YAKPAXK@Z");
        if (!CalcLpqCheckSum) {
            cout << "Could not locate ?CalcLpqCheckSum@@YAKPAXK@Z\n";
            system("PAUSE");
            return 0;
        }
        
        // Create CreatePatchFile from DLL using our prototype function pointer from earlier...
        CCreatePatchFile CreatePatchFile = (CCreatePatchFile)GetProcAddress(hLagerDLL, "?CreatePatchFile@CLpqPatcher@@UAEHXZ");
        if(!CreatePatchFile) {
            cout << "Could not locate ?CreatePatchFile@CLpqPatcher@@UAEHXZ\n";
            system("PAUSE");
            return 0;
        }
        
        // Pre-run testing to ensure that we can perform operations with LagerPacket.dll
        string buffer = "TEST", testFile = "test.lpq";
        int lpqcsy = 16660;
        void *pBuffer = &buffer, *p_testFile = &testFile;
        
        cout << "CalcLpqCheckSum(void *, unsigned long) returned " << CalcLpqCheckSum(pBuffer,lpqcsy) << " - (" << buffer << ", " << lpqcsy << ")\n"
             << "__TIME__: " << __TIME__ << endl;
        
        // Variables and initialize the LpqStruct structure
        string toolver = "v1.0.0.0", tab = "\x9", lpqname;
        LpqStruct LPQ;
        
        cout << "\n |*| LPQ Tool - LPQ Archive Zip/Unzip Utility - " << toolver << "\n"
             << " |*| Written by Karac Von Thweatt 2018\n\n"
             << " Enter the name of the LPQ file you wish to perform operations on.\n"
             << " NOTE: Must include \".lpq\" extension!\n"
             << " >>";
        
        // Get file name while extension is missing
        do
        {
            // Get LPQ file name in lpqname
            getline(cin, lpqname);
            // cin.clear() so automatic newline
            cin.clear();
        }
        while(!GetRightOf(lpqname, ".lpq"));
        
        // Swap char* to string then open file
        LPQ.fname = lpqname.c_str();
        ifstream LpqFile(LPQ.fname, ios::in | ios::binary);
        // If that failed..
        if(!LpqFile.is_open())
        {
            cout << " Couldn't open " << lpqname << " for input.\n"
                 << " Check to see if lpq_tool is in your game's directory.\n\n";
            system("PAUSE");
            return 0;
        }
        
        // Set up LPQ structure variable
        LPQ.offset = 0;
        
        // Read file into LPQ.data
        LpqFile.seekg(LPQ.offset, std::ios::end);   // Go to end of file
        LPQ.fsize = LpqFile.tellg();                // Report size in bytes
        LpqFile.seekg(LPQ.offset, std::ios::beg);   // Go to beginning
        LPQ.cdata = new char[LPQ.fsize];            // Set size of LPQ.cdata
        LpqFile.read(LPQ.cdata, LPQ.fsize);         // Read data to LPQ.cdata
        LPQ.data = LPQ.cdata;                       // Swap char to string
        delete[] LPQ.cdata;                         // Delete old char
        LpqFile.close();                            // Close file.
        
        // Report size of LPQ in bytes
        cout << " Size of " << lpqname << " :: " << LPQ.fsize << " bytes\n";
        
        // Set void pointer pBuffer to string address
        pBuffer = &LPQ.data;
        // Run CalcLpqCheckSum()
        cout << " CalcLpqCheckSum(); returned " << CalcLpqCheckSum(pBuffer,lpqcsy) << " - __TIME__: " << __TIME__ << "\n\n";
        
        // -- FOR LATER USE --
        // CreatePatchFile(p_testFile);
        
        system("PAUSE");    
        return 0;
    }
    So basically with CalcLpqCheckSum() we can perform a checksum on string data. Still have to delve further into LagerPacket.dll with IDA Pro to figure out just how the algorithm works, whether it uses __TIME__ or not, but I assumed and it doesn't really hurt anything anyway. Until I find out tho I'll leave the time stamp in the output.

    So here's the functions we're looking for:

    CreatePatchFile(void*)
    ExtendPatchFile(void*)
    ProceedPatch(char*)
    DeletePatchBlockFile(char*)
    SetLpqCallback(unsigned long, int (*) (LagerPacket *, int, int))
    // LagerPacket is a class if you didn't already know

    Variables:
    pBuffer, buffer, size - All unknown type; I think pBuffer is int* to &nBuffer
    int nBuffer
    ILpqSystem * g_piLpqSystem
    __security_cookie
    int g_nLpqErrorCode

    EDIT: -- ITS BEEN CONFIRMED THAT LPQ'S ARE COMPRESSED WITH 2008 LZO v0.22 --
    However, further details on compression are unknown.
    Last edited by VXP; 10-01-2018 at 12:08 AM. Reason: Forgot to throw in recently found property of LPQs

  7. #5
    Lionmirage's Avatar
    Join Date
    Dec 2018
    Gender
    male
    Posts
    1
    Reputation
    10
    Thanks
    0

    You are crazy!

    Ehi mate, you must be crazy! I always had your same idea but never really got to it! Totally with you on this project.
    At what point is it? like is it far from done? I am a PHP developer so is there any part i could help solving?
    Let me know

  8. #6
    Fasthackeromg's Avatar
    Join Date
    Jul 2013
    Gender
    male
    Location
    In your mind
    Posts
    14
    Reputation
    10
    Thanks
    0
    My Mood
    Inspired
    i love you if this happens

    edit: there is another guy working on this, you could make a team https://www.facebook.com/groups/1009866965705001/
    Last edited by Fasthackeromg; 01-05-2019 at 09:36 AM.

  9. #7
    VXP's Avatar
    Join Date
    Apr 2012
    Gender
    male
    Posts
    11
    Reputation
    18
    Thanks
    3
    My Mood
    Breezy
    That guy on Facebook is me lol, also will be updating with new progress report in the coming months. I'm still working on getting an apartment. I have the money but then after that I have to buy a new pc.
    Last edited by VXP; 03-16-2019 at 04:33 AM. Reason: Meant to reply more, am drunk atm (new pc not pic*** urgh)

  10. The Following User Says Thank You to VXP For This Useful Post:

    RobotGamingTV (04-28-2019)

  11. #8
    RobotGamingTV's Avatar
    Join Date
    Jul 2017
    Gender
    male
    Posts
    1
    Reputation
    10
    Thanks
    0
    My Mood
    Busy
    Quote Originally Posted by VXP View Post
    That guy on Facebook is me lol, also will be updating with new progress report in the coming months. I'm still working on getting an apartment. I have the money but then after that I have to buy a new pc.
    Ok so I managed to get a connection to my local server but the ports keep switching >_> Very annoying to have this.
    But atleast I got a connection.

  12. #9
    VXP's Avatar
    Join Date
    Apr 2012
    Gender
    male
    Posts
    11
    Reputation
    18
    Thanks
    3
    My Mood
    Breezy
    There's pretty much one solution to this. Listen to all ports, wait for a connection, then open the connection to the given client per requested port.

    Are you able to serve the Login.exe? If you're trying to write a server you'll need to serve all parts of the game including the update application, at least to serve a worldlist.xml. Beyond that, I still have to reverse the patchfile.lst and figure out exactly how that's working. I believe it to be a type of spreadsheet or CSV where it includes file names, dates, versions and other information relevant to patching out of date files.

    I'm in the process of writing up a full document for development purposes. Writing the server is cool and all, but reverse engineering the LPQ compression is crucial. Those LPQs contain critical client-server communication information which may make it a lot easier to reverse the server from scratch.

    There is also the Chinese server, however I can't access my Chinese KoK3 until I get a USB->SATA adapter to connect my old hard drives to my new pc.

Similar Threads

  1. [Assembly Tutorial] 40 Reverse Engineering
    By radnomguywfq3 in forum Programming Tutorials
    Replies: 26
    Last Post: 03-24-2019, 12:53 PM
  2. [Release] Crossfire > King < Have a look on this thread please :)
    By speedo_edoo in forum CrossFire Europe Discussions
    Replies: 3
    Last Post: 03-25-2016, 11:44 AM
  3. Warface Reverse Engineering, Addresses and Resources Thread
    By _PuRe.LucK* in forum Warface Coding & Source Code
    Replies: 2
    Last Post: 01-30-2015, 06:24 AM
  4. Replies: 0
    Last Post: 10-18-2008, 07:06 PM
  5. Reverse Engineering!
    By Jeckels in forum WarRock - International Hacks
    Replies: 13
    Last Post: 11-06-2007, 10:45 PM