Register for your free account! | Forgot your password?

Go Back   elitepvpers > Metin2 > Metin2 Guides & Templates
You last visited: Today at 17:50

  • Please register to post and access all features, it's quick, easy and FREE!


[How To]Locate "PacketSend"-Function in nearly every game

Reply
 
Old   #1
 
elite*gold: 5
Join Date: Dec 2011
Posts: 1,082
Received Thanks: 621
[How To]Locate "PacketSend"-Function in nearly every game

English translation

Hey epvp,

in diesem Tutorial zeige ich euch, wie man die Funktion ausfindig machen kann, welche für das Senden der Pakete zuständig ist.
Das Ganze zeige ich hierbei unabhängig vom Metin2 Client, aber am Beispiel von Metin2.
D.h. es kann ebenfalls auf andere Spiele angewendet werden.

Natürlich kann die Send Funktion bei Metin2 viel einfacher gefunden werden, da der Source public ist, aber so lernt man auch nichts, also hier the hard way

Für Leute die sich noch nie mit CE, OllyDBG, x32dbg oder sonstigen Debuggern beschäftigt haben, ist das hier leider nichts!
Ich setze Grundkenntnisse in ASM vorraus.

Vorwort/Die Theorie

Die Kommunikation von Client und Server in Online-Games wird, wie in allen Client-Server Anwendungen, über den Austausch von Paketen(in dem Fall TCP) realisiert. Die Pakete teilen dem Server/Client mit, wie er sich verhalten soll, bzw welche Funktionen ausgeführt werden sollen.

Windows stellt hierfür eine Bibliothek zur Verfügung, mit der TCP-Verbindungen aufgebaut werden können. Diese wird beim Start des Spiels mitgeladen.
Microsoft liefert außerdem die Programmdatenbank(.pdb)(von ziemlich allen API's), in welcher Debug - und Projektinformationen über die jeweilige Bibliothek
gespeichert sind. Darunter die genau Position von Funktionen, welche durch die dll/lib implementiert werden bzw. zur Verfügung stehen. Auch Symbole genannt.

Naja ich red mal nicht lang drum rum: Dies ermöglicht den Debuggern wie CE, OllyDBG, x32dbg etc. die Funktionen, die für die TCP-Verbindung zuständig sind, später im Speicher zu lokalisieren.

Die Funktion die wir suchen ist folgende: WS2_32.send()

MSDN says:
PHP Code:
int send(
  
__in  SOCKET s,
  
__in  const char *buf,
  
__in  int len,
  
__in  int flags
); 
Die Parameter SOCKET s und int flags sind hierbei nicht von Bedeutung für uns.

Vielmehr jedoch der char Buffer(__in const char *buf) und die Länge(__in int len).

__in const char *buf & __in int len

Der Parameter char * buf enthält hierbei, wie man sich denken kann, den Pointer zu dem Buffer, welcher unsere Pakete beinhaltet, die gesendet werden sollen.
Die Länge gibt an wie viele Bytes aus dem Buffer geladen werden/gesendet werden sollen.

Ich komm an der Stelle mal auf den Punkt:

Durch den Bufferpointer können wir mit CheatEngine herausfinden, welche Funktionen den Buffer beschreiben/ändern.
Wenn die Stelle gefunden ist, tracet man den Funktionsverlauf so weit zurück, bis man eine Funktion gefunden hat, mit welcher man selbst Pakete senden kann,
oder diese bearbeiten kann.

Was ist noch möglich?

1.) Alle Funktionen die im Spiel eine höhere Bedeutung für Bots/Hacks haben(PickUp, Useitem...), müssen früher oder später die Pakete in den Buffer schreiben, bzw. die Sendpacket Funktion
aufrufen. D.h. wenn wir diese ausfindig machen, können wir die Hauptfunktionen ebenfalls backtracen.

2.) Wenn man genügend Zeit hat, kann man den Netzwerkstream sogar emulieren und sogenannte Clientlessbots erstellen.

