Deprecated: The behavior of unparenthesized expressions containing both '.' and '+'/'-' will change in PHP 8: '+'/'-' will take a higher precedence in /home/iano/public_html/tpforums-vb5/forum/includes/class_core.php on line 5842

PHP Warning: Use of undefined constant MYSQL_NUM - assumed 'MYSQL_NUM' (this will throw an Error in a future version of PHP) in ..../includes/init.php on line 165

PHP Warning: Use of undefined constant MYSQL_ASSOC - assumed 'MYSQL_ASSOC' (this will throw an Error in a future version of PHP) in ..../includes/init.php on line 165

PHP Warning: Use of undefined constant MYSQL_BOTH - assumed 'MYSQL_BOTH' (this will throw an Error in a future version of PHP) in ..../includes/init.php on line 165

PHP Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in ..../includes/functions_navigation.php on line 588

PHP Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in ..../includes/functions_navigation.php on line 612

PHP Warning: Use of undefined constant archive_postsperpage - assumed 'archive_postsperpage' (this will throw an Error in a future version of PHP) in ..../archive/index.php on line 456
[Tutorial C++] Tibia Parser Hook, Sending To Client [Archive] - Forums

PDA

View Full Version : [Tutorial C++] Tibia Parser Hook, Sending To Client



Stepler
07-18-2009, 03:25 PM
Tibia Internals

Hello. It would be good to start with analysing what Tibia provides to us. Below is a code of Tibia packet receiver subsystem. It's a bit simplified but it shows all major parts:


void OnDataAvailable(...) //0x4F63C0
{
recv(...);

while(IsFullPacket)
{
Parser();
}
}


void Parser() //0x45B810
{
while(true)
{
int cmd = GetNextPacket();//0x45B845

if(cmd==-1) break;

switch(cmd)
{
//a big switch to process all packets...
}
}
}

The loop starts when a socket sink window receives an event that some data is available on the socket and OnDataAvailable(...) handler is called. It reads data by recv call and if a received bytes comtain at least one complete packet it starts calling Tibia parser for every packet. Remaining bytes are hold for the next recv event.

The next step in the line is Tibia Parser. It's just a loop with a huge switch with many cases to process every single logical command from the packet. At the beginning it calls a helper function to get next command
to process GetNextPacket(). The purpose of that helper is to manage packets and commands to provide commands one by one as long as one or more unparced packets are available.

GetNextPacket() is the main part of the puzzle. It is that function that can alone provide everything you need to use Tibia receiver subsystem. Internally it checks if we have a complete packet in the buffer, decodes it, sets internal pointer at the next logical command and return it's code (the first byte).
The next time Tibia calls it the next command will be returned. As soon as the packet is processed in full it will shift out processed packet from the buffer, update pointers, decrypt next available packet and so on...

It's clear that if we take GetNextPacket() under control then we can call Tibia parser as our own function and force it to get anything we want. And that's what we will do today.

You can also notice the name GetNextPacket and not GetNextCommand. Don't be cofused. It's because of historical reason, because the function works with both commands and complete packets.

Although before we start I would like to note that Tibia works with buffers in some kind of standardized way, by using structures to store all related info. Therefore it's good to use the same way in our code. Here is the structure,
I call it TPacketStream.

struct TPacketStream
{
LPVOID pBuffer; //pointer to the memory buffer
DWORD dwSize; //size of the buffer
DWORD dwPos; //current position to work with the buffer
};

All function in both subsystems Receiver and Sender in Tibia work using those streams. So it's useful to have it in our Tibia programms
whatever we code, be it a bot or not :)

The stream we need today is a Tibia parser stream. It's located at this address: 0x78BF24.
Let's start.

Sending plain decrypted packets to client

At first we need to init all data we need in our work. It's addresses of Tibia functions, stream and additional flag.


//addresses
#define FUNC_PARSER 0x45B810 //8.50
#define CALL_GET_NEXT_PACKET 0x45B845 //8.50
#define ADDR_RECV_STREAM 0x78BF24 //8.50

//structure Tibia uses to work with buffers
struct TPacketStream
{
LPVOID pBuffer;
DWORD dwSize;
DWORD dwPos;
};

//define types of functions we want to use or hook
typedef void TF_PARSER();
typedef int TF_GETNEXTPACKET();

