Bango KalOnline Blog

Injecting Code into KalOnline via Proxy DLL

This is continuation of "Creating Addon from Scratch" series, part 2. Check previous part if you want to read theory and some kal scene history.

Prerequesites

This post blog is directed towards KalOnline addon creators wanting to understand how to make an addon from scratch via proxy DLL. It is required to have minimum understanding of C/C++ and willingness to learn new things! The tutorial will not cover how to set up clean server, though. I'm assuming you already have your setup ready and you are able to log into the game. If you wish there was blog post on how to setup minimal server, let me know on discord. This is important to note that setup does not need to be clean, following steps should also work with other addons already installed.

Which DLL Cannot be Proxied?

First, optional step is to understand which DLL were used as a proxy in your existing addons – simply because we need to pick different DLL for our proxy. If you use clean set up and do not use any other addons – you can omit this entire paragraph.

I've looked up public "Core" addons and I can see multiple DLLs in the server and client side. On the server side there is Addons.dll, Core.dll, Data.dll, and Sue.dll. In Loader directory I found Sora.dll and bunch of other files. It's important to understand that not all those DLLs are proxy DLLs. It is also possible to loader DLLs programmatically via LoadLibrary API call (used in paragraphs below). In this post we will focus on adding modifications to MainServer.exe (look up previous part for application architecture), therefore open this file in CFF Explorer. Check "Dependency Walker" to see dependency DLLs:

custom-mainserver-dependencies

I have highlighted 3 libraries which seem to be custom. To understand which original libraries were replaced, use any binary diff tool and compare clean MainServer with your custom one. I will use Guiffy Binary Diff Tool. The difference looks like this:

binary-diff

It does look like Core.dll is a proxy dll for ADVAPI32.dll, so this library is already taken and we need to pick different one. This is important to track all taken libraries before going into next steps.

Picking the Right DLL for Proxy

We need to pick one from dependency DLLs of MainServer.exe application. We already looked up dependency list via CFF Viewer above. You can also use any Hex Editor and search for strings with .dll suffix.

We should pick DLL which has the lowest number of API calls, which we will need to re-export. In fact, creating the re-exports code can be automated, so we should not worry too much. For Bango Classic server I have used VERSION.dll. You can look up API calls under "Export Directory" tab. There's not many calls (I've found this DLL in C:/Windows/System32/version.dll):

version-api

For the sake of this post, I will pick dbghelp.dll which is unused in my setup.

Creating New DLL

To create new DLL project, we will need Visual Studio with C++ support. Install it from official Microsoft website, it is free. Community version will be enough. I suggest using latest version and update it frequently. After installation is completed, create new project "Dynamic-Link Library (DLL)":

new-project

After creation, switch from Debug mode to Release and x64 architecture to x86. We need 32-bit DLL to match KalOnline client/server. You can right click the project and "Build", to ensure your setup is correct so far.

Now, we need to re-export all original API calls. This can be achieved by plenty of methods:
- wrappit tool (used by kal scene alot, can't seem to find download link anymore)
- wrap_dll python tool - I will use this one
- any other DLL wrapper which automates the process (search online)

After cloning the wrap_dll repository I ran python script and passed path to my dbghelp.dll located in server files (I have python3.11 installed):

> pip install -r requirements.txt
> python wrap_dll.py \
  --dumpbin "C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\VC\\Tools\\MSVC\\14.35.32215\\bin\\Hostx86\\x86\\dumpbin.exe" \
  --undname "C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\VC\\Tools\\MSVC\\14.35.32215\\bin\\Hostx86\\x86\\undname.exe" \
  "<path to my server files>\\dbghelp.dll"

Change path to dumpbin and undname tools to your location in case it is different.

My directory after running the script:

after-wrap-tool

Now copy all the files to your project directory. Remove existing dllmain.cpp file and add dbghelp.cpp, empty.h and hook_macro.h to your source tree. Also change Character Set to "Use Multi-Byte Character Set" and Precompiled Headers to "Not Using":

multi-byte-charset remove-pch-requirement

Your project tree should look like that:

project-tree

Last step is to point to dbghelp.def file in linker settings:

point-to-def

Voila! Recompile the project to make sure everything works as expected:

1>Finished generating code
1>BlogDLL.vcxproj -> C:\Users\lafreak\source\repos\BlogDLL\Release\BlogDLL.dll
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
========== Rebuild started at 5:20 PM and took 01.125 seconds ==========

Injecting Your Code

To prove that we really can inject code into the process, the easiest thing to do will be to enable CLI (console) and log something to the screen. Edit dbghelp.cpp with following code:

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
  mHinst = hinstDLL;
  if (fdwReason == DLL_PROCESS_ATTACH) {
    AllocConsole();
    freopen("CONOUT$", "w", stdout);
    printf("Hello Bango!\n");

    mHinstDLL = LoadLibrary("real_dbghelp.dll");
    if (!mHinstDLL) {
      return FALSE;
    }
    for (int i = 0; i < 119; ++i) {
      mProcs[i] = (UINT_PTR)GetProcAddress(mHinstDLL, mImportNames[i]);
    }
    _hook_setup();
#ifdef _DEBUG
    debug = fopen("./debug.log", "a");
#endif
  } else if (fdwReason == DLL_PROCESS_DETACH) {
#ifdef _DEBUG
    fclose(debug);
#endif
    FreeLibrary(mHinstDLL);
  }
  return TRUE;
}

Make sure to add _CRT_SECURE_NO_WARNINGS to preprocessor defines as well. Right click project > Properties > C/C++ > Preprocessor > Preprocessor Definitions

After compilation, copy BlogDLL.dll to server files. Edit MainServer.exe - search for dbghelp.dll and replace with BlogDLL.dll. Also rename dbghelp.dll file to real_dbghelp.dll.

Start the server! We finally injected code! 🎉

injection-result

Next Steps

This is very basic modification but shows possibility to inject arbitrary code and have it persisted across multiple application runs. Having such setup, you can start adding your modifications. In my Bango Classic setup I have decoupled proxy DLL from actual Addon DLL by loading it directly via LoadLibrary Windows API call. This is good if you do not want to bloat your project with auto-generated exports (this is of course optional step).

Keep in mind you need to create separate DLL proxies if you want to make modifications to other applications such as: engine.exe for game client edits and AuthServer.exe/DBServer.exe to make Auth/DBServer edits. For more sophisticated systems you will be forced to edit all applications!

If you want to suggest some topic, having trouble, or want to give feedback, please let me know on discord. Contact me if you want access to source code from this post. This might be easier than copying from here!

I'm out for now.

lafreak