Schwierigkeit:

Ihr werdet kein Spiel finden, was die Pakete nicht verschlüsselt. Wir müssen also ebenfalls herausfinden, an welcher Stelle die Pakete encoded werden.
Sonst bringt uns das Ganze rein gar nichts, da wir den Inhalt der Pakete später nicht analysieren können.

Die Umsetzung

genug Theorie, auf zur Praxis. Hier einmal die Steps in der Übersicht:

1.) Send Funktion der WS2_32 Lib ausfindig machen
2.) Paket-Buffer lokalisieren
3.) Funktion des Spiels finden, welche den Buffer beschreibt
4.) Verschlüsselung umgehen
5.) Eigene Pakete senden + C++ Source

Okay, let´s reverse tha code.

Step 1: Send Funktion der WS2_32 Lib ausfindig machen

Zuerst einmal muss das Spiel in CheatEngine attached werden. Zunächst muss der Memory Viewer geöffnet werden. Oben im Menü des Memory Viewer findet man unter dem Menüpunkt "View"-> "Enumerate DLL's and Symbols".



In der Liste befindet sich eine DLL namens WS2_32.dll. Selektiert man den Eintrag, so öffnet sich ein Funktionsbaum.
Dort sucht ihr nach WS2_32.send und klickt diese mit einem Doppelklick an.



Ihr solltet nun im Assembler-Code an den Funktionsanfang gesprungen sein. In meinem Fall 75A21F60.

Step 2: Paket-Buffer lokalisieren

Zunächst wird ein Breakpoint auf die WS2_32.send Funktion gesetzt.
Achtung: Sollte der Gameclient crashen, nutzt den VEH Debugger von CE.

Die send Funktion muss nun zum Auslösen gebracht werden. Das kann durch sämtliche Aktionen im Spiel geschehen. Sei es eine Bewegung, Skill aktivieren oder das Ausrüsten eines Items. Ist der Breakpoint ausgelöst, so sehen wir die auf den Stack gepushten Parameter(SOCKET, buffer*, len, flags).



Ich habe die Parameter in dem Bild mal makiert und beschriftet.
Der BufferPointer wird nun zur CE Cheattable hinzugefügt. Als Typ stellt ihr Array of Bytes ein, mit einer Größe von 10 z.b.

Sieht dann so aus:



Step 3: Funktion des Spiels finden, welche den Buffer beschreibt

Nachdem der Pointer zur CheatTable hinzugefügt wurde, kann man diesen makieren
und drückt F6 oder macht einen Rechtsklick und wählt "Find out what writes to this address".

Folgendes Fenster öffnet sich:



Bewegt man sich nun Ingame oder löst andere Events aus, sollte dies nun so aussehen:



Nun eine nicht sehr einfache und meistens zeitraubende Arbeit. Wir müssen herausfinden, welche Funktionen in unseren Buffer schreiben.
Dabei kann man sich folgendes Konzept vorstellen:

Wird ein Paket gesendet, so muss es vorerst unentschlüsselt irgendwo erstellt werden(Objekt), und wird dann in den Buffer geschoben.
Soll der Buffer/bzw das Paket nun verschickt werden, wird er verschlüsselt und über die WS2_32.send rausgeschickt.

Hier ein C++ Beispiel-Code wie das aussehen kann, um sich das einmal vorzustellen:

PHP Code:

byte sendBuffer
[2048];
int sendBufferSize 0;

void ItemAusrüsten(byte slot)
{
    
ItemAusrüstenStruct ias;
    
ias.header 0x123;
    
ias.slot slot;
    
senden(&ias,sizeof(ias))
}

void senden(char paketptrint size)
{
    
memcpy(sendBuffer+sendBufferSizepaketptrsize);
    
sendBufferSize+=size;
}

running in another thread:

void finalSend()
{
    
verschlüssel(&sendBuffer,sendBufferSize);
    
    
WS2_32.send(sendBuffersendBufferSize);
    
sendBufferSize 0;

Dies ist bei Metin2 der Normalfall, bei dem Client den ich hier reverse allerdings etwas anders. Die Pakete werden sofort verschlüsselt in den Buffer gelegt, wie wir später sehen werden.

Zurück zum letzten Bild.
Die ersten 2 Instructionen oben im Screenshot liegen ziemlich nahe beieinander(von der Adressrange her).
Klickt man einer dieser Adressen an(Doppelklick), erhält man folgendes Fenster:


Bild6

Springen wir zuerst einmal zur ersten Adresse aus dem Screenshot Bild6 :



Oben im Bild6 steht, dass EAX unseren Buffer Pointer hält. Also müssen wir zuerst einmal schauen wo dieser herkommt.
Dazu schaut man sich die OP-Codes über unserer jetzigen Funktion an. Ich habe das schon getan und dabei folgendes festgestellt:



1.) In meinem Fall werden hier in einer Schleife(im Bild makiert), 2 DWORDS verschlüsselt und am Ende in den Buffer gemoved.
2.) Die Schleife holt sich dabei das zu verschlüsselnde DWORD aus dem EDI und ESI Register(Siehe Anfang while-Schleife).
3.) Dies weißt darauf hin, dass unser unentschlüsseltes Paket, bzw Stücke davon, vorher in EDI/ESI gemoved wurden.

Es muss also vorher eine Funktion existieren, die das Paket in DWORDS(4Byte Stücke) zerteilt, und dann die Funktion solange called, bis alle DWORD verschlüsselt sind.
Ich gehe hier nicht auf den Encrypt-Algorithmus ein, wir wollen hier ja kein Packet-Decrypter schreiben, sondern machen es uns einfach und suchen die Stelle, an der das ganze Paket übergeben wurde.

Step 4: Verschlüsselung umgehen

Setzen wir einen Breakpoint auf den Funktionsanfang können wir durch die Returnadresse(im Bild unten makiert) auf dem Stack zum Aufruf springen.



Doppelklick top of stack:



Dort sehen wir wieder Schleifen, meine Vermutung hat sich also bestätigt. Dort sieht man, dass [esi] und [esi+04] auf den Stack gepusht werden
(die 2 dwords die zu verschlüsseln sind) und anschließend durch lea esi,[esi+0x08](2xsizeof(DWORD)= 0x8), um genau die Größe der 2 DWORDs erhöht wird. ESI hält also den Pointer zum unencrypted Packet.
Danach wird die Verschlüsselungsfunktion gecalled.(call 006C6500)

Hier taucht also unsere Paket das erste Mal unverschlüsselt auf.

Wie auch immer, setzen wir einen BP auf den Funktionsanfang hier, realisieren wir 3 Pointer und ein DWORD.



In meinen Augen sieht die Funktion folgendermaßen aus:

bool CryptPacket(char * sendBuffer(Destination), char* packetUnencrypted(src), void * unknown, DWORD sizeofpacket);

Wir haben nun 3 Möglichkeiten:

1.) Einen InlineHook an dieser Stelle setzen, und die Pakete mitlesen und analysieren.
2.) Selbst Pakete in den buffer schieben und versuchen einen Packet-Sender zu schreiben.
3.) Schauen was die unencrypted Packets in den unencrypted Buffer schreibt und dazu einen "Find out what writes to this address" auf die Adresse setzen,
welche die unverschlüsselten bytes enthält.

Ich folge hierbei dem 3. Punkt, weil wir durch Bearbeitung der Pakete an dieser Stelle evtl andere Pakete ungewollt modifizieren könnten.

Wenn wir nun wie oben einst, die Adresse zur Cheattable hinzufügen und gucken was an diese Adresse schreibt(Find out what writes to this address), und bewegen uns Ingame, bekommen wir einen Eintrag.





