Gamehacking: Rakion, the begining

Part I: Gamehacking: Rakion, the begining
Parte II: Rakion: Entities and editing cell creatures

Some words of my past

When I started in reverse engineering I was in high-school in Peru and with my frieds I played DotA and Rakion. One day a dude started to use a cheat (at that time we called it “hack” :P), after asking him many times he told me where he had downloaded it.

At that moment I started in the world reversing, it was very hard to me because I didn’t know anything about programming, reverse engineer, assembly, even how to use google. Furthermore, starting in the gamehacking world is very hard because there is money involved.

In the forum where I downloaded the cheat, one day a guy shared a really good post about what I’m going to name, “My first cheat”. He said it was their research, but then other people denied it, saying it was someone else’s work that he had appropriated. While there was a fight of egos in that forum, for me it was the beginning of my world in reverse engineering.

Several years have passed and I want to technically tell you how I started and what cheats for Rakion I did.

Introduction

The main goal to start cheating any MMORPG or similar games is to find the entity player, because usually it will be found inside a bigger structure and/or reference to another ones with game player information.

Possibly there will exist at least two entity player (or only one), entity in-lobby player and entity in-game player, of course it depends on the game design. In the entity lobby player we could found player armor (such as helm, gauntlet, cuirass, primary and secondary weapon, rings, etc), summoned creatures and their information (such as the kind and level), inventory, gold (it’s very likely that we cannot modify it because is server-side information), stats, etc. And for the entity in-game player we could found, the character position axis according to the render game model, virtual camera system (such as camera display spectrum of the character, observer camera, graphical perspective camera, etc), type of character, etc.

Rakion is an 3D MMORPG computer game that has two main DLLs, the first one is entitiesmp.dll and the second one is engine.dll. entitiesmp.dll has the implementation of character interaction such as its armor, health points (in this game that info is client-side), cell points (points required for cell creature summoning), attacks , etc. engine.dll has the implementation of game capabilities or character information such as position axis, enemies position axis, character encryption information, etc. Furthermore, Rakion has a file called DataSetup.xfs where there is game information such as stage map, items, cell creatures, etc, and historically it was zlib compressed, DataSetup.xfs is read when the game is loaded therefore, some malicious modification was reflected in the game (some information is read in runtime such as stage configuration), now (I think) it is encrypted on disk but unencrypted in memory ;).

My first cheat

An “easy cheat” to write in this first blogpost (even was my first cheat ever) is about summoning a cell creature for another one, also know as Cell2Cell. entitiesmp.dll export objects of CNpc[\w+]_DLLClass, in other words the information of cell creatures are exported and its size is 84 bytes.

.data:355F0FD0 CNpcNak4_DLLClass dd offset unk_356149AC
.data:355F0FD0                                         ; DATA XREF: .rdata:off_35588D28↑o
.data:355F0FD4                 align 8
.data:355F0FD8                 dd offset unk_356149B0
.data:355F0FDC                 align 10h
.data:355F0FE0                 dd offset unk_355F0FC0
.data:355F0FE4                 db    1
.data:355F0FE5                 db    0
.data:355F0FE6                 db    0
.data:355F0FE7                 db    0
.data:355F0FE8                 dd offset unk_356149D0
.data:355F0FEC                 db    1
.data:355F0FED                 db    0
.data:355F0FEE                 db    0
.data:355F0FEF                 db    0
.data:355F0FF0                 dd offset aNpcnak4      ; "NpcNak4"
.data:355F0FF4                 dd offset Buffer
.data:355F0FF8                 db  73h ; s
.data:355F0FF9                 db    4
.data:355F0FFA                 db    0
.data:355F0FFB                 db    0
.data:355F0FFC                 dd offset CNpcNakBase_DLLClass
.data:355F1000                 dd offset sub_351D86D0
.data:355F1004                 dd offset nullsub_1804
.data:355F1008                 dd offset nullsub_1805
.data:355F100C                 dd offset nullsub_1806
.data:355F1010                 dd offset nullsub_1807
.data:355F1014                 dd offset nullsub_1808
.data:355F1018                 dd offset nullsub_1809
.data:355F101C                 dd offset nullsub_1810
.data:355F1020 ; public class CNpcNak4 /* mdisp:0 */ :
.data:355F1020 ;   public class CNpcNakBase /* mdisp:0 */ :
.data:355F1020 ;     public class CNpcBase /* mdisp:0 */ :
.data:355F1020 ;       public class CMovableModelEntity /* mdisp:0 */ :
.data:355F1020 ;         public class CMovableEntity /* mdisp:0 */ :
.data:355F1020 ;           public class CRationalEntity /* mdisp:0 */ :
.data:355F1020 ;             public class CLiveEntity /* mdisp:0 */ :
.data:355F1020 ;               public class CEntity /* mdisp:0 */
.data:355F1020 ; class CNpcNak4 `RTTI Type Descriptor'
.data:355F1020 ??_R0?AVCNpcNak4@@@8 dd offset ??_7type_info@@6B@
.data:355F1020                                         ; DATA XREF: .rdata:354F46A8↑o
.data:355F1020                                         ; .rdata:CNpcNak4::`RTTI Base Class Descriptor at (0,-1,0,64)'↑o
.data:355F1020                                         ; reference to RTTI's vftable
.data:355F1024                 dd 0                    ; internal runtime reference
.data:355F1028 aAvcnpcnak4     db '.?AVCNpcNak4@@',0   ; type descriptor name

