Gunbound (Thor's Hammer) Server Emulator

This project attempts to emulate the server components of GunBound Thor's Hammer, which was discontinued in 2006.

녹염만세만세만만세

Screenshots

Channel Chat Game Load In Game Game End Post Game Lobby Avatar Random Dragon/Knight

Objective

Status

Broker Server: Fully implemented

Game Server: Mostly implemented

Buddy Server: Not implemented

Getting started

Requires Python 3.6

  1. Clone the project into a directory of your preference, preferably in a venv
  2. Install the project's requirements (pip install -r requirements.txt)
  3. In coordinator.py, replace the 192.168.1.x address with your current IP
  4. Run coordinator.py to launch the game server and the broker server
  5. Configure your client to connect to your server

TODO / Please help!


Contributors / Credits

Softnyx ethera knights blash45 pirania chuko scjang loserii johnny5 designer reddragon jchlee75 yaong2 jaeyong yesoori enddream cozy comsik

There are plenty of # unknown, comments in the code, TODOs, as well as guessed packet structures. If you spot an error, or would like to add information, please open an issue or PR. Don't worry about the formatting - I'll fix it.


License

MIT

Game client and artwork assets belong to Softnyx


GunBound Documentation

This attempts to be a comprehensive GunBound documentation (no specific scope). There might be some overlaps with my other repositories.

Client

Minimal Client

To start the game, these files are required as a minimum

Launcher

The main GunBound.gme client is launched via the GunBound.exe launcher. However it is possible (and desirable) to skip the launcher for the following reasons:

To launch the client directly:

An open-source replacement launcher is available here.

Unpacking

Most GunBound.gme clients that I've seen are packed with ASProtect/ASPack, which can be unpacked using stripper v2.07 by syd, kiev. Look around online for stripper_v207ht.

Enabling debug logs

In some clients, very useful debug logging is included, but not enabled. If a string containing "None Debug Mode\n" exists, the feature is most likely present. Find the xref to that string, and look for the subsequent CALL to find the debug function.

The original code likely had #define directives to create a logfile and store its handle, located at a static address. Thereafter, when the function is called, the client checks if the handle is valid before writing to it. This handle has to be restored for the debug logs to work again

My preferred way to enable logging is to inject a DLL into the process, conveniently after starting it using CreateProcess with the CREATE_SUSPENDED flag. Thereafter, CreateFile is called and the resulting handle is "restored" into the client. This also allows arbitary file names to be used. The debug log can alternatively be routed to a console using AllocConsole, and calling CreateFile with "CONOUT$" as the lpFileName parameter. A nearby jump also has to be disabled to prevent the handle from being overwritten.

Disabling GameGuard

GameGuard (anti-cheat rootkit) prevents the game from launching normally, as the compatible GameGuard servers are no longer operating.

GameGuard from Softnyx's perspective

From our perspective, the easiest way to disable GameGuard is to:

GameGuard strings are encrypted, and are only decrypted right before they are used. They can be fixed using this IDAPython script by vkiko2. The automatic detection feature does not work by default, and requires manually finding the decryption method and plugging the address in the script. Thereafter, finding the GameGuard functions is much easier, with obvious strings like '== InitNPGameMon done' .

The above technique has worked for me on the official GIS 313 client (protocol-incompatible with Serv2), where I could successfully play a buggy round of Jewel.

XFS Container Format

XFS (Xenesis File System) is a container format which is used for most of GunBound's assets, that is (probably) preferred for its ability to compress data while keeping changes contiguous to enable efficient patching. XFS2 is a versioned filesystem, and the client will compare the container's version with the registry value during startup to determine if it is outdated.

Softnyx appears to have leaked their editor XFS2.EXE (XFS2 응용 프로그램), and XFS2.EXE is the preferred tool for modifying GunBound Thor's Hammer files. For browsing, an "open source" tool InsideGB works well. There is no clear origin/author or license to it.


Server

Components

These are the key "Serv2" components required for full GunBound functionality:

Why not stick to Serv2?


Communications

Sequence

After establishing a connection, both client and server both track of the number of bytes that they have sent and received. "Sequence" is a 16-bit value (WORD) that can be calculated from the number of sent bytes:

Server to Client, where sum_packet_length is the total number of bytes that the server has sent the client.

(((sum_packet_length * 0x43FD) & 0xFFFF) - 0x53FD) & 0xFFFF

When generating a sequence value for sending a packet, take note that the sum_packet_length also includes the size of the outgoing packet.

This server does not verify the client's sequence, as TCP implies that the packet order and values are already checked at a lower layer. The sequence value may be more useful in UDP, especially during GunBound's in-game P2P communication.

Cryptography

GunBound encrypts some packets such as channel communications, avatar shop interactions, and places where sensitive data is exchanged. The process for setting up the encryption is as follows:

Because of the way which the dynamic key is determined (during the hashing process), user passwords have to be stored in plaintext.

Modified SHA-0

A modified SHA-0 hash is used when the client and server generate a shared dynamic AES key. The hash itself is SHA-0, although every DWORD in the output (5 total) is endian-flipped. AES only requires 16 bytes for a key, so the last 4 bytes are discarded. The original code used by GunBound appears to be from OpenSSL, sharing the same SHA_CTX structure.

AES

For secure network traffic and passing of login parameters, GunBound uses AES-128 in ECB mode. This configuration is insecure.

Padding is required to align to the 16-byte boundary during block encryption. The client does not care about the padding content. In fact the client sometimes sends irrelevant neighbouring bytes which may be mistaken for meaningful data.

To find the AES block operation, travel up the xrefs from the S/S-inv box (for encryption/decryption, respectively), which can be found in its expanded form. The right function should appear as a lot of move, shift and xor operations, along with comparisons/jumps for 10/12/14 (number of rounds for AES 128/192/256).

Keys can be found by placing a breakpoint before the AES block operation, and reading the memory region of the key schedule parameter. The memory region should contain two 176-byte key schedules, one of which is reversed in blocks of 16 bytes, each used for encryption and decryption. The key is the first 16 bytes of the encryption schedule, or the last 16 bytes of the decryption schedule.

Packet structure

Normal

Most unencrypted packets follow this format

Packet length: 2 Bytes / WORD
Sequence: 2 Bytes / WORD
Command: 2 Bytes / WORD
Payload: 0-N Bytes, defined by packet length

RTC

Essentially a normal packet with an extra "RTC" value before the payload. This is used in room-related packets. I have no idea what "RTC" stands for; the name is simply lifted from the disassembly.

Packet length: 2 Bytes / WORD
Sequence: 2 Bytes / WORD
Command: 2 Bytes / WORD
RTC: 2 Bytes / WORD
Payload: 0-N Bytes, defined by packet length

Encrypted

There are commands that encrypt the payload of the packet (cash-updates, avatar commands, user query, game start, channel chat and in-game server commands). Depending on the command, they can either be in the style of a "Normal" or "RTC" packet.

To encrypt a packet

P2P

TODO: describe host "key"

UDP Echo

TODO

Tunnel

TODO


Misc

The Softnyx programmers appear to be big fans of Neon Genesis Evangelion (1995)

Mobile/Bot/Tanks Internally represented as a BYTE

Name Name (KR) ID Remarks
Armor 아머 0x00 (0) Defaults to Armor when ID is invalid
Mage 메이지 0x01 (1)
Nak 나크 0x02 (2)
Trico 트리코 0x03 (3)
Bigfoot 빅풋 0x04 (4)
Boomer 부머 0x05 (5)
Raon 레온런쳐 0x06 (6)
Lightning 라이트닝 0x07 (7)
J.D. 제이디 0x08 (8)
A.Sate 에세트 0x09 (9)
Ice 아이스 0x0A (10)
Turtle 터틀 0x0B (11)
Grub 그럽 0x0C (12)
Aduka 슈퍼2 ("Super 2") 0x0D (13)
Dragon 드래곤 0x11 (17) Serv2 incorrectly sends 14
Knight 나이트 0x12 (18) Serv2 incorrectly sends 15
Random - 0xFF (-1) Asset name is "rider"

Ranks Internally represented as a 16-bit WORD

Name ID
20 ADMINISTRATOR
19 CHICK
18 WOODEN
17 DOUBLE WOODEN
16 STONE
15 DOUBLE STONE
14 AXE
13 DOUBLE AXE
12 SILVER AXE
11 DOUBLE SILVER AXE
10 GOLD AXE
9 DOUBLE GOLD AXE
8 BATTLE AXE
7 BATTLE AXE PLUS
6 SILVER BATTLE AXE
5 SILVER BATTLE AXE PLUS
4 GOLDEN BATTLE AXE
3 GOLDEN BATTLE AXE PLUS
2 VIOLET WAND
1 SAPPHIRE WAND
0 RUBY WAND
-1 DIAMOND WAND
-2 BLUE DRAGON
-3 RED DRAGON
-4 SILVER DRAGON

Game Mode Internal value used to track the client's state

ID Name
1 Intro Splash? (never called)
2 World Select
3 Channel
5 Init3D/Evangelion failed
7 Avatar Shop
9 Room
11 In Game Session (Play)
15 Exit to Desktop