//pointers to call original function
TF_PARSER *TfParser = (TF_PARSER*)FUNC_PARSER;
TF_GETNEXTPACKET *TfGetNextPacket = NULL;

//pointer to Tibia packet stream
TPacketStream * pRecvStream = (TPacketStream*)ADDR_RECV_STREAM;

//flag indicates that we are sending to client
BOOL fSendingToClient = FALSE;

The next usual thing we need is a function to hook call instruction in memory.
It injects a new address as operand and stores the old one to call original function later. Plus below is a line to actually to actually do injection.


void HookCall(DWORD dwCallAddress, DWORD dwNewAddress, LPDWORD pOldAddress)
{
DWORD dwOldProtect, dwNewProtect, dwOldCall, dwNewCall;
BYTE call[5] = {0xE8, 0x00, 0x00, 0x00, 0x00};

dwNewCall = dwNewAddress - dwCallAddress - 5;
memcpy(&call[1], &dwNewCall, 4);

VirtualProtectEx(GetCurrentProcess(), (LPVOID)(dwCallAddress), 5, PAGE_READWRITE, &dwOldProtect);
if(pOldAddress)
{
memcpy(&dwOldCall, (LPVOID)(dwCallAddress+1), 4);
*pOldAddress = dwCallAddress + dwOldCall + 5;
}
memcpy((LPVOID)(dwCallAddress), &call, 5);
VirtualProtectEx(GetCurrentProcess(), (LPVOID)(dwCallAddress), 5, dwOldProtect, &dwNewProtect);
}

//we hook a single CALL instruction in packet parser
//that calls GetNextPacket function
HookCall(CALL_GET_NEXT_PACKET, (DWORD)&OnGetNextPacket, (LPDWORD)&TfGetNextPacket);

Now let's write a new functionality for hooked GetNextPacket. Here we check if we are sending to client then we skip real body and just follow the original behavior by adjusting position in the stream and returning a first byte of the command. Otherwise we call original function unchanged.


int OnGetNextPacket()
{
if(fSendingToClient)
{
if(pRecvStream->dwPos < pRecvStream->dwSize)
{
//read the first byte of command to return from the function
BYTE bNextCmd = *((LPBYTE)pRecvStream->pBuffer + pRecvStream->dwPos);

//increase stream pointer since we've read the first byte
pRecvStream->dwPos++;
return (int)bNextCmd;

} else return -1;
}

return TfGetNextPacket();
}


Once GetNextPacket is under our control. We are free to play with parser.
Let's change the current packet stream for the stream with our own packet, call Tibia Parser function directly and then return back the old stream.
The result - a wonderful SendToClient.


void SendToClient(LPBYTE pBuffer, DWORD dwSize)
{
//turn on new behavior for GetNextPacket
fSendingToClient = TRUE;

//store Tibia recv stream
TPacketStream StreamHolder = *pRecvStream;

//point the stream to our buffer with packet
pRecvStream->pBuffer = pBuffer;
pRecvStream->dwSize = dwSize;
pRecvStream->dwPos = 0;

//call Tibia packet parser directly
TfParser();

//restore Tibia recv stream
*pRecvStream = StreamHolder;

//turn off new behavior for GetNextPacket
fSendingToClient = FALSE;
}

As you can see the complete code is very simple. It consists of two really small functions and involves only three addresses that are very easy to find and update. As a bonus I have looked into old Tibia and can confirm that the way it works wasn't changed within many years. So it is the most prefered
way to work with Tibia.

Receving separate packet commands in your app

Most of the projects for Tibia requires the ability to receive and process packets. It is very complex task to create a complete and working Parser for all possible packets. So instead of processing everything we can use Tibia internals to receive all commands separately, process only those we need and skip a complex map packet for example (and still be able to not miss anything).
I believe it's the best way to create an ideal bot core, so far I haven't seen anything more comfortable. Let's modify our function by a few lines to get the desired result.


int OnGetNextPacket()
{
int iCmd = TfGetNextPacket();
if(iCmd != -1)
{
LPBYTE pCmdBuffer = (LPBYTE)pRecvStream->pBuffer + pRecvStream->dwPos - 1;

//here you can process every single command from the packets
//pCmdBuffer is a pointer to the buffer with the command
//example: MyProcessCommands(pCmdBuffer);
}
return iCmd;
}