Springen wir dorthin und setzen ein BP sehen wir, dass dieser sofort auslöst ohne, dass wir ein Paketsenden veranlasst haben.

Wir scheinen also in einer Funktion zu sein, welche von anderen Funktionen ebenfalls zum Kopieren von Bytes verwendet wird.

An dieser Stelle wird es schwer einen Breakpoint zu setzen und zur Calladresse zu springen, da diese von Tausenden anderen Funktionen gecallt wird.

Da es sich hierbei um eine riesige Funktion handelt, kann man, bevor man den ganzen ASM Code analysiert, vorerst einfach etwas versuchen.
Dazu scollen wir eine Weile nach oben und setzen einen Breakpoint, während unser Character läuft!
Dazu klickt man im Spiel einen weit entfernten Punkt an und setzt während er läuft schnell einen Breakpoint.

Ich habe hierbei Glück und sehe den Pointer(06C4A310), der auf das unencrypted Paket zeigt, direkt auf dem Stack nach dem 1. Versuch,



und kann ohne probleme der Returnadresse folgen:



Falls das nicht der Fall sein sollte, Breakpoint wieder rausnehmen, Character wieder laufen lassen, erneut BP setzen und wieder Stack überprüfen.
Fortgeschrittene können auch eine InlineHook setzen und die Parameter auf dem Stack nach der gesuchten Adresse durchsuchen
und dann den [ESP+0] returnen.

Setzen wir in dieser Funktion(anfang) nun einen Breakpoint, löst dieser nicht sofort aus. Soweit sogut. Wir müssen uns im Spiel nun wieder bewegen oder andere Events wie Item anziehen etc auslösen.

Ich zieh an dieser Stelle mal ein Item an und schaue wie der Stack aussieht() nach dem auslösen des BP's).



Im Bild sieht man, dass auf dem Stack ein Pointer(esp+8) und ein DWORD(esp+4) liegt.
Springt man zu dem Pointer, findet man das Paket vor, welches gesendet wird, um ein Item auszurüsten.

Woher weiß ich das?

Führt man verschiedene Aktionen Ingame aus und checkt dabei immer wieder den Pointerinhalt, merkt man, der (Pointer)Bufferinhalt ändert sich.
Das erste Byte jedoch bleibt bei gleichen Aktion ebenfalls gleich.
An dem ersten Byte, auch Header genannt, identifiziert der Server das Paket.

Springe ich zum Pointer, welcher im letzten Bild sichtbar ist, so sieht das an der Stelle folgendermaßen aus:

PHP Code:
0B 01 02 00 
Warum nehm ich nur die ersten 4 Byte? Weil auf dem Stack ein DWORD übergeben wurde, welches den Wert 4 hat. Dieses gibt die Größe des Paketes an.

Analyse des Paketes:

Nun zum schwierigen Teil, wir müssen herausfinden, wofür die Bytes des Paketes stehen.
Das erste ist einfach: Wie ich oben gesagt habe ist das erste Byte immer der Header, d.h. der muss immer gleich bleiben für diese Aktion.
Da ich ein Item ausgerüstet habe, wir 0x0B der Header des "UseItem"-paketes sein.

Was ist mit den anderen 3?

Das Item was ich vorhin angezogen habe, lag auf dem Slot nr 3 im Inventar.
Versuch ich das gleich doch mal auf Slot 4. Der BP schlägt an und ich seh folgendes Paket an der Stelle des Pointers:

PHP Code:
0B 01 03 00 
Das 3. Byte scheint also die Zelle im Inventar anzugeben, welche ich angeklickt habe.

Durch mehrmaligem Ausprobieren habe ich festgestellt, dass das letzte Byte immer 0 bleibt, und das 2. immer 01.

Wir fassen also zusammen:

1.) Paket für das benutzen von Items besteht aus 4 Byte.
2.) Der Header ist 0x0B
3.) Der 3 Byte ist für den Slot, wobei dieser bei 0 anfängt
4.) Byte 2 und 4 sind für uns erstmal nicht von Bedeutung.


Step 5: Eigene Pakete senden + C++ Source

Auf Basis der Analyse des Paketes oben, können wir nun programmtechnisch folgendes festhalten:

1.) Wir benötigen ein Objekt(Das Paket), was die Bytes enthält, und die Größe(beim UseItem = 4).
2.) Alles was wir dann noch machen müssen ist, den Pointer zu dem Objekt und die Größe(4) an die Funktion zu übergeben.

Für Punkt 1.) erstellen wir uns ein struct.

PHP Code:

struct ItemUsePacket
{

    
BYTE header;
    
BYTE unknown;
    
BYTE slot;
    
BYTE unknown2;

}; 
Für Punkt 2.) benötigen wir folgendes:

I.) Die Adresse zu unserer Sendfunktion.
II.) Den statischen Pointer zur Klasse der Send Funktion.
III.) Die Parameter

Die Adresse der Funktion sehen wir oben im letzten Bild(006E0250).
Den statischen Pointer haben wir noch nicht gefunden, ich gehe aber davon aus, dass ihr das selber könnt, ansonsten mein erstes Tutorial gucken.
Das Klassenregister ist immer das ECX Register, und da ich diesen Wert ebenfalls auf dem Bild oben habe, kann man zu Testzwecken auch diesen nichtstatischen nehmen.

Achtung: Der wird nach dem Programmneustart nicht mehr funktionieren.

Die Parameter sind einmal unser oben definiertes Struct und die Größe(in unserem Fall 4).

Haben wir das alles, können wir nun die Funktion mit dem Funktionspointer definieren, und ganz einfach in unserer Entwicklungsumgebung aufrufen:

PHP Code:


typedef void
(__thiscall *tSendUseItem)(DWORD thisPTRunsigned intpointerToStructunsigned int size);
tSendUseItem SendUseItem = (tSendUseItem)0x006E0250;
DWORD classPTR 03BA0878;

void OurSendFunction(BYTE slot)
{
    
ItemUsePacket iup;
    
iup.header 0x0B;
    
iup.unknown 0x01;
    
iup.slot slot;
    
iup.unknown2 0x00;
    
    
SenduseItem(classPTR ,&iup4);

Wenn ihr die OurSendFunction nun aufruft, solltet ihr das Item in dem gewünschten Slot benutzen/ausrüsten.



.Verkauf´ is offline  
Thanks
15 Users
Old 04/25/2018, 23:23   #2
 
elite*gold: 0
Join Date: Apr 2018
Posts: 4
Received Thanks: 0
Thank you! But without images to understand it is very difficult. Can you please upgrate images?


ZSavich is offline  
Old 04/26/2018, 16:59   #3
 
elite*gold: 5
Join Date: Dec 2011
Posts: 1,082
Received Thanks: 621
#updated
.Verkauf´ is offline  
Thanks
1 User
Old 08/23/2018, 18:57   #4
 
elite*gold: 0
Join Date: Nov 2017
Posts: 2
Received Thanks: 1
thank you for sharing us your knowledges and experiences. "I know the metin2 source is already public and with that there are much more easy ways to locate the send function in this game" u said so i really wonder this way either, would you teach the easier method u said?
xeqoz26 is offline  
Reply



« Metin2 psd logo by Jemilo | - »

Similar Threads
can someone help me in reversing game packetsend function?
10/30/2015 - Aion Main - Discussions / Questions - 3 Replies
thanks in advance
How to fix "Unable to locate data in excutable file" ONLYDBG"
12/08/2011 - Dekaron Private Server - 3 Replies
Thanks All !! I have done!!



All times are GMT +1. The time now is 17:50.


Powered by vBulletin®
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Abuse
Copyright ©2018 elitepvpers All Rights Reserved.