📜 ⬆️ ⬇️

Parsing protocol World Of Tanks

Part One: Bicycle Inventory Toolkit

Why and why: long and optional preamble

It’s good that I had little experience in modding games - a couple of custom sights for Deer Hunter 2005 and an “unlicensed” non-client VATSIM / FSD with the accompanying “hacking” of the latter’s protocol. It’s even better that I never had to plunge into any time-consuming and lengthy debugging and disassembling. That is, with IDA and OllyDBG I am superficially familiar, but not like with daily work tools.

I've been playing WOT since the beginning of 2011. Do not binge, but rather raids - 5-6 battles in the evening. There was a time 2 years ago, our clan was in the Red Alliance, went to the globalku at night, performed some tactical tasks on a European theater , trained and sparred, in-game political passions boiled over, training clans budded. Now all this is no longer there, and our golden hedgehog has turned into a tablet above the “House of Veterans”.

I will not fall into the heresy of comparing tanks with other MMOs , since I am only familiar with tanks. Moreover, I am not familiar with any other project using BigWorld, therefore I sincerely believe WarGaming to the word that there are and (not) trivially (not) various techno-magical limitations of the engine — on the size of the map, on the maximum speed of the unit, on the number of teams And so on. Staying within the framework of internal criticism, I also understand that, from the point of view of the target audience of tanks in general, and their active gaming community in particular, each innovation from the next patch is certainly much more in demand and justified, no matter how small. And that to optimize Motion Blur by a few percent is certainly more important than canceling the fundamental invariability of the action binding on the mouse buttons for those who are used to put movement back and forth on them (DOOM-style, yes).
')
So, I am sure that in the foreseeable future there are no plans to introduce a full-fledged spectator mode in WOT, and never will. By a full-fledged spectator mode, I understand the multiple connections of players to a battle session initially as non-interacting “ghosts” —observers for the game, and not on the technique. This is the very regime that, due to the lack of which, commentators at the WOT championships are forced to enter battle with the 15th tank, which they kill at their base. This is the same mode that caused the “commanding zoom” and “cinematic camera” mods - in fact, just crutches. But such a regime is needed in order for the company commander to be in charge of the command, rather than galloping ahead on a dashing tank like Chapaevsky, so that he could see the situation on the map as a whole uninterruptedly, and not be distracted by the ambush attack. Ideally, the commander does not even need the beauty of a three-dimensional world — one large card is enough for the entire monitor with a real-time game situation — HP, damage to the modules, crew members, directions of barrels and sights of allied equipment illuminated at each moment of enemy units and other supporting information .

WOT provides ample opportunities for modding, but this idea goes beyond the classic "replace a couple of swf files with your own." It will require interception and analysis of the game protocol itself in order to be able to transfer its vision of the game situation to the commander’s tablet.

0x0A encryption bypass dear

I made the first tentative attempts to get into the tank protocol in 2011. Starting this, as elsewhere in such tasks, is a good sniffer (by the way, in the case of VATSIM / FSD, this can be stopped — it suddenly turned out that the protocol is text), and I, armed with Microsoft Network Monitor, rushed into the battle. For the reported 3 years, nothing fundamentally changed in the entry scheme, except for the number of game clusters. The authorization service of each cluster lives on the same IP, it receives one packet with a login password, and one packet comes from it - at least with the identifier of a specific game server from the same cluster to which the client must switch, and with whose IP there is a whole further exchange. It looks something like this

image

Marked octets, judging by the fact that they change little and more or less predictably - the packet header. After them there is a uniform bit noise, that is, there encryption is already included. It was possible to recall here that the records of the battles at WOT are encrypted by BlowFish, and that the encryption key has not changed since it became known to the general public, and delve deeper - but I don’t see any sense in this. It is much more important for the conceived that what goes between the client and the server during the game, than how it is authorized at the entrance.

So, if traffic passes streaming encryption, it is logical to assume that somewhere in the depths of the program there is a function like SendToServer (), in which there is a call like EncryptBuffer () and in which, ultimately, execution reaches a specific sendto (). Our task, to begin with, is to find where this is going. We load tanks into OllyDbg and before clicking on the “Enter” button we put the log breakpoint on sendto ().

