|
You last visited: Today at 16:04
Advertisement
[Tutorial] Reversing the Client (EP. 1)
Discussion on [Tutorial] Reversing the Client (EP. 1) within the S4 League Hacks, Bots, Cheats & Exploits forum part of the S4 League category.
02/28/2016, 14:42
|
#1
|
elite*gold: 225
Join Date: Sep 2014
Posts: 334
Received Thanks: 460
|
[Tutorial] Reversing the Client (EP. 1)
[ Hey guys. I wanna start a new series of tutorials for reversing
the S4 League Game Client since it is very easy. ]
To get started we need a few things set-up:
[0. x86 (intel syntax) asm knowledge and the ability to think logically]
1. Disassembler (I prefer IDA Pro 6.9 with Hex-Rays plugin)
2. Struct Analyzing tool (ReClass 2015 or Cheat Engine)
3. Executable Dumper (Scylla)
4. Fundamental IDA Pro plugins (Class Informer and String Associate Plugin)
5. C++ knowledge for better understanding of the compiler
After that let's first get a fresh dump of the client. Start up the game with a bypass and dump it with scylla once you are in the lobby to make sure everything is initialized. In this episode we are only applying static analysis so we can close the client again and drag the dumped file into ida pro; it will start to analyze the binary which can take about 5-10 minutes with an average pc. You will notice that the IAT and EAT is broken. Of course it'd be better if it wasn't but we don't need it in this episode. Since I have no point to start reversing we will just use strings.
I found an interesting format string that could give us alot of information:
PHP Code:
.text:01A2A3B8 0000006E C %s [%s Actor %p] TeamID: %I64d, ActorID: %I64u, P2PID:{%d,%d,%d}, StateID: 0x%X, 0x%x, HP: %d(%d), MP: %d(%d)
This string will get us to know the following points:- Live status (dead or alive)
- Spectator or Player
- Class ptr
- TeamID
- ActorID
- P2PID
- StateID
- HP
- MP
The most useful thing in here is the class ptr. If you got the instance you can reverse it at runtime(ingame) and find really much stuff out.
Let's now reverse some code by going to the cross-reference of the string.
PHP Code:
.text:00AF7B77 mov eax, [ebp+var_10] .text:00AF7B7A push eax .text:00AF7B7B mov ecx, [ebp+var_64] .text:00AF7B7E push ecx .text:00AF7B7F mov edx, [ebp+var_60] .text:00AF7B82 push edx .text:00AF7B83 push offset aSSActorPTeamid ; "%s [%s Actor %p] TeamID: %I64d, ActorID"... .text:00AF7B88 lea eax, [ebp+var_A0] .text:00AF7B8E push eax .text:00AF7B8F call sub_168B4C0 .text:00AF7B94 add esp, 48h
We can immediately identify the calling convention by looking after the call instruction. The stack is cleaned by the caller. Thus it's __cdecl.
(https://en.wikipedia.org/wiki/X86_calling_conventions)
We can also count the arguments that were passed to this function by dividing 48h with 4 and then convert it to decimal. [ATTENTION: for optimization some compilers only use one add instruction and you cant really see if its only cleaning up one call or few)
Now let's continue with finding out what actually is passed to our function.
The first argument is eax which is &var_A0. Sooo find out what var_A0 is:
Scroll up and lookup an instruction that involves var_A0 as operand.
Nothing is being moved(copied) in var_A0 so it's just used as a reference or pointer to fill some value in it(buffer). Second argument is the format string...
Third argument seems to be a string which is passed in edx, whereas edx is var_60. Let's dig deeper.
PHP Code:
.text:00AF7A40 loc_AF7A40: ; CODE XREF: sub_AF78A0+197j .text:00AF7A40 mov edx, [ebp+var_10] .text:00AF7A43 mov eax, [edx] .text:00AF7A45 mov ecx, [ebp+var_10] .text:00AF7A48 mov edx, [eax+0D4h] .text:00AF7A4E call edx .text:00AF7A50 movzx eax, al .text:00AF7A53 test eax, eax .text:00AF7A55 jz short loc_AF7A60 .text:00AF7A57 mov [ebp+var_60], offset aLive ; "Live" .text:00AF7A5E jmp short loc_AF7A67 .text:00AF7A60 ; --------------------------------------------------------------------------- .text:00AF7A60 .text:00AF7A60 loc_AF7A60: ; CODE XREF: sub_AF78A0+1B5j .text:00AF7A60 mov [ebp+var_60], offset aDeath ; "Death"
here's where var_60 is being set. var_10 is probably some player class or actor class. then the address of the virtual table get's copied into eax and it's calling the 53th virtual function of this class (0D4h / 4 to decimal)[basic index calculating]. This function can be compared to
PHP Code:
virtual bool IsAlive() = 0;
. The result of this function gets tested and if it returned true the string which gets copied into var_60 is "Alive" and otherwise "Death".
Wow this took longer to explain than I expected. We will continue this shxt in the next episode. cya.
|
|
|
02/28/2016, 15:41
|
#2
|
elite*gold: 1
Join Date: Apr 2012
Posts: 3,001
Received Thanks: 6,198
|
Added to my Overview. 
Keep it up! Looks nice.
|
|
|
02/28/2016, 15:57
|
#3
|
elite*gold: 225
Join Date: Sep 2014
Posts: 334
Received Thanks: 460
|
PS: I have noticed that the function we were in is also a virtual function and additionally the main loop (it loops thru CTeam* vector and thru CActor* vector). Here's a nice static ptr to CTeamManager: (DWORD)GetModuleBase("S4Client.exe") + 0x2098574
And this is how it loops thru the teams:
PHP Code:
CTeam* __thiscall GetTeam(CTeamManager* pThis, size_t idx) { int result; // eax@4 int v3; // [sp+10h] [bp-4h]@2
if ( (*(_DWORD *)(*(_DWORD *)(this + 4) + 4) - **(_DWORD **)(this + 4)) >> 2 <= a2 ) { result = 0; } else { v3 = *(_DWORD *)(this + 4); if ( (*(_DWORD *)(v3 + 4) - *(_DWORD *)v3) >> 2 <= a2 ) v57AD3A0C("invalid vector<T> subscript"); result = *(_DWORD *)(*(_DWORD *)v3 + 4 * a2); } return result; }
Therefore CTeamManager + 0x4 is CTeams vector. (CTeamManager + 0x0 is vftable).
And this is how it loops thru actors/players:
PHP Code:
CActor* __thiscall GetPlayer(CTeam* pThis, size_t idx) { int result; // eax@4 int v3; // [sp+10h] [bp-4h]@2
if ( (*(_DWORD *)(*(_DWORD *)(this + 0x24) + 4) - **(_DWORD **)(this + 0x24)) >> 2 <= a2 ) { result = 0; } else { v3 = *(_DWORD *)(this + 0x24); if ( (*(_DWORD *)(v3 + 4) - *(_DWORD *)v3) >> 2 <= a2 ) v57AD3A0C("invalid vector<T> subscript"); result = *(_DWORD *)(*(_DWORD *)v3 + 4 * a2); } return result; }
Therefore CTeam + 0x24 is the players vector.
With this information you can either call the engine functions to get ptrs to specific players, teams
or you can rebuild this function in cpp what i've done for you:
PHP Code:
class CTeamManager; class CTeam; class CActor;
class CTeamManager { public: void* pVTable; //0x0000 std::vector<CTeam*>* ppTeams; //0x0004 static CTeam* GetTeamById(size_t idx) { return *(CTeam**)(*ppTeams + idx * 4); } }
class CTeam { public: void* pVTable; //0x0000 char pad[0x20]; //0x0004 std::vector<CActor*>* ppPlayers; //0x0024 static CActor* GetPlayerById(size_t idx) { return *(CActor**)(*ppPlayers + idx * 4); } }
Have fun guys playing with this stuff :P
EDIT:
new stuff:
PHP Code:
.text:00AF7B6F mov ecx, dword ptr [ebp+var_78+4] .text:00AF7B72 push ecx // ActorID .text:00AF7B73 mov edx, dword ptr [ebp+var_78] .text:00AF7B76 push edx //TeamID
PHP Code:
.text:00AF79F6 mov ecx, [ebp+var_48] .text:00AF79F9 add ecx, 8 .text:00AF79FC call sub_AFA390 .text:00AF7A01 movzx eax, al .text:00AF7A04 cdq .text:00AF7A05 mov dword ptr [ebp+var_78], eax .text:00AF7A08 mov dword ptr [ebp+var_78+4], edx
PHP Code:
TeamID = sub_AFA390(var_48+8);
how it gets calculated in the function itself: ( this is var_48+8)
PHP Code:
v3 = *(_BYTE *)(this + 2) ^ *(_BYTE *)this;
[means values are encrypted :^)]
EDIT2:
that's how you decrypt the teamID:
the first byte at selection is *(BYTE*)this which is 'E1' and +2 bytes is 'E1' too. XOR'ing 'E1' with 'E1' results to 0. So the team I'm in has ID 0. (for the ones who does not know, " ^ " <- this character means bitwise exclusive or aka. XOR which is mostly used in games to encrypt things like strings or other values)
|
|
|
03/01/2016, 15:56
|
#4
|
elite*gold: 225
Join Date: Sep 2014
Posts: 334
Received Thanks: 460
|
more info:
how to get game rules (again s4 provides us a static ptr..)
PHP Code:
.text:00AF8761 mov ecx, ds:dword_20968B4 .text:00AF8767 mov [ebp+var_114], ecx .text:00AF876D mov edx, [ebp+var_114] .text:00AF8773 mov eax, [edx+12Ch] .text:00AF8779 mov [ebp+var_16C], eax .text:00AF877F mov ecx, [ebp+var_16C] .text:00AF8785 add ecx, 1Ch .text:00AF8788 call sub_AFA400 .text:00AF878D mov [ebp+var_11C], eax .text:00AF8793 mov ecx, [ebp+var_11C] .text:00AF8799 push ecx .text:00AF879A push offset aLimitscoreD ; "LimitScore = %d" .text:00AF879F lea edx, [ebp+var_244] .text:00AF87A5 push edx .text:00AF87A6 call sub_168B4C0
c++
PHP Code:
typedef int (__thiscall *tGetScoreLimit)(void* pThis); tGetScoreLimit GetScoreLimit = 0xAFA400 + (DWORD)GetModuleBase("S4Client.exe");
uintptr_t pECX = *(uintptr_t*)(0x20968B4); uintptr_t pEAX = *(uintptr_t*)(pECX + 0x12C); pEAX += 0x1C;
^minimizes to:
void* pObject = *(uintptr_t*)(*(uintptr_t*)(0x20968B4) + 0x12C) + 0x1C; int LimitScore = GetScoreLimit(pObject);
you can find other game rules around there :P
(more will come, when im back home)
|
|
|
03/01/2016, 17:44
|
#5
|
elite*gold: 10
Join Date: May 2013
Posts: 814
Received Thanks: 1,265
|
well, finally something useful in this section.  not bad. keep it up!
|
|
|
03/01/2016, 19:33
|
#6
|
elite*gold: 225
Join Date: Sep 2014
Posts: 334
Received Thanks: 460
|
PHP Code:
ecx = *(DWORD*)(*(DWORD*)(*(DWORD*)(0x28C68CC) + 0x12C) + 0xC); call eax [11]
Get current round time
virtual call -> 11th index of ecx's(instance ptr) virtual table(vftable)
after the call the result or the current round time gets set in eax
which means the return value of *(DWORD**)(ecx)[11]() is the current round time.
for calling virtuals you just need the correct calling convention which is always __thiscall
parameters get passed normally - pushed onto the stack in x86 before ecx is getting set.
PHP Code:
typedef int (__thiscall* tGetRoundTime)(void);
then just call it like that:
PHP Code:
( tGetRoundTime ( * ( uintptr_t** )( ecx )[ 11 ] ) ) ( )
hf.
|
|
|
03/02/2016, 14:57
|
#7
|
Trade Restricted
elite*gold: 74
Join Date: Mar 2015
Posts: 632
Received Thanks: 109
|
Really nice. Keep that **** going ^^
|
|
|
03/03/2016, 00:54
|
#8
|
elite*gold: 0
Join Date: Dec 2008
Posts: 423
Received Thanks: 1,378
|
Wow! Really nice to see someone actually reversing the game for once and not just doing small ****.
P.S.: I released the source of my  , and it contains some S4 information like this; maybe it will be of assistance.~
|
|
|
03/03/2016, 02:14
|
#9
|
elite*gold: 10
Join Date: May 2013
Posts: 814
Received Thanks: 1,265
|
Quote:
Originally Posted by ▒ Ant.
Wow! Really nice to see someone actually reversing the game for once and not just doing small ****.
P.S.: I released the source of my  , and it contains some S4 information like this; maybe it will be of assistance.~
|
lel just noticed your release now. it's very nice! thanks for that!
|
|
|
03/03/2016, 15:28
|
#10
|
elite*gold: 20
Join Date: Oct 2012
Posts: 346
Received Thanks: 361
|
as a member(me ) who is intersted in reversing games. happy to see someone reversing S4 and and posting tips/tuts. helped me alot.
was looking for smthin like that. keep it up.
|
|
|
03/04/2016, 00:05
|
#11
|
elite*gold: 0
Join Date: Jul 2015
Posts: 6
Received Thanks: 1
|
Quote:
Originally Posted by Shinzuya
Added to my Overview. 
Keep it up! Looks nice.
|
Since you understood !!
So what exactly is this :! so confuse ?
|
|
|
03/04/2016, 17:37
|
#12
|
elite*gold: 36
Join Date: Jan 2014
Posts: 73
Received Thanks: 13
|
and this is what?
|
|
|
 |
Similar Threads
|
[TUTORIAL] Reversing the graphics pipeline
10/19/2015 - S4 League Hacks, Bots, Cheats & Exploits - 4 Replies
Hey guys, this tutorial will show you basic static reverse engineering or analysis of a dump. In this case I will show you something of S4's graphics pipeline. The practical example will be the D3D Font. This tutorial aims at a bit advanced members and requires asm knowledge. At the end of the tutorial, I will also give you a tip on how to aquire a static pointer.
D3D Font
http://i.imgur.com/yzxNCmF.png
Here, we found an interesting string that will help us to get a starting point....
|
[Tutorial-Collection] Metin2 Reversing/Hacking
07/03/2015 - Metin2 Guides & Templates - 149 Replies
Hey Epvp'ler.
Ich habe mich dazu entschieden die ungebildete Metin2-Community mal ein bisschen auf Vordermann zu bringen und release hier meine private Playlist mit Reversing-Tutorials für Metin2.
Die Tutorials sind so vom Niveau her für Anfänger, jedoch nicht für welche, die keine Ahnung von Memory, Assembler und Cheat Engine und sowas haben. Also WriteProcessMemory() solltet ihr schonmal gehört haben.
Wenn ihr Ideen für weitere Videos oder Fragen habt, fragt einfach hier, wenn sie mir...
|
Reversing Tutorial 1 (Kickvote)
04/18/2015 - S4 League Hacks, Bots, Cheats & Exploits - 26 Replies
http://i.epvpimg.com/GWnxd.png
http://i.epvpimg.com/wPtlf.png
http://i.epvpimg.com/YTLnb.png
Das folgende Tutorial ist von mir geschreiben und ist nur für Elitepvpers, ich erlaube es nicht es in andere Foren zu kopieren!
-Zu erst downloadet ihr euch Cheat Engine falls ihr das noch nicht habt.
-Danach öffnet ihr S4 mit einen Bypass.
-Startet nun Cheat engine und geht in die Settings siehe Bild.
|
[Tutorial] Basic Reversing in S4L (KickVote Function)
04/13/2015 - S4 League Hacks, Bots, Cheats & Exploits - 13 Replies
Hello. :)
Wanna show you how to reverse functions in S4 League.
So let's get started;
First you need the address of the pre-kickvote processing function,
which is located in some class(__thiscall). I found out that it's not a vtable entry, so that's not so good because with vftables you can always grab your functions easier.
http://i.imgur.com/8x9rO2q.png
.text:00E5FB12 push eax
|
[Tutorial] Reversing Spell Encryption
06/15/2014 - CO2 Programming - 1 Replies
It has been quite a while since I last wrote a tutorial related to Conquer. This is mostly useless because as far as I know the spell encryption has already been reversed long time ago. The purpose of this post / tutorials is to help people understand how it's possible to find functions and implement them in desired language by reverse engineering. This will also be kind of log for me on how I approach reversing.
This reverse engineering was performed on a unnamed private server so I'm not...
|
All times are GMT +1. The time now is 16:07.
|
|