For example CNpcNak4_DLLClass could be overwrite with the information of another cell creature object, so when it is summoned the creature will be the another one.

Basically is do:

PBYTE nak4_addr = (PBYTE)GetProcAddress(GetModuleHandleA("entitiesmp.dll"), "CNpcNak4_DLLClass");
PBYTE taurus1_addr = (PBYTE)GetProcAddress(GetModuleHandleA("entitiesmp.dll"), "CNpcTaurus1_DLLClass");
memcpy(nak4_addr, taurus1_addr, 84);

Nak4 is white nak, and when it is summoned three naks appear. It is why three Taurus are summoned.


The entities

In this game, there are two entities, in-lobby and in-game (at least I call it that way). Of course the key for many cheats is to get its addresses, because we can modify and abuse many things such as cell creatures, stats, type of attacks, position axis, etc.

The entitiy in-game, has information of the character in-game, and in this we can abuse in the health point (HP), armor point (AP), cell point (CP), caos points, potions (pots see the number 6), position axis, etc.

To get the entity in-game player address I should call two functions entitiesmp!CPlayer::GetLocalPlayer and engine!FieldInfo::GetLocalPlayer.

FieldInfo& CPlayer::GetFieldInfo()
CEntity& FieldInfo::GetLocalPlayer()

There is a function that shows us how I should call both, entitiesmp! CPlayer::GetLocalPlayer.

CEntity& CPlayer::GetLocalPlayer()
{
  FieldInfo objFieldInfo = this->GetFieldInfo();
  return objFieldInfo->GetLocalPlayer();
}

In assembler:

invoke GetModuleHandle, addr entitiesmp
invoke GetProcAddress, eax, addr GetFieldInfo
cmp eax,NULL
jz end
call eax
push eax

invoke GetModuleHandle, addr engine
invoke GetProcAddress, eax, addr GetLocalPlayer
pop ecx
call eax
; eax: entity in-game address

This entity can only be obtained in game, and its address will always change for each play.

The entitiy in-lobby, is totally different than entity in-game, because the address does not change and has information about the items and stats of the character, etc. When I was 15 years old, that address can be obtained from entitiesmp!GetSelectLevelInfo() function.

// IDA Pro 6.x decompiler
struct _s_stage *__cdecl GetSelectLevelInfo()
{
  int v0; // edx@1
  int v1; // ecx@1
  int v2; // eax@1

  v2 = (*(int (**)(void))(*(_DWORD *)dword_354B7FC4 + 12))();
  if ( IsBattleMap(*(_BYTE *)(v2 + 443)) )
    v1 = 1;
  return GetLevelInfo(, *(_BYTE *)(v0 + 442));
}

The address of entity in-lobby is 0x354B7FC4, but currently the entitiesmp!GetSelectLevelInfo() implemention changed and and it never shows the address anymore.

struct _s_stage *__cdecl GetSelectLevelInfo()
{
  FieldInfo objGFieldInfo = GetGlobalFieldInfo();
  int game_type = objGFieldInfo->GetGameType();
  if ( IsBattleMap(game_type) )
    game_type = 1;
  return (struct _s_stage *)GetLevelInfo(game_type, objGFieldInfo->LevelInfo));
}

Of course in the nexts blogposts I will explain how we can get it ;).

For now, here I leave just a small example of what can we do getting the entities.


Written by Nox

Follow us on our twitter account @rop-la, our public Github repositories RoP-LA and Youtube Channel.