Very easy but solves a lot. Unfortunately by design you can't get the length of the current Command, since only Parser knows about that, but it is still possible to add some logic to process the commands with a single step lag
and calculate sizes. (I leave this for advanced programmers, there is no special magic about that)

Receving complete decrypted packets in your app

Ok, if you create a different kind of application (TibiaCam?) and don't need to process every single command but have to record complete packets
then you can do that as well. Let's modify our function a bit more.


int OnGetNextPacket()
{
int iCmd = TfGetNextPacket();
if(iCmd != -1)
{
if((pRecvStream->dwPos-1) == 8)
{
LPBYTE pWholePacket = (LPBYTE)pRecvStream->pBuffer + pRecvStream->dwPos - 1;
DWORD dwPacketSize = pRecvStream->dwSize - pRecvStream->dwPos + 1;

//now you can process complete packet
//example: MyParsePacket(pWholePacket, dwPacketSize);
}
}
return iCmd;
}

The things we have added is a check for current position in the stream. Since there are additional data at the beginning of the packet (totalsize, adler, size) with a constant size of 8 bytes, then every time the position is equal to 8 we have a new packet. Please also note that we should go back 1 byte before we check because original function has already read one byte.

Synchronization

The only thing that I moved out from this tutorial and a demo is thread synchronization part. Although the packets are synchronized between each others perfectly now, you also have to Choose a correct place to call SendToClient(LPBYTE pBuffer, DWORD dwSize). I advice to use socket
sink window procedure to do all work. The sequence to achive this could be:

1 - FindWindow OR hook for WSAAsyncSelect OR sink handle from memory
2 - SetWindowLong(hSink, GWL_WNDPROC...)
3 - New handler for events, SendToClient inside

Demo Application

I have attached a demo application. It consists of Injector.exe and Hook.dll and source code. It you run it in Game you will see a test message sent back to client every 2 seconds. The source code is very simplified, class-free and without thread synchronization.
The project is for Visual C++ 2008.

Improving

There is a way to make the technique more smart and able to replace and delete packets, Tibia provides everything we need for that. It is not trivial though, therefore I won't mess with it in this article. Also Tibia sender works in the similar way and can be hooked as well.

Conclusion

As you see the code I have described has one page in size with three addresses involved but it does everything we ever need from Tibia receiver. This technique was used with a success in a bot and tibiacam development and was tested very well.

History
Below are some topics you can read to know more about Tibia internals.
http://www.tpforums.org/forum/showthread.php?t=3327
http://www.tpforums.org/forum/showthread.php?t=3024
http://www.tpforums.org/forum/showthread.php?t=2399
We have much more, but I could not find those I wanted.

Thank you.
Stepler.

And sorry about my english. I will re-read my article and try to fix mistakes later.

asta
07-18-2009, 04:09 PM
Hehe, simply perfect. I can't say anything else.

DarkstaR
07-18-2009, 05:11 PM
Stepler, your are truely amazing when it comes to hooking. Honeslty, this is amazing.

Lemme ask one question:
I do all my sycronization for sending packets in the PeekMessage loop.. Would this syncronize fine there as well?

My virus protection delete the file when I download it.
I guessing its the injector

Ian
07-18-2009, 08:39 PM
Very nicely written. This seems to be the most straightforward method to use and update that we have seen so far.

I think the wiki is a must simply to showcase your tutorials. I can't help but get the feeling that you have more knowledge of the client's source than you let on, especially when you were talking about the legacy naming of the function.

All the same, we are very lucky to have you here at TP!

DarkstaR
07-18-2009, 09:58 PM
Very nicely written. This seems to be the most straightforward method to use and update that we have seen so far.

I think the wiki is a must simply to showcase your tutorials. I can't help but get the feeling that you have more knowledge of the client's source than you let on, especially when you were talking about the legacy naming of the function.

All the same, we are very lucky to have you here at TP!

All who think Stepler secretly works for cipsoft but comes here because he really doesnt care about anti-bot measures, say I!

I! I!

Jk, but really, he is amazing at reverse engineering. It amazes me.

