Parsing WeChat - the second most popular messenger in the world

- A short excursion into WeChat;
- About the platform, application version, utilities used and decryption of the executable file;
- About two protocols (old and new);
- About serialization of objects;
- Used cryptography and key exchange;
- About headers and hash functions;
- About found vulnerabilities.
WeChat messenger from the Chinese company Tencent
WeChat is the second most popular instant messenger in the world. It is very difficult to find official data on the number of users, but a rough estimate can be made.
We are talking about 800 million users worldwide, 90% of which are in China.
In China, almost every smartphone owner uses WeChat (Chinese self-name -Weixin), since this is not only a messenger in the classical sense, but a whole system, including a mobile wallet, a built-in browser, an online store, etc. All government agencies of China are represented in it. You can pay for a receipt or make an appointment with a doctor through a messenger.
An urgent task was the integration of CRM systems of customers actively working in China with WeChat. This was facilitated by the widespread use of WeChat in China, as well as the lack of an official API. SMS-informing in China is expensive and, most importantly, it is unstable, in addition, there is no "read" status. The customer, using the API, will be able through his CRM system to notify WeChat users (subscribed to receive information from the customer’s number, we will discuss this below) about the delivery of goods, new orders and other service information.
Protocol research
It was decided to study the messenger "from the inside", to understand the code of the 32-bit version of the messenger for iOS. We have an old, well-worn iPhone 4S with iOS 7.2.1.
As MITM we use Burp Suite Free Edition .
We download the application and use the wonderful dumpdecrypted utility to decrypt the executable file.
At the time the WeChat reverse engineering started, version 6.3.13 was relevant.
Now it remains to copy the file from the device, disassemble it in the IDA, and you can start.
Consider the key exchange algorithm using registration as an example.
We launch the application and see a proposal to enter a phone number for registration.
Enter the phone number and see in MITM an HTTP request for the addresshkshort.weixin.qq.com/bindopmobileforreg .

The request body consists of:
- Header (yellow color);
- Encrypted data (blue color).
After a very long static analysis of the code, it was possible to find out that the client is communicating with the server through serialized objects. Objects are serialized using the Protocol Buffers library .
The first message transfers the following data:
- Phone number ;
- Phone system language
- Device ID
- Client version;
- The key to decrypt the response (random 16 bytes);
- Other data (not very interesting to us).
The message is serialized and encrypted with the public key of the server using the RSA algorithm. The server response is decrypted with the AES key passed in the request. The response states that either everything is in order or an error is indicated.
We get SMS and enter the code. The same request is formed, only with the code from SMS, and in the response we get the so-called ticket. Now, having a ticket, you can send a registration request. Enter the name and click OK.
Key exchange takes place according to the Diffie-Hellman algorithm using an elliptic curve over the final field "secp224r1". The private and public keys are generated, and a request is sent to the address hkshort.weixin.qq.com/newreg. The server generates its keys, and also gives us the so-called CryptUin and ServerID, which we will discuss later. In response, the server sends us its public key and session key.
Now we have the public key of the server, and we calculate the shared key with which we decrypt the session key. From this moment, client-server communication is carried out using the symmetric AES algorithm with a key length of 128 bits.
In general, there is nothing supernatural in the connection setup and key exchange algorithm. Correctly encrypting data and exchanging keys is half the task; you must also correctly compose a header for each message. Even if the data is serialized and encrypted correctly, if the header is incorrect, the server will send an error response. The title looks like this:
bfa65f16050520252cb6b770021001754cc8fd5e57e085457800fb0242420001aeb890f40b010a0080
Now we will tell you more about each field:
1. Protocol ID. Each packet begins with this byte.
2. Flags. It stores information about the length of the SrvID, the length of the header itself, and the compression of the original message.

The compression flag is set to 0b10 if the message was not compressed, otherwise it is set to 0b01.
3. The version of the application. No comments.
4. CryptUin. After registration, each account is assigned a unique identifier of four bytes.
5. SrvID. ID of the current session. Changes with every new connection.
6. uiCgi. Team code. Each team has its own uiCgi and url. For example, for the bindopmobileforreg command, uiCgi is 0x91, and for newreg is 0x7e. Most numbers are packed using the following algorithm:
private static void Write7BitEncodedInt(BinaryWriter store, int value)
{
Debug.Assert(store != null);
// Write out an int 7 bits at a time. The high bit of the byte,
// when on, tells reader to continue reading more bytes.
uint v = (uint)value; // support negative numbers
while (v >= 0x80)
{
store.Write((byte)(v | 0x80));
v >>= 7;
}
store.Write((byte)v);
}In this example, uiCgi is 0x17b, and in packaged form is fb02.
7. The length of the original message. The length of the serialized data. The number is also packaged, but since it is less than 0x80, it remains unchanged.
8. The length of the compressed message. Compression was not performed, therefore, does not differ from the previous one.
9. The flag.
10. Hash.
It is calculated as follows: –this is the common key obtained during the handshake. The hash is also packaged. 11. Flags. The meaning of these flags remained a mystery, but they are static, so there was no point in studying them separately.
hash1 = md5(cryptUin.shareKey);
hash2 = md5( strlen(data).shareKey.hash1.data)
resultHash = adler32(hash2)sharedKeyNow another protocol is already being used, which we will write about in subsequent publications. In fact, it is a wrapper over the above. The old protocol is still supported - to do this, you must clear the MmtlsCtrlFlag flag in the debugger.
Spam protection.
To protect against spam, the user can enable the option "confirmation of friendship." In this case, you can write a message to him only after he confirms that you are friends. A friend confirmation request may include a welcome message.
Sending a lot of welcome messages will fail. After sending fifteen requests, all the others stop sending and queue up. A sixteenth request will only be sent when someone from the previous fifteen adds you as a friend. But the user does not know this, and the application interface does not report it either. It was possible to find out with the help of traffic analysis and experiments.
In the process, an interesting vulnerability was also discovered. The application has the ability to find the user by phone number. The server either answers that there is no such user, or returns information about him (name, gender, city, photo, etc.). But if you send these requests too often, the server responds with the message “Too many attempts. Try again later. " This can be used, because if the user does not exist, the server will always report this, and if the response is “Too many attempts. Try again later „- this means that the user exists. Using this, you can build a user base. By the way, there were very “interesting” users who use WeChat, but they can’t be found in the usual way, and it’s even impossible to send them an invitation, most likely these are “special” people of China.
Using 20,000 streams, it is possible to collect the entire WeChat database from China from one account per day, the account is not blocked.
I would also like to inform you separately that End-to-End encryption between users does not occur. Messages are encrypted only with a symmetric key, the server decrypts them and re-encrypts with the recipient's symmetric key and sends them to the recipient.
This article is introductory, if there is interest from the Habr community, the following publications on WeChat may appear, since any event in WeChat (for example, serialization of objects) is worthy of separate articles.