image

After several dozen triggers, already in the hangar, sendto () calls become more monotonous, in the sense that the address of the buffer for sending data does not change:

  77062E14 Call to WS2_32.sendto from WorldOfTanks.00BA5B97
             000004D8 Arg1 = 4D8
             04436A75 Arg2 = 4436A75
             00000010 Arg3 = 10
             00000000 Arg4 = 0
             0018E2DC Arg5 = 18E2DC
             00000010 Arg6 = 10 

To find out where this buffer is encrypted, I first went the wrong way - I began to study call graphs in IDA and track manually returns from functions. Somewhere on the 15th of such a function I got tired of it, I got completely lost in the code and set a break to write to the buffer address.

image

The break worked inside such a wonderful function at 0x00BAF76F, the miracle of which is that 3 parameters are passed to it - two buffer addresses and one more - their same length. That same EncryptBuffer (ptr_src, ptr_dest, len) that we need. What is there in an unencrypted form, we will not look at it yet, more on that later.

image

This is about sending data. How to deal with the reception? It is a bit more complicated, but, in general, in the same way, so I will not bore you with large and terrible screenshots of the debugger. The scheme is this - we put a break on recvfrom (), we look at the address of the buffer where the received encrypted packet is put. We set a breakpoint to read from the buffer address, and here we are once again lucky - the breakdown is triggered by calling the function at 0x00BAFB79, which decrypts blocks of 8 bytes and is, in turn, in the function body at 0x00BAFB30. And this function is almost as wonderful as the one we found above: it takes 4 parameters - two buffer addresses, their length and some kind of flag.

image

Call it DecryptBuffer (ptr_src, ptr_dest, len, flag). The addresses of the buffers are apparently identical. That is completely logical, since streaming encryption should expect the same length of the blocks of the source and ciphertext.

There is only one subtlety. If it is enough to intercept the EncryptBuffer () function immediately before its call (i.e., set INT3-breakpoint instead of CALL) pulling the memory to ptr_src with len length, then at the time of DecryptBuffer () call both pointers will show the same block, which yet encrypted. Therefore, it is necessary to intercept this function just before the return, which is happening to it by the RETN 10 command at 0x00BAFBA8. At this moment, the same parameters are on the stack, except that ptr_src is zero (this is an innovation of version 0.8.11, in the previous pointers somehow remained equal) and the return address. And ptr_dest, of course, points to the decoded buffer. Now, when we know where the messages in the WOT client are not yet encrypted and where they have already been decrypted, we need to automatically pull them out for further analysis.

What is there inside?

Here, experienced reverse engineering gurus, in the spirit of "There's an emacs command to do that" think: "aha, well, now you can write in python / esoteric language this script / plugin for all \ Ida that will do everything with this data you want and even for a beer runs away. " But we will go the other way. I want to immediately focus on the fact that even before it comes to developing a commander tablet, that is, even for the most complete analysis of the WOT protocol, I will need helpers and testers who are far from the programming world. They will need a simple tool with a clear interface that provides easy-to-read data.

So, anticipating skeptical smirks, I sat down at Lazarus and sketched a specialized win32 debugger in it, the main function of which is to put two INT3-breakpoints in the right places and by pulling them pull the data by address and length of the buffer lying on the stack at known shifts. He also knows how to log the txt.gz log with hexdumps of the packets and write all the past packets so that they can be “lost” again through the parser. That's what happened. So WOT the client is just starting to enter the hangar.

image

But this is how he behaves already in the hangar.

image

What conclusions can be made, even despite the explanations to the packages, which I was too lazy to remove for these screenshots? Just a few.

First, it is clear that all packages start either from 0x48, or from 0x58, or from 0x78; I have not yet caught any patterns in this regard, except that the packets with the already known function do not change their significant byte.