Ian
07-19-2009, 12:31 AM
All who think Stepler secretly works for cipsoft but comes here because he really doesnt care about anti-bot measures, say I!

Haha, I was going to say that, but thought it a bit bold, and didn't want to lose him if it was true! But I agree, he is the best published reverse engineer we have!

Farsa
07-19-2009, 01:22 AM
Once again Stepler impresses us. Thank you very much! Because of this tutorial of yours i had something to do the whole day haha

I have "ported" your code to C# without any dll injection, just used a method similar to packet.dll's. Although I haven't polished the code pretty much nor tested it a lot, it seems to be working fine without crashes. TY once again

DarkstaR
07-19-2009, 01:35 AM
I have done the same with VB, but was scared to hit syncronization issues and decided to inject it with a dll. I think keeping it in an injected .DLL will be much more effective, personally.

Anyways, I'll share my addresses for anyone wanting to use this with older versions



'Parser hook/own packet injection 842
BaseParserFunc("8.42") = &H45B110
BaseGetNextPacketFunc("8.42") = &H45B145
BaseRecvStreamPtr("8.42") = &H78ADE4

'Parser hook/own packet injection 841
BaseParserFunc("8.41") = &H45ACA0
BaseGetNextPacketFunc("8.41") = &H45ACD5
BaseRecvStreamPtr("8.41") = &H785D84

'Parser hook/own packet injection 840
BaseRecvStreamPtr("8.40") = &H784CFC

BaseParserFunc and BaseGetNextPacketFunc were both easily found in 8.42/8.41 using OsQu's Olly method, didnt woth for 8.40 though. Theres another way that I found BaseRecvStreamPtr, which worked the same way for all versions. But, it ruins the fun if I tell you guys how to find it easily :P

asta
07-19-2009, 10:14 AM
Sticked it.

Stepler
07-19-2009, 01:16 PM
Hey guys.
Thanks to all of you.


I do all my sycronization for sending packets in the PeekMessage loop.. Would this syncronize fine there as well?

My virus protection delete the file when I download it.
I guessing its the injector

It's hard to say without digging into sources. Most likely you are safe with your method, since internal Tibia synchronization linked with message loop a lot.

As for the false detection, it is normal thing for such type of app. McAfee and NOD32 shows possible detection if heuristic more is on. Usually within next 2-6 days more antiviruses should react, it is time they need to get files from VirusTotal.com and add into the base.

Of course the demo is clean. If you are not sure, then you can run a demo under VirtualPC or compile the sources. I have attached all sources you need for Visual C++ 2008.


I have "ported" your code to C# without any dll injection, just used a method similar to packet.dll's

Oh, that's cool, I have downloaded it right away. This topic is very interesting for me, since I'm leaving C++ to C# in October. So I'm trying to get and see everything you guys made in C#. Very helpful ;)

Thanks

DarkstaR
07-19-2009, 03:04 PM
I know its clean, I trust you man ^^

Just letting you know it gives a false positive



And your leaving C++?
You so good at it, why even bother? haha

Yaboomaster
07-19-2009, 03:08 PM
omg Stepler u are rly great programmer.

I have only one question?
Names of functions and procedures are only your invention or maybe u have some informations about client source? xD

Farsa
07-19-2009, 10:24 PM
Oh, that's cool, I have downloaded it right away. This topic is very interesting for me, since I'm leaving C++ to C# in October. So I'm trying to get and see everything you guys made in C#. Very helpful ;)

Thanks

good to know :D the hard part for me was making the asm code, but you probably know it better and/or will find a better way eventually hehe

jeremic
07-19-2009, 10:36 PM
Nice Alex :) good tutorial its also an interesting fact to know that CIP has changed their compiler in the latest tibia, if someone hasent noticed that.

beziak
07-20-2009, 08:11 AM
Thanks Stepler! This is exactly what i needed!

One question:
Can you post how to get command one by one with it's size?
I think its possible but I get lost with "pos" and "size"

Stepler
07-22-2009, 01:43 AM
DELETED:
I have deleted this post because the code I post for Beziak had a small mistake. I shouldn't hurry, sorry. I will post updated version soon.
Thanks

beziak
07-22-2009, 07:28 AM
Hi Stepler!
Its working but sometimes its not catching all command, i dont know why.

My suggestion is replacing code in section
Receving complete decrypted packets in your app


