[Guide] Extracting Built Packets in Silkroad

On viernes, 13 de agosto de 2010 0 comentarios

Extracting Built Packets in Silkroad

I. Purpose

This guide will provide a complement to the previous guide of extracting the parsed packets in Silkroad. This time, we will learn how to extract packets the client sends the serer as they are built. Because most of the concepts in and theory are the same, this guide will be much shorter than the previous so please refer back to that article for all the nitty-gritty details.

An article like this is also just as important to understand as the previous was for any development that makes use of packets. If you are writing a client based tool, a clientless tool, or even a Silkroad emulator, you need to understand how packets are built and the format they follow. Using the techniques provided in this guide should help you achieve that.

I wanted to do make this a separate article as the previous one was getting pretty long itself. With all of the concepts explained already though, we can move through this one pretty fast. This lies in line with my first guide explaining how each article will relate to the others in one way or the other and once we have our base knowledge base established, we can do fun stuff. In addition, there are a few tricky parts in replicating the client's logic that we have not come across yet.
 
II. Requirements
 
If you have been keeping up with the previous guides, you should be able to follow everything presented here. There are no new concepts used. There is a bit more assembly logic used here though, but everything is based off our previously established concepts of codecaves and inline patching.

In order to be able to follow along this guide, you will need:

 
  • OllyDbg 1.10 (or equivalent)
  • Visual Studio 2008 (or equivalent)

 III. Theory


As I discussed in the previous guide, most games use code that greatly simplifies the programming challenges that lies ahead of them. When programming a networked game, there are a lot of things the developer must take into account when designing code. When it comes to packets, the developer has to figure out a way to make sure the client cannot spam the same packet over and over flooding the server.

Silkroad does just this with a simple packet opcode checking function. While I have not explored the internals of this function in a great degree at this time, just understanding that such a function exists can greatly help in our client reversing endeavors. The client will first check the opcode to see if such a packet can be sent. If it cannot, then the function usually skips execution by jumping over the packet building code.

Assuming the packet checking function is successful, the client will then build a new packet with the given opcode. Similarly to the opcode checking function, I’ve not actively explored the internals of how packet objects are constructed, but given I know C++ programming, it’s not hard to imagine how the process goes. We can hook this function to get the opcode of our current packet that is being built just as we were able to easily get the opcode of the packet that was being processed before.

Similarly to a ReadBytes function there is also a WriteBytes function. We can hook this function and simply rip out the data as it is being passed into the packet’s data buffer. By looking at multiple sections of code that build packets, we can also look for some final function that is called on the packet object to know when we are done building the packet. We know such a function has to exist because the client has to put in security bytes into the packets and actually dispatch the packet for sending to the server.

So, with this theory in mind as well as the knowledge gained in the previous guide, we can get started!

IV. Implementation

In order to get started, we need to know a packet opcode that is sent. By now, this information can easily be obtained, so there is no need to work from scratch to obtain one. If we had to, we would just breakpoint on a WSASend call and trace back the buffer until we could determine where the opcode was in the packet.

For this example, I will be using the character action packet, which uses an opcode of 0x7017. In order to find where the functionality lies for a packet that is sent to the server, we just have to search for the opcode! To begin our search, right click on the main assembly window and choose Search for -> All constants. Type in 7017 under the hexadecimal box and hit Ok. We get a new window that has search results in it and we can see we have a couple of entries. Double click on the first one to go to the client’s location.

We arrive in the main function that builds the 0x7017 packets. We know this because we see our opcode listed. Using what we learned in the theory section, we should first see a function call that checks to see if the packet can be used or not. We find that to be the case because there is a conditional jump after the first opcode but before the second that skips over a large part of the function. So far so good!

Next, we should be finding a function that writes bytes to the packet buffer. If we remember how the ReadBytes function was setup, it took two parameters, the buffer and the size. The WriteBytes should be no different. If we were to trace the function, we would quickly find the function and be able to identify the parameters.

Finally, we need to find a common function that is called after the packet building is complete. In order to do this, we will simply follow the call to the opcode setting function and look at all the other addresses that make calls to the function. We should be able to find a common function that is called in all of them and we do.

Here is an annotated screenshot of the entire function:
 

Now that we know the logic of how packets are built. All we have to do is setup the code to extract the data. This code is going to be really similar to the previous guide’s code, so I will not go through it in detail.


However, there were a few very tricky things to figure out during the process. Packets that are spanned across multiple opcodes for example, such as group spawn, make use of our packet writing functions! I came up with a little approach that should fix that issue for us.

Next, there was a problem replacing the client logic in the codecave. Since the code made use of the stack, it would incorrectly compile in Visual Studio this making the client disconnect. To fix this issue, I just used more commands to replicate the logic as it would be if it was done the long way.

Issues like these are not uncommon when working with game clients. When you have unexpected behavior and are sure your code is right, you have to be sure you do not have a problem like this. When I first wrote the code I was sure it was right and tracked the problem down to the WriteBytes codecave. I figured the stack operation should work fine, but it doesn’t.

After replacing the code, it worked perfectly! Chalk that up as a lesson learned of how tricky this development can get at times. The code for stealing the opcode seemed to work just fine, so I'm not really sure why this wouldn't either.


V. Conclusion

You should not have a good idea of how it is possible to let your game client do most of the hard work for you, whether it is extracting the received or sent packets. By following this guide, you should be able to greatly speed up packet related development efforts as all you have to do is trigger packets and save how the client parses them. Using this information, you can further your understanding of the client and track down the specific areas in the client responsible for generating certain packets. All you have to do is search for the opcode of interest!

That wraps up this guide! Between this guide and the previous, you should be able to work a lot more efficiently now with building a packet database for use in your programs. I hope you found this it informational and beneficial. Stay tuned for future guides!

Drew “pushedx” Benton
edxLabs

0 comentarios:

Publicar un comentario