Second, almost every packet has one or more message counters; for example, in the keep-alive and replies to them, the third and fourth byte in the header is engaged in this, besides the packet itself contains another message counter common (including keep-alive) and some specific one (counting packets without keeping-alive quantity); All this has to do with the control of packet delivery, which had to be screwed to UDP, probably for estimating losses and sending BLOBs (more on this later).

The sizes of all packets are aligned on the boundaries of 8 bytes, which unobtrusively indicates to us the block size of the same BlowFish; while I guessed this, a lot of time passed in an attempt to explain the strange “checksum” at the end, and even variable length. In the end, having received the word “Flags” instead of simple rubbish in this padding, I finally saw the light.

Well, in the best hacker traditions, at the end of each package is the signature of dead beef ; who would have thought where we find it :)

After receiving the Session ID from the server, a packet containing the player's number in an unexpectedly text format (792067) arrives. But the package that starts at 0x78 0x00 in the first screenshot is especially interesting. The combination of the signature 0x80 0x20, along with the fact that before each string literal in it is 0x55 and a byte of the string length, and after each 0x71 there is an increasing number, it should alert experienced pythoners - this is the hell of a Python Pickle with your own stuffing in a row into a memo ! Here it is:

Pickle
 Dct [15] :( xmpp_host = wot-ru.loc
 captchaKey = 6Lc8GcASAAAAAKffZdxeZZvOvmSTNXbZvsy6CgBR
 voipDomain = www.wotp.vivox.com
 file_server = Dct [6] :( clan_emblems_small = Dct [1] :( url_template = http://ce.worldoftanks.ru/dcont/clans/emblems/%d/emblem_32x32.png)
 clan_emblems_big = Dct [1] :( url_template = http://ce.worldoftanks.ru/dcont/clans/emblems/%d/emblem_64x64.png)
 rare_achievements_images_big = Dct [1] :( url_template = http://ce.worldoftanks.ru/dcont/achievements/medals/180x180/%d.png)
 clan_emblems = Dct [1] :( url_template = http://ce.worldoftanks.ru/dcont/clans/emblems/%d/emblem_64x64_tank.png)
 rare_achievements_images = Dct [1] :( url_template = http://ce.worldoftanks.ru/dcont/achievements/medals/67x71/%d.png)
 rare_achievements_texts = Dct [1] :( url_template = http://ce.worldoftanks.ru/dcont/achievements/medals/medals_%s.xml))
 newbieBattlesCount = 100
 roaming = Lst [4] :( 1.1, Lst [3] :( Lst [4] :( 1,1,499999999, RU), Lst [4] :( 2,500000000,999999999, EU), Lst [ 4]: (3,1000000000,1499999999, NA)), Lst [0]: ())
 xmpp_enabled = true
 jdCutouts = 0
 xmpp_port = 5222
 isTutorialEnabled = True
 wallet = Lst [2] :( True, True)
 xmpp_connections = Lst [1] :( Lst [2] :( xmppcs.worldoftanks.net, 5222))
 xmpp_resource = wot
 regional_settings = Dct [2] :( starting_day_of_a_new_week = 0
 starting_time_of_a_new_day = 0)
 reCaptchaParser =)



In the next part, if it is of interest to the respected inhabitants of Habr, I will talk about how the WOT protocol transfers files that are much larger than the realistic size of the UDP packet and the MTU. And that these files are compressed zlib'om and inside they have all the same Python Pickle with various unexpected things.

Thanks for attention!

UPD. Latest news! According to intelligence reports from the very heart of KVG, my little hint at the potential for commercial use of the commander's tablet raised a “little bugur” (literally), as a result of which, in complete panic, the bloody moderators finally noticed and drafted the topic of the project on the official tank forum, and I was in a confidential whisper, it was advised to blur the data of your account in the screenshots.

I, in a sane and sober memory, I know I solemnly swear that this development is purely academic in nature, I never swear to never buy WarGaming.net for countless billions from the sale of even a non-existing development or otherwise attempt at financial well-being of your favorite company! Amen.

UPD2.
"Video presentation" of the now-killed topic on the forum of tanks

Source: https://habr.com/ru/post/212351/


All Articles