[C] WoW packet decryption menu

User Tag List

Results 1 to 13 of 13
  1. #1
    pendra's Avatar Active Member
    Reputation
    46
    Join Date
    Jul 2008
    Posts
    42
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [C] WoW packet decryption

    Here is my implementation of WoW packet decryption written in C. The ring buffer implementation may require some minor tweaks to work on windows. If anyone wants to modify it and post the changes I'll incorporate them and edit my post.

    Everything should work as is on linux or OSX.

    Credits:

    • I learned how the encryption works from the mangos server emu source
    • I'm using the OpenSSL implementations of HMAC and RC4.
    • The ring buffer implementation is from the wikipedia page on circular buffers at Circular buffer - Wikipedia, the free encyclopedia
    • Includes a fix from Nonal for large packets.


    The whole decryption implementation is only 142 lines, so I thought it might be useful to the community as a clean, easy to read reference implementation. It's also quite fast, not that it really matters.

    How to use it:

    Before doing anything else, instantiate a wowcrypt:

    Code:
    struct wowcrypt s;
    int order = 22;
    wowcrypt_init(&s, order);
    This single instance will handle data going both from the wow server to the client and vice versa. The ring buffers for storing the inbound/outbound packet data will be 2^order bytes long, so in my case 2^22 = 4MB. The ring buffers must be at least as large as the largest wow message.

    When you want to clean up, delete the wowcrypt:

    Code:
    wowcrypt_destroy(&s);
    You then need some way of getting the raw WoW network traffic into the buffers. You could do this by hooking the winsock send/recv, or by running an out of process packet sniffer, etc. At the end of the day, you should have some bytes, you insert them into the wowcrypt like this, given 'len' bytes in 'buf':

    Code:
    /* data going from wow server to wow client */
    if (wowcrypt_free_bytes(&s,WOWCRYPT_INBOUND) < len) { not enough room! }
    wowcrypt_insert(&s,WOWCRYPT_INBOUND,len,buf);
    Swap WOWCRYPT_INBOUND for WOWCRYPT_OUTBOUND for data going the other way. wowcrypt_insert assumes there is enough room to insert the data, so you must check this yourself with wowcrypt_free_bytes and do something if there is not enough space. This should not happen if you always pop messages after inserting data and your buffer is large enough, depends on how your bot is organized.

    WoW has two static HMAC keys which I've hard coded into the source. I've never seen these change. There is also a 40 byte session key that must be read from memory once WoW has instantiated the client connection.

    As of 3.3.2 [11403], this key can be found starting at [[0x00C93410]+0x508].

    The messages sent between the WoW client and the WoW server are unencrypted until the authentication handshake has been performed, at which point the headers become encrypted. In my experience on private servers, the handshake messages are always the first message going in each direction, but that may not always be true.

    The code to setup decryption will look something like like this, note I'm assuming in this that all the unencrypted messages have already been received for simplicity:

    Code:
       /* pop the unencrypted messages before enabling encryption */
    
       do {
           wowcrypt_get_msg(&s,WOWCRYPT_INBOUND,&opcode,&len,&data);
           /* do something with the message before popping it */
           wowcrypt_pop_msg(&s,WOWCRYPT_INBOUND);
       } while (not the inbound auth msg);
    
       do { 
           wowcrypt_get_msg(&s,WOWCRYPT_OUTBOUND,&opcode,&len,&data);
           /* do something with the message before popping it */
           wowcrypt_pop_msg(&s,WOWCRYPT_OUTBOUND);
       } while (not the outbound auth msg);
    
       /* set the session key and enable decryption from here out */
       wowcrypt_enable(&s,session_key,40);
    From this point forward, messages will be decrypted on the fly, so further messages can be obtained as follows:

    Code:
        int opcode,length;
        unsigned char *data;
    
        if (wowcrypt_get_msg(&s,WOWCRYPT_INBOUND,&opcode,&len,&data)) {
              /* do something with the message before popping it */
              wowcrypt_pop_msg(&s,WOWCRYPT_INBOUND);
        }
       
        if (wowcrypt_get_msg(&s,WOWCRYPT_OUTBOUND,&opcode,&len,&data)) {
              /* do something with the message before popping it */
              wowcrypt_pop_msg(&s,WOWCRYPT_OUTBOUND);
        }
    wowcrypt_get_msg either returns zero if no message is ready, or returns 1 and sets opcode, len, and data to the opcode, length, and data pointer of the next decrypted message.

    The 'len' will be the amount of data after the opcode. You can learn about what the different message types are and what they do by reading the mangos source code.

    Note that the copy of the message you get out of wowcrypt_get_msg is still owned by wowcrypt and will be invalidated when you call wowcrypt_pop_msg to advance to the next message, so treat it as a read only and take your own copy if you need to keep it.

    Here's the wowcrypt source:

    Code:
    /*
     * WoW packet decryption 
     * pendra@mmowned
     *
     */
    
    #include <openssl/hmac.h>
    #include <openssl/sha.h>
    #include <openssl/evp.h>
    
    #include "ringbuffer.h"
    
    #define WOWCRYPT_SEED_KEY_SIZE 16
    #define WOWCRYPT_DISCARD_LEN   1024
    
    #define WOWCRYPT_INBOUND      0
    #define WOWCRYPT_OUTBOUND     1 
    
    static const int wowcrypt_oplen[2] = { 2, 4 };
    
    static const char wowcrypt_keys[2][WOWCRYPT_SEED_KEY_SIZE] =
    {
       { 0x22, 0xBE, 0xE5, 0xCF, 0xBB, 0x07, 0x64, 0xD9, 0x00, 0x45, 0x1B, 0xD0, 0x24, 0xB8, 0xD5, 0x45 },
       { 0xF4, 0x66, 0x31, 0x59, 0xFC, 0x83, 0x6E, 0x31, 0x31, 0x02, 0x51, 0xD5, 0x44, 0x31, 0x67, 0x98 },
    };
    
    struct wowcrypt
    {
       EVP_CIPHER_CTX       sarc4[2];
       HMAC_CTX             hmac[2];
       unsigned char        digest[2][SHA_DIGEST_LENGTH];
       struct ring_buffer   rb[2];
       int                  hdr[2];
       int                  enabled;
    };
    
    void wowcrypt_init(struct wowcrypt *s,int rb_order)
    {
       int i;
    
       for (i = 0; i < 2 ; i++) {
          HMAC_CTX_init(&s->hmac[i]);
          HMAC_Init_ex(&s->hmac[i], wowcrypt_keys[i], WOWCRYPT_SEED_KEY_SIZE, EVP_sha1(), NULL);
          EVP_CIPHER_CTX_init(&s->sarc4[i]);
          ring_buffer_create(&s->rb[i],rb_order);
          s->hdr[i] = 0;
       }
    
       s->enabled = 0;
    
    }
    
    void wowcrypt_destroy(struct wowcrypt *s)
    {
       int i;
    
       for (i = 0; i < 2 ; i++) {
          EVP_CIPHER_CTX_cleanup(&s->sarc4[i]);
          HMAC_CTX_cleanup(&s->hmac[i]);
          ring_buffer_free(&s->rb[i]);
       }
    
    }
    
    void wowcrypt_enable(struct wowcrypt *s,unsigned char *seed, int length)
    {
       unsigned char  buf[WOWCRYPT_DISCARD_LEN];
       unsigned int   hlen;
       int            len;
       int            i;
    
       for (i = 0; i < 2 ; i++){
          HMAC_Update(&s->hmac[i], seed, length);
          HMAC_Final(&s->hmac[i], s->digest[i], &hlen);
    
          EVP_EncryptInit_ex(&s->sarc4[i], EVP_rc4(), NULL, NULL, NULL);
          EVP_CIPHER_CTX_set_key_length(&s->sarc4[i], SHA_DIGEST_LENGTH);
          EVP_EncryptInit_ex(&s->sarc4[i], NULL, NULL, s->digest[i], NULL);
    
          memset(buf,0,WOWCRYPT_DISCARD_LEN);
    
          EVP_EncryptUpdate(&s->sarc4[i],buf,&len,buf,WOWCRYPT_DISCARD_LEN);
          EVP_EncryptFinal_ex(&s->sarc4[i],buf,&len);
       }
    
       s->enabled = 1;
    
    }
    
    int wowcrypt_free_bytes(struct wowcrypt *s,int dir) {
       return ring_buffer_count_free_bytes(&s->rb[dir & 0x1]);
    }
    
    void wowcrypt_insert(struct wowcrypt *s, int dir, int len, unsigned char *data)
    {
       struct ring_buffer *rb = &s->rb[dir & 0x1];
    
       memcpy(ring_buffer_write_address(rb),data,len);
    
       ring_buffer_write_advance(rb,len);
    }
    
    int wowcrypt_get_msg(struct wowcrypt *s, int dir, int *opcode,int *length, unsigned char **data)
    {
       const int               d = dir & 0x01;
       struct ring_buffer    *rb = &s->rb[d];
       int                  *hdr = &s->hdr[d];
       const int           oplen = wowcrypt_oplen[d];
       const int              bl = ring_buffer_count_bytes(rb);
       unsigned char         *ba = ring_buffer_read_address(rb);
       int sizelen = 2;
       int l;
    
       if (*hdr==0) {
          if (bl >= sizelen + oplen) {
             if (s->enabled) {
                EVP_EncryptUpdate(&s->sarc4[d],ba,&l,ba,sizelen + oplen);
                EVP_EncryptFinal_ex(&s->sarc4[d],ba,&l);
             }
             *hdr = 1;
          } else {
             return 0;
          }	
       }
    
       if (*ba >= 0x80) {
    	  sizelen = 3;
       }
    
       if (*hdr == 1 && sizelen == 3) {
         if (bl >= sizelen + oplen) {
    	     if (s->enabled) {
    	        EVP_EncryptUpdate(&s->sarc4[d],ba+oplen+2,&l,ba+oplen+2,1);
    	        EVP_EncryptFinal_ex(&s->sarc4[d],ba+oplen+2,&l);
    	     }
    	     *hdr = 2;
    	  } else {
    	     return 0;
    	  }	
       } else {
    	 *hdr = 2;
       }
    
       l = ntohs(*(unsigned short*)(ba+sizelen-2));
       if (sizelen==3) {
    	   unsigned int added = (*ba) & 0x7F;
    	   l += added<<16;
       }
    
       if (bl >= sizelen + l) {
          *length = l - oplen;
          *opcode = (oplen == 2) ? *(unsigned short*)(ba+sizelen) : *(unsigned int*)(ba+sizelen);
          *data   = ba + sizelen + oplen;
          return 1;
       } else {
          return 0;
       }
    }
    
    void wowcrypt_pop_msg(struct wowcrypt *s, int dir)
    {
       const int               d = dir & 0x01;
       struct ring_buffer    *rb = &s->rb[d];
       int                  *hdr = &s->hdr[d];
       const unsigned char   *ba = ring_buffer_read_address(rb);
    
       if (*ba < 0x80) {
    	   ring_buffer_read_advance(rb,2 + ntohs(*(unsigned short*)(ba)));
       } else {
    	   ring_buffer_read_advance(rb,3 + ntohs(*(unsigned short*)(ba+1)) + ((*ba & 0x7f)<<16) );
       }
    
       *hdr = 0;
    }
    Here's the ring buffer implementation it uses:

    Code:
    /*
     * This was taken verbatim from http://en.wikipedia.org/wiki/Circular_buffer 
     */
    
    #ifndef __RINGBUFFER_H__
    #define __RINGBUFFER_H__
    
    #include <sys/mman.h>
    #include <stdlib.h>
    #include <unistd.h>
     
    #define report_exceptional_condition() abort ()
     
    struct ring_buffer {
       char *address;
     
       unsigned long count_bytes;
       unsigned long write_offset_bytes;
       unsigned long read_offset_bytes;
    };
     
    void ring_buffer_create(struct ring_buffer *buffer, unsigned long order) {
       
       char path[] = "/dev/shm/ring-buffer-XXXXXX";
       int file_descriptor;
       void *address;
       int status;
     
       file_descriptor = mkstemp (path);
    
       if (file_descriptor < 0) report_exceptional_condition ();
     
       status = unlink (path);
    
       if (status) report_exceptional_condition ();
     
       buffer->count_bytes = 1UL << order;
       buffer->write_offset_bytes = 0;
       buffer->read_offset_bytes = 0;
     
       status = ftruncate (file_descriptor, buffer->count_bytes);
    
       if (status) report_exceptional_condition ();
     
       buffer->address = mmap(NULL, buffer->count_bytes << 1, PROT_NONE,
                              MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
     
    
       if (buffer->address == MAP_FAILED) report_exceptional_condition ();
     
       address = mmap(buffer->address, buffer->count_bytes, PROT_READ | PROT_WRITE,
                      MAP_FIXED | MAP_SHARED, file_descriptor, 0);
     
       if (address != buffer->address) report_exceptional_condition ();
     
       address = mmap(buffer->address + buffer->count_bytes,
                      buffer->count_bytes, PROT_READ | PROT_WRITE,
                      MAP_FIXED | MAP_SHARED, file_descriptor, 0);
     
       if (address != buffer->address + buffer->count_bytes) report_exceptional_condition ();
     
       status = close(file_descriptor);
      
       if (status) report_exceptional_condition ();
    }
     
    void ring_buffer_free (struct ring_buffer *buffer) {
    
      int status;
     
      status = munmap (buffer->address, buffer->count_bytes << 1);
    
      if (status) report_exceptional_condition ();
    
    }
     
    void *ring_buffer_write_address (struct ring_buffer *buffer) {
       return buffer->address + buffer->write_offset_bytes;
    }
    
    oid ring_buffer_write_advance (struct ring_buffer *buffer,
                                    unsigned long count_bytes) {
    
      buffer->write_offset_bytes += count_bytes;
    
    }
    
    void *ring_buffer_read_address (struct ring_buffer *buffer) {
      return buffer->address + buffer->read_offset_bytes;
    }
    
    void ring_buffer_read_advance (struct ring_buffer *buffer,
                                   unsigned long count_bytes) {
    
       buffer->read_offset_bytes += count_bytes;
    
       if (buffer->read_offset_bytes >= buffer->count_bytes) {
          buffer->read_offset_bytes -= buffer->count_bytes;
          buffer->write_offset_bytes -= buffer->count_bytes;
       }
    }
    
    unsigned long ring_buffer_count_bytes (struct ring_buffer *buffer) {
      return buffer->write_offset_bytes - buffer->read_offset_bytes;
    }
    
    unsigned long ring_buffer_count_free_bytes (struct ring_buffer *buffer) {
      return buffer->count_bytes - ring_buffer_count_bytes (buffer);
    }
    
    void ring_buffer_clear (struct ring_buffer *buffer) {
      buffer->write_offset_bytes = 0;
      buffer->read_offset_bytes = 0;
    }
    
    #endif
    Enjoy...
    Last edited by pendra; 02-28-2010 at 01:39 PM.

    [C] WoW packet decryption
  2. #2
    leather's Avatar Member
    Reputation
    1
    Join Date
    Jun 2007
    Posts
    19
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    good work!
    +Rep

  3. #3
    AlexF's Avatar Banned
    Reputation
    1
    Join Date
    Feb 2010
    Posts
    10
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    i Run source in VC++ 2008 but
    ringbuffer.h( : fatal error C1083: Cannot open include file: 'sys/mman.h': No such file or directory


    ringbuffer.h(10) : fatal error C1083: Cannot open include file: 'unistd.h': No such file or directory
    i want download sys/mman.h and unistd.h but dont known where it

    "ringbuffer.h"
    Code:
    /*
     * This was taken verbatim from http://en.wikipedia.org/wiki/Circular_buffer 
     */
    
    #ifndef __RINGBUFFER_H__
    #define __RINGBUFFER_H__
    
    #include <sys/mman.h>
    #include <stdlib.h>
    #include <unistd.h>
     
    #define report_exceptional_condition() abort ()
     
    struct ring_buffer {
       char *address;
     
       unsigned long count_bytes;
       unsigned long write_offset_bytes;
       unsigned long read_offset_bytes;
    };
     
    void ring_buffer_create(struct ring_buffer *buffer, unsigned long order) {
       
       char path[] = "/dev/shm/ring-buffer-XXXXXX";
       int file_descriptor;
       void *address;
       int status;
     
       file_descriptor = mkstemp (path);
    
       if (file_descriptor < 0) report_exceptional_condition ();
     
       status = unlink (path);
    
       if (status) report_exceptional_condition ();
     
       buffer->count_bytes = 1UL << order;
       buffer->write_offset_bytes = 0;
       buffer->read_offset_bytes = 0;
     
       status = ftruncate (file_descriptor, buffer->count_bytes);
    
       if (status) report_exceptional_condition ();
     
       buffer->address = mmap(NULL, buffer->count_bytes << 1, PROT_NONE,
                              MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
     
    
       if (buffer->address == MAP_FAILED) report_exceptional_condition ();
     
       address = mmap(buffer->address, buffer->count_bytes, PROT_READ | PROT_WRITE,
                      MAP_FIXED | MAP_SHARED, file_descriptor, 0);
     
       if (address != buffer->address) report_exceptional_condition ();
     
       address = mmap(buffer->address + buffer->count_bytes,
                      buffer->count_bytes, PROT_READ | PROT_WRITE,
                      MAP_FIXED | MAP_SHARED, file_descriptor, 0);
     
       if (address != buffer->address + buffer->count_bytes) report_exceptional_condition ();
     
       status = close(file_descriptor);
      
       if (status) report_exceptional_condition ();
    }
     
    void ring_buffer_free (struct ring_buffer *buffer) {
    
      int status;
     
      status = munmap (buffer->address, buffer->count_bytes << 1);
    
      if (status) report_exceptional_condition ();
    
    }
     
    void *ring_buffer_write_address (struct ring_buffer *buffer) {
       return buffer->address + buffer->write_offset_bytes;
    }
    
    oid ring_buffer_write_advance (struct ring_buffer *buffer,
                                    unsigned long count_bytes) {
    
      buffer->write_offset_bytes += count_bytes;
    
    }
    
    void *ring_buffer_read_address (struct ring_buffer *buffer) {
      return buffer->address + buffer->read_offset_bytes;
    }
    
    void ring_buffer_read_advance (struct ring_buffer *buffer,
                                   unsigned long count_bytes) {
    
       buffer->read_offset_bytes += count_bytes;
    
       if (buffer->read_offset_bytes >= buffer->count_bytes) {
          buffer->read_offset_bytes -= buffer->count_bytes;
          buffer->write_offset_bytes -= buffer->count_bytes;
       }
    }
    
    unsigned long ring_buffer_count_bytes (struct ring_buffer *buffer) {
      return buffer->write_offset_bytes - buffer->read_offset_bytes;
    }
    
    unsigned long ring_buffer_count_free_bytes (struct ring_buffer *buffer) {
      return buffer->count_bytes - ring_buffer_count_bytes (buffer);
    }
    
    void ring_buffer_clear (struct ring_buffer *buffer) {
      buffer->write_offset_bytes = 0;
      buffer->read_offset_bytes = 0;
    }
    
    #endif







    Code:
    /*
     * WoW packet decryption 
     * pendra@mmowned
     *
     */
    
    #include <openssl/hmac.h>
    #include <openssl/sha.h>
    #include <openssl/evp.h>
    
    #include "ringbuffer.h"
    
    #define WOWCRYPT_SEED_KEY_SIZE 16
    #define WOWCRYPT_DISCARD_LEN   1024
    
    #define WOWCRYPT_INBOUND      0
    #define WOWCRYPT_OUTBOUND     1 
    
    static const int wowcrypt_oplen[2] = { 2, 4 };
    
    static const char wowcrypt_keys[2][WOWCRYPT_SEED_KEY_SIZE] =
    {
       { 0x22, 0xBE, 0xE5, 0xCF, 0xBB, 0x07, 0x64, 0xD9, 0x00, 0x45, 0x1B, 0xD0, 0x24, 0xB8, 0xD5, 0x45 },
       { 0xF4, 0x66, 0x31, 0x59, 0xFC, 0x83, 0x6E, 0x31, 0x31, 0x02, 0x51, 0xD5, 0x44, 0x31, 0x67, 0x98 },
    };
    
    struct wowcrypt
    {
       EVP_CIPHER_CTX       sarc4[2];
       HMAC_CTX             hmac[2];
       unsigned char        digest[2][SHA_DIGEST_LENGTH];
       struct ring_buffer   rb[2];
       int                  hdr[2];
       int                  enabled;
    };
    
    void wowcrypt_init(struct wowcrypt *s,int rb_order)
    {
       int i;
    
       for (i = 0; i < 2 ; i++) {
          HMAC_CTX_init(&s->hmac[i]);
          HMAC_Init_ex(&s->hmac[i], wowcrypt_keys[i], WOWCRYPT_SEED_KEY_SIZE, EVP_sha1(), NULL);
          EVP_CIPHER_CTX_init(&s->sarc4[i]);
          ring_buffer_create(&s->rb[i],rb_order);
          s->hdr[i] = 0;
       }
    
       s->enabled = 0;
    
    }
    
    void wowcrypt_destroy(struct wowcrypt *s)
    {
       int i;
    
       for (i = 0; i < 2 ; i++) {
          EVP_CIPHER_CTX_cleanup(&s->sarc4[i]);
          HMAC_CTX_cleanup(&s->hmac[i]);
          ring_buffer_free(&s->rb[i]);
       }
    
    }
    
    void wowcrypt_enable(struct wowcrypt *s,unsigned char *seed, int length)
    {
       unsigned char  buf[WOWCRYPT_DISCARD_LEN];
       unsigned int   hlen;
       int            len;
       int            i;
    
       for (i = 0; i < 2 ; i++){
          HMAC_Update(&s->hmac[i], seed, length);
          HMAC_Final(&s->hmac[i], s->digest[i], &hlen);
    
          EVP_EncryptInit_ex(&s->sarc4[i], EVP_rc4(), NULL, NULL, NULL);
          EVP_CIPHER_CTX_set_key_length(&s->sarc4[i], SHA_DIGEST_LENGTH);
          EVP_EncryptInit_ex(&s->sarc4[i], NULL, NULL, s->digest[i], NULL);
    
          memset(buf,0,WOWCRYPT_DISCARD_LEN);
    
          EVP_EncryptUpdate(&s->sarc4[i],buf,&len,buf,WOWCRYPT_DISCARD_LEN);
          EVP_EncryptFinal_ex(&s->sarc4[i],buf,&len);
       }
    
       s->enabled = 1;
    
    }
    
    int wowcrypt_free_bytes(struct wowcrypt *s,int dir) {
       return ring_buffer_count_free_bytes(&s->rb[dir & 0x1]);
    }
    
    void wowcrypt_insert(struct wowcrypt *s, int dir, int len, unsigned char *data)
    {
       struct ring_buffer *rb = &s->rb[dir & 0x1];
    
       memcpy(ring_buffer_write_address(rb),data,len);
    
       ring_buffer_write_advance(rb,len);
    }
    
    int wowcrypt_get_msg(struct wowcrypt *s, int dir, int *opcode,int *length, unsigned char **data)
    {
       const int               d = dir & 0x01;
       struct ring_buffer    *rb = &s->rb[d];
       int                  *hdr = &s->hdr[d];
       const int           oplen = wowcrypt_oplen[d];
       const int              bl = ring_buffer_count_bytes(rb);
       unsigned char         *ba = ring_buffer_read_address(rb);
       int l;
    
       if (!*hdr) {
          if (bl >= 2 + oplen) {
             if (s->enabled) {
                EVP_EncryptUpdate(&s->sarc4[d],ba,&l,ba,2 + oplen);
                EVP_EncryptFinal_ex(&s->sarc4[d],ba,&l);
             }
             *hdr = 1;
          } else {
             return 0;
          }
       }
    
       l = ntohs(*(unsigned short*)ba);
    
       if (bl >= 2 + l) {
          *length = l - oplen;
          *opcode = (oplen == 2) ? *(unsigned short*)(ba+2) : *(unsigned int*)(ba+2);
          *data   = ba + 2 + oplen;
          return 1;
       } else {
          return 0;
       }
    }
    
    void wowcrypt_pop_msg(struct wowcrypt *s, int dir)
    {
       const int               d = dir & 0x01;
       struct ring_buffer    *rb = &s->rb[d];
       int                  *hdr = &s->hdr[d];
       const unsigned char   *ba = ring_buffer_read_address(rb);
    
       ring_buffer_read_advance(rb,2 + ntohs(*(unsigned short*)ba));
    
       *hdr = 0;
    }

  4. #4
    adaephon's Avatar Active Member
    Reputation
    76
    Join Date
    May 2009
    Posts
    167
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Here is my implementation of WoW packet decryption written in C. The ring buffer implementation may require some minor tweaks to work on windows. If anyone wants to modify it and post the changes I'll incorporate them and edit my post.

    Everything should work as is on linux or OSX.
    Re-read the start of the post. Then install Linux. Then try again.

    Or figure out what you need to change to get it to compile under Windows. I.e. not using unix specific includes.

  5. #5
    pendra's Avatar Active Member
    Reputation
    46
    Join Date
    Jul 2008
    Posts
    42
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by AlexF View Post
    i Run source in VC++ 2008 but


    i want download sys/mman.h and unistd.h but dont known where it

    The ringbuffer uses mmap to allow for efficiency, as it allows reading and writing through the end of the ringbuffer with automatic wrap around.

    Windows does not have mmap, but they have some similar stuff.

    Your options are (1) Figure out how to replace the mmap calls with a windows equivalent or (2) Figure out how to modify the ring buffer to make the mmap trick optional or (3) find a completely different ring buffer implementation that works on windows and swap it in or (4) modify wowcrypt to use a plain old char *buffer.

  6. #6
    Nonal's Avatar Member
    Reputation
    3
    Join Date
    Sep 2008
    Posts
    17
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    Works great here, except one bug which gets me out of sync on a regular basis. In wowcrypt_get_msg/wowcrypt_pop_msg you need to account for long opcodes for which "size" is stored 3 bytes, not 2. This can be detected if first byte is >=0x80.

    Mangos code is quite explicit:
    Code:
        ServerPktHeader(uint32 size, uint16 cmd) : size(size)
        {
            uint8 headerIndex=0;
            if(isLargePacket())
            {
                sLog.outDebug("initializing large server to client packet. Size: %u, cmd: %u", size, cmd);
                header[headerIndex++] = 0x80|(0xFF &(size>>16));
            }
            header[headerIndex++] = 0xFF &(size>>8);
            header[headerIndex++] = 0xFF &size;
    
            header[headerIndex++] = 0xFF & cmd;
            header[headerIndex++] = 0xFF & (cmd>>8);
        }
    Once fixed your code never looses sync.

    Proposed fix:

    Code:
    void wowcrypt_pop_msg(struct wowcrypt *s, int dir)
    {
       const int               d = dir & 0x01;
       struct ring_buffer    *rb = &s->rb[d];
       int                  *hdr = &s->hdr[d];
       const unsigned char   *ba = ring_buffer_read_address(rb);
    
       if (*ba < 0x80) {
    	   ring_buffer_read_advance(rb,2 + ntohs(*(unsigned short*)(ba)));
       } else {
    	   ring_buffer_read_advance(rb,3 + ntohs(*(unsigned short*)(ba+1)) + ((*ba & 0x7f)<<16) );
       }
    
       *hdr = 0;
    }
    Code:
    int wowcrypt_get_msg(struct wowcrypt *s, int dir, int *opcode,int *length, unsigned char **data)
    {
       const int               d = dir & 0x01;
       struct ring_buffer    *rb = &s->rb[d];
       int                  *hdr = &s->hdr[d];
       const int           oplen = wowcrypt_oplen[d];
       const int              bl = ring_buffer_count_bytes(rb);
       unsigned char         *ba = ring_buffer_read_address(rb);
       int sizelen = 2;
       int l;
    
       if (*hdr==0) {
          if (bl >= sizelen + oplen) {
             if (s->enabled) {
                EVP_EncryptUpdate(&s->sarc4[d],ba,&l,ba,sizelen + oplen);
                EVP_EncryptFinal_ex(&s->sarc4[d],ba,&l);
             }
             *hdr = 1;
          } else {
             return 0;
          }	
       }
    
       if (*ba >= 0x80) {
    	  sizelen = 3;
       }
    
       if (*hdr == 1 && sizelen == 3) {
         if (bl >= sizelen + oplen) {
    	     if (s->enabled) {
    	        EVP_EncryptUpdate(&s->sarc4[d],ba+oplen+2,&l,ba+oplen+2,1);
    	        EVP_EncryptFinal_ex(&s->sarc4[d],ba+oplen+2,&l);
    	     }
    	     *hdr = 2;
    	  } else {
    	     return 0;
    	  }	
       } else {
    	 *hdr = 2;
       }
    
       l = ntohs(*(unsigned short*)(ba+sizelen-2));
       if (sizelen==3) {
    	   unsigned int added = (*ba) & 0x7F;
    	   l += added<<16;
       }
    
       if (bl >= sizelen + l) {
          *length = l - oplen;
          *opcode = (oplen == 2) ? *(unsigned short*)(ba+sizelen) : *(unsigned int*)(ba+sizelen);
          *data   = ba + sizelen + oplen;
          return 1;
       } else {
          return 0;
       }
    }
    Thanks,

  7. #7
    pendra's Avatar Active Member
    Reputation
    46
    Join Date
    Jul 2008
    Posts
    42
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Nonal View Post
    Hi,

    Works great here, except one bug which gets me out of sync on a regular basis. In wowcrypt_get_msg/wowcrypt_pop_msg you need to account for long opcodes for which "size" is stored 3 bytes, not 2. This can be detected if first byte is >=0x80.
    Thanks!

    I've updated my original post to include your fix.

  8. #8
    SinnerG's Avatar Member
    Reputation
    6
    Join Date
    Aug 2006
    Posts
    78
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm trying to convert this to c# - maybe someone already has such a thing? (or a .dll that I can use?)

    I hope this openssl wrapper I'm using is sufficient ^^

    edit : I failed :/ - Anyone who is capable to port it to windows (dll) / c#?
    Last edited by SinnerG; 03-05-2010 at 12:51 AM.

  9. #9
    amadmonk's Avatar Active Member
    Reputation
    124
    Join Date
    Apr 2008
    Posts
    772
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I posted a pretty much complete C# WoW packet decryptor some time ago. You could rip the key out of this code and it should still work (unless they've dramatically changed the encryption since then).

    Search on the forum for it. Or, just adapt this to C# (it'd be a good learning experience).
    Don't believe everything you think.

  10. #10
    SinnerG's Avatar Member
    Reputation
    6
    Join Date
    Aug 2006
    Posts
    78
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by amadmonk View Post
    I posted a pretty much complete C# WoW packet decryptor some time ago. You could rip the key out of this code and it should still work (unless they've dramatically changed the encryption since then).

    Search on the forum for it. Or, just adapt this to C# (it'd be a good learning experience).
    That is what I attempted. Problem is that the C code is hell for me - I have no idea what most things are doing :s

    I tried to convert it 1 on 1 but 1) the openssl c# binding I used was lacking 2) I had issues with finding a replacement for the ring buffer

    I'll look for your decryptor now :P

  11. #11
    SinnerG's Avatar Member
    Reputation
    6
    Join Date
    Aug 2006
    Posts
    78
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    WoW has two static HMAC keys which I've hard coded into the source. I've never seen these change.
    If you are known with the MaNGOS src, are these the v and s values?

  12. #12
    SinnerG's Avatar Member
    Reputation
    6
    Join Date
    Aug 2006
    Posts
    78
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by amadmonk View Post
    I posted a pretty much complete C# WoW packet decryptor some time ago. You could rip the key out of this code and it should still work (unless they've dramatically changed the encryption since then).

    Search on the forum for it. Or, just adapt this to C# (it'd be a good learning experience).

    amadmonk, I'm trying to use your code now.

    I used the following code to get the session key:
    public static class Wow
    {
    static readonly IntPtr ThePointer = new IntPtr(0x00C93410); //old=0xC923C0 //new=0x00C93410
    static readonly IntPtr TheOffset = new IntPtr(0x50;

    public static byte[] GetSessionKey()
    {

    var proc = Process.GetProcessesByName("Wow")[0];

    var x = new ProcessMemoryChunk(proc, ThePointer, IntPtr.Size);

    var p1 = x.ReadIntPtr();


    var x2 = new ProcessMemoryChunk(proc, new IntPtr(p1.ToInt64() + TheOffset.ToInt64()), IntPtr.Size);
    var p2 = x2.ReadIntPtr();
    var x3 = new ProcessMemoryChunk(proc, p2, 40);
    return x3.Read(); // could it be possible?
    }
    }
    then I init it in my code (in CMSG_AUTH_SESSION)

    Context.Crypt = new WorldCrypt(Wow.GetSessionKey());
    Context.Crypt.setEnabled();
    Then, after CMSG_AUTH_SESSION, I receive 6 bytes and decrypt the data, byte per byte as such:

    var processed = new List<byte>();
    foreach (var b in receivedData)
    {
    processed.Add(Context.Crypt.processClientSide(b));
    }
    byte[] decrypted = processed.ToArray();
    Is it correct that the client sends only 6 bytes after CMSG_AUTH_SESSION + is this the correct method to decrypt / retrieve the session key?

    It seems ok but this is the raw result of the decrypted 6 bytes:

    -
    processed Count = 6 System.Collections.Generic.List<byte>
    [0] 172 byte
    [1] 208 byte
    [2] 122 byte
    [3] 1 byte
    [4] 157 byte
    [5] 117 byte
    Since the 'header' differs each time, I think something went wrong :P

    edit: Hmm, either I'm getting things to early, or getting the wrong things to start with - Just noticed (gaah) that the session key that I fetch only exists out of 0 bytes :/

    edit 2 : might have found it.. I thought it [[address] + offset] refered to ANOTHER address and not where the data starts - getting real data now

    edit 3 : yup that did the trick, you can ignore this post now ;p (jeez such lot of wasted time lol ah well )
    Last edited by SinnerG; 03-05-2010 at 10:56 PM.

  13. #13
    TOM_RUS's Avatar Legendary
    Reputation
    914
    Join Date
    May 2008
    Posts
    699
    Thanks G/R
    0/52
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by SinnerG View Post
    If you are known with the MaNGOS src, are these the v and s values?
    No they aren't v and s values. And they are changed in current 3.3.3 PTR build.

Similar Threads

  1. ESO Packet Decrypt/Encrypt. Close, but cannot determine key
    By jarjar1 in forum Elder Scrolls Online General
    Replies: 3
    Last Post: 10-06-2013, 12:19 PM
  2. WoW Packets QUESTION
    By thewowar in forum World of Warcraft Exploits
    Replies: 5
    Last Post: 02-28-2012, 12:39 AM
  3. [python] wow packet logging proxy
    By argh44z in forum WoW Memory Editing
    Replies: 15
    Last Post: 09-21-2011, 11:25 PM
  4. WPE Cannot detect WoW packets
    By popinman322 in forum WoW Bots Questions & Requests
    Replies: 0
    Last Post: 07-01-2010, 12:46 PM
  5. WoW packets.. where are they?
    By Stretch in forum World of Warcraft General
    Replies: 0
    Last Post: 01-28-2007, 01:54 PM
All times are GMT -5. The time now is 07:17 PM. Powered by vBulletin® Version 4.2.3
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2024 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search