EDKObfuscation
Where to look at
in the eMule 0.48a sources
eMule 0.48a seems to have implemented the bigger part of the logic in EncryptedStreamSocket.h and EncryptedStreamSocket.cpp. The encryption is AFAIK implemented as a frontend to CAsyncSocketEx, supposedly because the latter is used for communication and extending it would make it compatible with existing code. An implementation in mldonkey could use the same techique. It could wrap the encryption code just around the existing "connections" infrastructure. It also makes understanding the algorithm comparably easy, since all we need to look at sits in EncryptedStreamSocket.cpp (well and EncryptedDatagramSocket respectively).
The file EncryptedStreamSocket.cpp also contains a brief introduction to protocol applied. The big encryption and decryption parts sit in the Receive() and the Send() methods. That's also where the larger part of the state machine inside those classes is handled. The encryption negotiation sits in the Negotiate() method. For the encryption itself RC4 seems to be used. The initial author of this section will provide a few UML graphs of his findings soon as well (in other words: stay tuned).
The eMule encryption state machine for stream sockets
Overview
The encryption state machine is implemented as an extension to sockets eMule uses for up- and downloading as class CEncryptedStreamSocket in EncryptedStreamSocket.cpp. There are three possible connection types:
- An outgoing connection to an eDonkey server
- An outgoing connection to an eDonkey client
- An incoming connection from an eDonkey client
As a result we find three possible flows through that state machine:
The protocol state machine for server sockets
For server connections the initial keys are exchange via Diffie-Hellman key exchange. That type of key exchange ensures that only the exchanging peers will be able to read the traffic between each other and is widely used.
Transition 15: Diffie-Hellman exchange 1st step
This is the first step in establishing a encrypted connection to an eDonkey server. In this transition our local key is calculated and send to the server using a DH exchange. Prior to sending any data the following numbers are calculated:
- a is a random number
- p is a prime number
- e = a_exp_b_mod_c( 2, a, p )
In the Wikipedia page example g = 2
Length | Content |
---|---|
1 Byte | SemiRandomNotProtocolMarker (see below) |
96 Bytes | e |
1 Byte | Padding length |
n Byte | Padding |
Transition 16: Diffie-Hellman exchange 2nd step
In the 2nd step, we receive the server exchange and calculate all the keys (see below).
Length | Content |
---|---|
96 Bytes | d |
Now with the above received data the keys are calculated as follows:
- a buffer is created that is one by longer than the key
- the key is copied to the buffer and the MAGICVALUE_REQUESTER is appended
- RC4SendKey = RC4CreateKey(md5sum(buffer))
- the MAGICVALUE_REQUESTER in the buffer is replaced by MAGICVALUE_SERVER
- RC4ReceiveKey = RC4CreateKey(md5sum(buffer))
Transition 17: Verification of the key exchange succeeded
From the 3rds step on, all the communication is sent in a RC4 encrypted stream. In the 3rd step, we verify that the server has the correct idea about what our keys are. It will have got a 4 byte value from the server that should equal to MAGICVALUE_SYNC.
Length | Content |
---|---|
4 Byte | dwValue |
if dwValue will be decrypted and compared with MAGICVALUE_SYNC. If they're equal the key is known to the server, as it could encode it correctly.
Transition 18: Check for the encryption method succeeded
In the 4th step we check that the encryption method presented by the server is supported here. eMule 0.48a only supports ENM_OBFUSCATION.
Length | Content |
---|---|
1 Byte | Encryption supported |
1 Byte | Encryption requested |
1 Byte | Padding length |
Transition 19: The padding has been read from the socket
The 5th step of server encryption establishment is about reading the padding data from the socket and sending the server a confirmation that we have the right idea about its key. For that reason we send MAGICVALUE_SYNC. Further we select ENM_OBFUSCATION as the encryption method and send some garbage as padding.
Length | Content |
---|---|
n Byte | Padding |
Length | Content |
---|---|
4 Byte | MAGICVALUE_SYNC |
1 Byte | ENM_OBFUSCATION ( the selected encryption method ) |
1 Byte | padding length |
n Byte | padding |
Transition 20: Logging into the server
Here we send our normal eDonkey login over the encrypted connection and if we did everything else right we'll receive a proper response from it.
SemiRandomNotProtocolMarker
The SemiRandomNotProtocolMarker used above is to make sure that the protocol is not misunderstood as an unencrypted protocol. For that reason the first byte of the stream cannot be OP_EDONKEYPROT, OP_PACKEDPROT or OP_EMULEPROT. eMule will try up to 128 times to find a random number that is not equal to these three. So each number unequal to these three values is considered a protocol marker for an encrypted stream.
The protocol state machine for outgoing client sockets
Inter-client key generation
Unlike in the server handshake, where the intial key is exchange with Diffie-Hellman, the keys for the clients are generated based on their client hash.
Transition 11: Initiating an outgoing connection
Transition 11 happens when after connecting the socket, the encrypted handshake is initiated.
The inter-client key generation algorithm is performed:
- Constructrt the following buffer:
Length | Content |
---|---|
16 Bytes | Client MD4 hash |
1 Byte | Either MAGICVALUE_REQUESTER |
4 Byte | Random key part |
- Calculate the MD5 hash of the buffer
- RC4SendKey = RC4CreateKey(md5 checksum)
- Replace MAGICVALUE_REQUESTER with MAGICVALUE_SERVER in the buffer
- Calculate the MD5 hash of the buffer
- RC4ReceiveKey = RC4CreateKey(md5 checksum)
Length | Content |
---|---|
1 Byte | SemiRandomNotProtocolMarker |
4 Byte | random part of the initiator |
4 Byte | MAGICVALUE_SYNC |
1 Byte | ENM_OBFUSCATION (our supported encryption protocol) |
1 Byte | ENM_OBFUSCATION (our preferred encryption protocol) |
1 Byte | padding length |
n Byte | padding |
Since the initiator already knows the client MD4 hash of the outgoing connection, it can encrypt the traffic without any previous handshake. The encryption starts after the random part of the initiator and thus with byte 5.
Transition 12: Verification of the key exchange succeeded
The received dwValue has been compared with MAGICVALUE_SYNC and was identical.
Length | Content |
---|---|
4 Byte | dwValue |
Transition 13: The selected encryption method has been verified to be supported.
Length | Content |
---|---|
1 Byte | EncryptionMethodSet |
1 Byte | Padding length |
Transition 14: The padding has been received
Length | Content |
---|---|
n Byte | Padding |
The protocol state machine for incoming client connections
Transition 6: The random part has been received
Length | Content |
---|---|
1 Byte | SemiRandomNotProtocolMarker |
4 Byte | random part of the initiator |
This is the companion of transition 11.
Transition 7: Our random-part has been sent to the initiator
Prior to sending the random-part the inter-client key generation takes place:
- Create the buffer:
Length | Content |
---|---|
16 Byte | My MD4 client hash |
1 Byte | MAGICVALUE_REQUESTER |
4 Byte | received random part from initiator |
- ReceiveKey = RC4CreateKey(md5sum(buffer))
- replace MAGICVALUE_REQUESTER with MAGICVALUE_SERVER
- SendKey = RC4CreateKey(md5sum(buffer))
Length | Content |
---|---|
4 Bytes | Random part |
Transition 8: It has been verified that the key exchange succeeded
Length | Content |
---|---|
4 Bytes | dwValue |
dwValue has been equal to MAGICVALUE_SYNC and thus had been encrypted correctly by the peer.
Transition 9: List of supported protocols received and supported
Length | Content |
---|---|
1 Byte | EncryptionSupported |
1 Byte | EncryptionRequested |
1 Byte | Padding length |
Transition 10: Encryption succeeded from our perspective.
Length | Content |
---|---|
n Bytes | Padding |
Length | Content |
---|---|
4 Byte | MAGICVALUE_SYNC |
1 Byte | the selected encryption method: ENM_OBFUSCATION |
1 Byte | padding length |
n Byte | padding |
Links
Protocol obfuscation
- eMule on obfuscation: http://www.emule-project.net/home/perl/help.cgi?l=1&topic_id=848&rm=show_topic
- Protocol obfuscation in the eMule wiki: http://wiki.emule-web.de/index.php/Protocol_obfuscation#Technical_implementation
Ocaml and RC4
- OCaml-SSL: http://caml.inria.fr/cgi-bin/hump.en.cgi?contrib=368
- RC4 in OcaML alone: http://abaababa.ouvaton.org/caml/rc4.ml
- ocaml code for a general stream cipher setup: http://www.matt-mcdonnell.com/blog/perm_2006_12_03_122324.html