int OnGetNextPacket()
{
int iCmd = TfGetNextPacket();
if(iCmd != -1)
{
if((pRecvStream->dwPos-1) == 8)
{
LPBYTE pWholePacket = (LPBYTE)pRecvStream->pBuffer + pRecvStream->dwPos - 1;
DWORD dwPacketSize = pRecvStream->dwSize - pRecvStream->dwPos + 1;


//now you can process complete packet
//example: MyParsePacket(pWholePacket, dwPacketSize);
}
}
return iCmd;
}

Now it works;P

beziak
09-30-2009, 10:15 PM
I've got an idea how to get length of each command,
Wait patiently for my code.. I will post it here soon!

DarkstaR
10-01-2009, 01:48 AM
I've got an idea how to get length of each command,
Wait patiently for my code.. I will post it here soon!


Why is the length needed? You can simply parse the packets you need, and keep offseting the pos. Then return the next command.

This way you dont have to worry about getting the length of packets you dont need to parse, only those you need to. Thats how I do it.

beziak
10-01-2009, 05:51 AM
REMOVED~~ DUE TO SMALL BUG

Farsa
10-01-2009, 12:26 PM
you are getting the length of the previous command, not the current one
but hey, grats! HURRA!

beziak
10-01-2009, 01:56 PM
Don't post if you don't know how parser work.
Analyze it again and post ;)

After parsing one command parser is jumping to my function so im getting len of it ;]

beziak
10-01-2009, 07:00 PM
Here is addresses for Tibia 8.52:


#define CALL_GET_NEXT_PACKET 0x45B8E5 //8.52
#define ADDR_RECV_STREAM 0x78CEE4 //8.52
#define FUNC_PARSER 0x45B8B0 //8.52

Lucas
01-15-2010, 12:32 AM
I'm having problems to see where are the attached files. I don't know if it is the new look of the forum. Could you PM me with the link to the files? Or can anybody help me out? I really want to work with proxies :-/

DarkstaR
01-15-2010, 02:14 AM
Gotta check old attachments, fucked up when forum was ported from VBulletin

http://tpforums.org/attachments/

Lucas
01-15-2010, 09:48 AM
Thanks DarkstaR. I found the file.

CristoferMartins
04-09-2010, 05:20 PM
Hi,i want to do a question:
I'm using the thing thats get all packet when its complete and then parse it,but if i want to block the full packet i just need to Return -1 in the GetNextPacket?
Thanks :D

Eduardowm
12-22-2010, 07:58 PM
This is correct?


for (int i = 0; i < dwPacketSize; i++)
Logger << "0x" << std::hex << (int)pWholePacket[i] << std::dec << " ";


Hmm, something is wrong, I'm getting (sometimes) more than one command from the server in the whole package (in the same package).

Example logger with the problem:
0x1E 0x1E 0x1E

Should be:
0x1E
..
0x1E
..
0x1E

Not with the ping packet, it was just an example.

Maybe if I got the packet header I solve this problem. But how can I?

Anyway,
Thanks.

Abdelrahman
07-10-2011, 04:53 PM
I see that all of you guys are professional guys :D So can you help me with this?
Well , firstly I'm sorry if I'm writing this on the wrong part of forum but I have searched I didnt know which part I should type in so here is my problem :
I tried to open my elf bot on a custom client the msg (please run tibia first) appears I asked about that someone told me that you have to change tibia class name to ( Tibia) Well I did it should now work but it doesnt. I guessed it doesnt work because the ot when you download the custom client of it its only get 2 file which we open tibia from it and file.dll there is no data or file.spr or graphic but i noticed that the size of the icon which we open tibia from it is 41 mb so i think its includes them so how to use bot with it!! I wish you understand me and if you didn't. please try to make bot work on ( Swev.se ) It's the ot and tell me how to do it or give me link to download the client which you have made ! Thanks . =)

jo3bingham
02-05-2012, 02:39 AM
If anyone could supply me with Stepler's original attachment, I would be grateful.

ufo
03-20-2012, 07:51 AM
Does anyone at least remember the name of the attachment ?
Can't find it in the old attachments list (link posted by DarkstaR).
I didn't check all files 1 by 1 tho - not enough free time to do so...

