Fond Memories: WoW

Came across a blog post a few weeks ago where someone was describing the methods he used to sniff WoW’s (World Of Warcraft) packets. That led me to think that I should write down about the time I have been developing the “WoW Reverse Engineering Tool“. It’s been nearly 3 years since then but hey – I did not know or care about blogging then.

Before I start – I am not really that passionate about WoW anyway. What attracted me was the challenge in writing such a tool.

The beginning

I have started participating in “underground” WoW community about 4 years ago when me and my friend (later a whole team) started gathering a database of quests for the game from all the possible sources we could get our hands on. We then developed a “compiler” that would transform that DB in the format needed by (long time defunct) WoWEmu. The first version (the IRPG QDB) would compile TCL scripts required by that particular server. Later on I realized we could bypass the whole TCL scripting by “injecting” our own DLL into the executable and simulate running of scripts. In reality all the calls to those TCL scripts would be handled directly in our DLL so the speed would be much much greater. That also gave us a big leap forward regarding the quality of the quests – we had the possibility to store much more information in memory which would not be disposed after the TCL script would end it’s execution.

In all this time (about 4 months) I learned a lot about WoW protocol in general so teh obvious way was to start working on a new server software. Here came along Ludmilla. It was a C++ project derivative from another GPL’ed sources we had laying around. My task was to write a good quest engine for the server while others would work on their tasks too. I will not detail too much about what happened except that the Ludmilla developers did not want to release the source back which was a breach in the GPL. This, and a talk to a member of WoW community paved the way for the MaNGOS project. A lot of things happend in the mean time so we have decided to start a new WoW server written entirely in Delphi – YAWE.

The need for data

Yes, right about the same time I was working on Ludmilla the need for a good packet sniffer became even more visible. In those days there were quite a few teams working on their WoW servers and only one IIRC had a semi-working packet sniffer. All that determined me to start working on WoWRE Tool.

It is important to understand that I already had good knowledge of the underlying protocol and I had access to WoW server implementations – this made my life much easier. It would have been a much bigger pain If we would have started on absolutely zero knowledge.

What would the new sniffer provide? Well, a good logging for all the traffic going on between server and client and a way for us to grab WoW world content and store it in a database. You see, while you would walk around in the game, the server would send you mostly 100% complete information about all objects that come into your range. We would then take that information and pack it in the required format.

Another problem was WoW’s “warden” software. It was a process that would monitor all other system processes and see which one is interferring with the WoW executable. This way they ensured you wouldn’t cheat. This made us realize that we needed a real TCP based “Man-In-The-Middle” approach so that we don’t get detected!

The realm server

Before your WoW could connect to the actual world it would connect to a general realm server. Here the user would authenticate and select the actual world server he wants to connect to. Once selected WoW will receive the IP:Port of the world he wants to connect to. One very important aspect is that while communicating with the realm server, both parties generate a 40 bytes hash key by using the SRP6 algorithm. I will not dig too deep into this but know that it’s a very messy stuff and it takes a lot of tries to get this part right (if you’re doing a WoW server).

The protocol of the realm server is not yet encrypted so nothing too fancy here. I came up with a working version on this part in a few days. The only problem I had was the realm selection – there are many world servers (I mean many!) and the client can connect to all of them if it desires so. But I need to intercept that traffic too (actually that was the whole point). The decision was to buffer all that info on the sniffer (it took quite a few packets to gather all the list) and change all the IP:Port fields to point to the sniffer. The client can select any realm and try to connect but still be redirected to my software. This lead to another problem – the client may want to connect to any world he wants but still, the actual world selection must be made on the sniffer itself. But it wasn’t a big deal because people actually play on the same world most of the time so the sniffer would auto-select it.

After all this intermediary work, came the complicated part – communication with the world!

The world server

The protocols used by the realm and world servers was completely different. My opinion is that the realm server used a very old protocol (probably something from Diablo days).

OK, so now we’re talking with the world! But there is just garbage on the line. Nothing recognizable! The problem was that nasty SRP6 stuff. After a few tries I realized it is going to take too long to actually negotiate all this key stuff, so here came the best idea ever – I don’t even have to simulate the SRP6 negotiations. Let the real server do it. But how would I then get that 40 bytes key? SRP6 ensures that both client and server would generate it on their own so the sniffer has not way of knowing it. And we really need that key for the world server – it’s used to encrypt all the data.

The answer came from the knowledge of the protocol of course:

Client side request looks like: Packet Length: 2 Bytes, Packet ID: 4 Bytes, … Packet data

The ecryption is a XOR-like method where the next XOR operation is dependent on the previous non-XOR’ed data. Also, only the size and the packet ID are encrypted.