ufo
03-26-2012, 01:54 PM
One amazing thing about using this for CAM like software (what I already have achieved like few minutes ago :P) is that you don't have to make client connect anywhere in order to playback a cam file.
You simply send the first map packet and it loads up like you just logged in ^^

My question though:
How to get back to the main menu (with "Enter Game" button ect) ???
Setting connection state does not help (writing 0 to the connection state address that is).
I've found packet type 0x14 in OT sources that's meant to be logout but Tibia just excepts and stays "logged in".

Any ideas ?

Blaster_89
03-26-2012, 02:25 PM
One amazing thing about using this for CAM like software (what I already have achieved like few minutes ago :P) is that you don't have to make client connect anywhere in order to playback a cam file.
You simply send the first map packet and it loads up like you just logged in ^^

My question though:
How to get back to the main menu (with "Enter Game" button ect) ???
Setting connection state does not help (writing 0 to the connection state address that is).
I've found packet type 0x14 in OT sources that's meant to be logout but Tibia just excepts and stays "logged in".

Any ideas ?


Assuming you need this for playing back recordings, just close the server socket.

ufo
03-26-2012, 05:37 PM
The fun part (what I was saying above) is I don't use sockets.
Unless they open automatically in Tibia client after sending first map packet.

Step by step - what I do - to better show my prob:
1. Start Tibia client.
2. Start cam player.
3. Open cam file.
4. Send first packet from the file to client (PacketToClient).
5. get next packet and so on and so forth...

Notice there's no part about making any connection with client.

So... how do I close socket that I didn't open before ?


Unless, like I already said, I didn't get your response :/

Blaster_89
03-26-2012, 05:57 PM
The fun part (what I was saying above) is I don't use sockets.
Unless they open automatically in Tibia client after sending first map packet.

Step by step - what I do - to better show my prob:
1. Start Tibia client.
2. Start cam player.
3. Open cam file.
4. Send first packet from the file to client (PacketToClient).
5. get next packet and so on and so forth...

Notice there's no part about making any connection with client.

So... how do I close socket that I didn't open before ?


Unless, like I already said, I didn't get your response :/


You don't need to hook the socket for playing recordings.

Overwrite login servers
Set up a TCP listener
Log in automatically using Send/PostMessage
Start sending packets to the client

ufo
03-26-2012, 06:04 PM
I am sending packets to client.
Client shows what was recorded.
Yet I DO NOT touch any sockets or any connection with client AT ALL.

I just inject the packets with PacketToClient (which is adapted from source posted here by Stepler).

But I've just stumbled upon a post by Snowak saying about closing Tibia client socket (force logout - link: Force logout? (http://tpforums.org/forum/thread-4832-post-53844.html#pid53844)).
Might be the socket opens up in client autmatically on packet arrival.
I'll be looking into this and will let ya'll know :)


[edit]
Thanks for your input though - I DO appreciate it :)

[edit2]
The socket structure does not get initiated :/
Dunno how to get around this...

theafien
04-30-2012, 10:18 PM
Sorry to revive the topic, but does anyone have attachments the post?

benji69
06-06-2012, 02:53 AM
Excellent tutorial! I am also looking for the hook source in this tutorial if anyone could please upload it :) would be much appreciated.

EDIT: Might have found it in the attachments (could be wrong but I think this is it).

Czepek
06-08-2012, 05:35 AM
This attachment is wrong, it isn't it. It is Stiju's "Print Text hooking". Also if you want the source code, find for "TugBOT" of DarkstaR on googlecode. There you'll find it.

Regards, Czepek!

benji69
06-10-2012, 09:22 PM
This attachment is wrong, it isn't it. It is Stiju's "Print Text hooking". Also if you want the source code, find for "TugBOT" of DarkstaR on googlecode. There you'll find it.

Regards, Czepek!


Ah ok, thanks!

fraxe
01-31-2013, 05:44 PM
Does somebody have Stepler's hook proxy source? Google code doesn't work, i can't download any file. :(

ResQue1980
02-02-2013, 10:58 AM
Thanks a lot for the walk through.

belzebub
06-12-2015, 04:34 PM
Hello, anyone could show me where I can find the attachment?

mululu
03-22-2016, 04:42 AM
Thanks a lot for the walk through.