The method I used is based on the fact that initial client-server dialog looks more like question-answer, where teh client is initiating the dialog every time. This ensures that each separate packet is flushed individually resulting in a predictable pattern. The net-result is this:

  1. Catch the client-side packet. It’s real length is most probably equal to the TCP packet length.
  2. Subtract 2 from that TCP length and change the byte order.
  3. Read the first 2 bytes from the packet and apply the XOR-like algorithm on them until they match the length we expect.
  4. This way we have 2 bytes out of 40 of the key! Put them into an array at the required position and add another 4 to that index so the next 2 pieces are properly identified (do not forget that the packet ID is also encrypted which results in 4 key pieces being used also – but we don’t have those yet).
  5. Repeat this process until all key pieces are in place!
  6. When the key is ready take all the packets (that were previously buffered somewhere) and actually decrypt them properly.

Note that the predictability of the packet sizes works only on client-side packets, because the server could send more game packets in one TCP chunk. This method works in 60% of the cases. Why so low? Because the client sometimes issued a very large packet that was split in 2 TCP chunks – this of course resulted in corrupted key.

So why stop here I said? Maybe we could get rid of that 60% problem and even speed-up things! I came up with a predictability pattern that would involve the packet ID’s. I knew which packets the client were sending so I could predict them in the initial client-server dialog. Well … that turned out to be quite a challenge:

  1. Even if I knew which packets are going to appear in the dialog, they did not come in the same order.
  2. Sometimes if you wait too much time in the character selection dialog, too many PING packets would be sent so that also poses a challenge.
  3. That 60% problem – well the packet that is split was actually easy to detect even without the encryption: It was a zip-packed data chunk. So I could try to unzip it and see if the result looks like what I expect it to look.

The fast-key method was born! This method will detect 6 pieces of the key in one packet so that means we can collect the whole key just in 7 packets! The only down-side of this method is that I needed to update the tool for each release of WoW, because most of the time new packets were introduced.

Other challenges

While the actual sniffing was a big challenge, we (at YAWE) needed much more gathered information, so I decided to extend the tool into much more that just a sniffer:

Packet helper mode: This mode looks like a calculator actually. You can enter a string for example in a edit box and it would automatically be translated into all kinds of data types – hex string, bytes, almost all data types that could have been found in WoW packets. It actually helped us a lot to identify things in newly introduced packets.

Update Field Extractor: These fields are important in the whole protocol because they represent particual attributes of objects (e.g. Health of a player). I’d actually say that most of the traffic results in packets that depend on those fields. There are a lot of them out there and even more are being introduced with each new update. It’s imperative that the server developers know about them and what they represent. I knew there was a possibility to extract them form the WoW.exe executable but noone seemed to share that info! Well, suffice to say that it’s pretty easy actually: There is a table in the exe file that contains the names of all those fields and another one that contains their interger IDs and their types. A day of hacking and we’ve got ourselves an extractor that can generate C++, Delphi and some other formats of constants. Not sure why Blizzard would keep that info in the executable but there have been even times when debug build were released with all the packet names in the executable.

Data file reader: A simple explorer for the data files that come bundled with the WoW. You can extract file if you want and even read DBC files which contain very important information for the server developers.

3D terrain converter: It’s a tool that reads WoW’s map files and generates a new format suitable to be used in the server. Those files provide a mapping function Z = function(X, Y) that can be used to calculate positions and other important 3D things.

Conclusion

If you’re passionate about reverse engineering and like to play with other programs’ brains – DO IT. Nothing can replace the old-school “getting your hands dirty“.

Anyway, the if you like to take a look at the sources of RE Tool you can find it below:

[download#3]

You May Also Like

About the Author: Alexandru Ciobanu

5 Comments

  1. Hey there.

    I must comfess, pretty good story and information. I started yesterday a very simple server but, SRP-6 is killing me. For example, at the 1st server’s parket reply (AuthLogonChallenge) we know the “standard” values: (00 00 00 [32] 01 07 20 [32] [32] [16] 00)
    The second 32byte value is always the same.. but what about the first and third one? A description from a site says:

    * 1st char[32] = Public ephemeral value (SRP)
    * 2nd char[32] = A large safe prime (N = 2q+1, where q is prime). All arithmetic is done modulo N.(SRP)
    * 3rd char[32] = User’s salt
    (http://www.madx.dk/wowdev/wiki/index.php?title=Packets)

    Also i’ve found somewhere that the 16byte is a CRC.. seems like i still have to learn lots staff 🙁

  2. I just watched the rest of your Blog. You’re amazing delphi programmer! ..Now i’m just wondering, would there be a chance to publish something upon SRP or Salt? 😛

  3. @Grim
    Thanks for the kind words.

    To be honest I remember very little now about the actual code. I would suggest you head over to MangOS project home and download their sources. They have a working implementation of this.

  4. @alex
    Yeah i know, currently studying their source along with srp’s protocol design (http://srp.stanford.edu/design.html) but just by looking in c++ code feel like i’m damaging my brain cells more and more. 😀 I’ll work it out tho, thanks for your reply (i wasn’t expecting any since the topic’s date is uhmm.. a years old? 😛 )

    Keep up the hard work with your projects!

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.