Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

Original Encrypt/Decrypt C3/C4 packets (eX803 main)pcode

Junior Spellweaver
Joined
Dec 23, 2006
Messages
140
Reaction score
153
Like title says..
It's server side version of original encrypt/decrypt of C3/C4 packets from all eX mains.
Tested and fully working on 1.05.13 main GMO, for S9 main initial data (DefaultData in CSessionCryptor) may be different.

Requirement: CryptoPP library (I used 5.6.2)

.cpp:
Code:
#include "stdafx.h"#include "SessionCryptor.h"


CCryptoModulus::CCryptoModulus()
{
    this->m_cipher = NULL;
}


CCryptoModulus::~CCryptoModulus()
{
    if (this->m_cipher != NULL)
    {
        delete this->m_cipher;
        this->m_cipher = NULL;
    }
}


bool CCryptoModulus::InitCrypto(BYTE *temp, DWORD length)
{
    int hashid = 0;
    BYTE digest[2048];


    memset(digest, 0, sizeof(digest));


    if (length >= 10)
    {
        hashid = temp[4] ^ (temp[3] | (temp[1] ^ temp[0]) < temp[2] % 3);
    }


    else
    {
        hashid = temp[0];
    }


    hashid = (hashid % 11) - 1;


    switch (hashid)
    {
        case 0:
        {
            CryptoPP::SHA256 hash;
            hash.Update(temp, length);
            hash.Final(digest);


            this->m_algorithm = digest[0] % 10;
            this->InitCrypto(this->m_algorithm, digest, 32);
        }
        break;


        case 2:
        {
            CryptoPP::SHA512 hash;
            hash.Update(temp, length);
            hash.Final(digest);


            this->m_algorithm = digest[1] % 10;
            this->InitCrypto(this->m_algorithm, digest, 64);
        }
        break;
        
        case 3:
        {
            CryptoPP::SHA384 hash;
            hash.Update(temp, length);
            hash.Final(digest);


            this->m_algorithm = digest[2] % 10;
            this->InitCrypto(this->m_algorithm, digest, 48);
        }
        break;


        case 4:
        {
            CryptoPP::Weak1::MD4 hash;
            hash.Update(temp, length);
            hash.Final(digest);


            this->m_algorithm = digest[3] % 10;
            this->InitCrypto(this->m_algorithm, digest, 16);
        }
        break;


        case 5:
        {
            CryptoPP::Weak1::MD5 hash;
            hash.Update(temp, length);
            hash.Final(digest);


            this->m_algorithm = digest[4] % 10;
            this->InitCrypto(this->m_algorithm, digest, 16);
        }
        break;


        case 6:
        {
            CryptoPP::RIPEMD160 hash;
            hash.Update(temp, length);
            hash.Final(digest);


            this->m_algorithm = digest[5] % 10;
            this->InitCrypto(this->m_algorithm, digest, 20);
        }
        break;


        case 7:
        {
            CryptoPP::RIPEMD320 hash;
            hash.Update(temp, length);
            hash.Final(digest);


            this->m_algorithm = digest[6] % 10;
            this->InitCrypto(this->m_algorithm, digest, 40);
        }
        break;


        case 8:
        {
            CryptoPP::RIPEMD128 hash;
            hash.Update(temp, length);
            hash.Final(digest);


            this->m_algorithm = digest[7] % 10;
            this->InitCrypto(this->m_algorithm, digest, 16);
        }
        break;


        case 9:
        {
            CryptoPP::RIPEMD256 hash;
            hash.Update(temp, length);
            hash.Final(digest);


            this->m_algorithm = digest[8] % 10;
            this->InitCrypto(this->m_algorithm, digest, 32);
        }
        break;


        default:
        {
            CryptoPP::Weak1::MD5 hash;
            hash.Update(temp, length);
            hash.Final(digest);


            this->m_algorithm = digest[9] % 10;
            this->InitCrypto(this->m_algorithm, digest, 16);
        }
        break;
    }


    return true;
}


bool CCryptoModulus::InitCrypto(DWORD algorithm, BYTE *key, DWORD keyLength)
{
    if (this->m_cipher != NULL)
    {
        delete this->m_cipher;
        this->m_cipher = NULL;
    }


    switch (algorithm % 10)
    {
        case 1:
            this->m_cipher = new ConcreteCipher < CryptoPP::ThreeWay, 2048 > ;
            break;
        case 2:
            this->m_cipher = new ConcreteCipher < CryptoPP::CAST128, 2048 > ;
            break;
        case 4:
            this->m_cipher = new ConcreteCipher < CryptoPP::RC5, 2048 > ;
            break;
        case 5:
            this->m_cipher = new ConcreteCipher < CryptoPP::RC6, 2048 > ;
            break;
        case 6:
            this->m_cipher = new ConcreteCipher < CryptoPP::MARS, 2048 > ;
            break;
        case 7:
            this->m_cipher = new ConcreteCipher < CryptoPP::IDEA, 2048 > ;
            break;
        case 8:
            this->m_cipher = new ConcreteCipher < CryptoPP::GOST, 2048 > ;
            break;
        case 0:
            this->m_cipher = new ConcreteCipher < CryptoPP::TEA, 2048 > ;
            break;
        case 9:        
            {
                switch (key[0] % 8)
                {
                    case 0:
                        this->m_cipher = new ConcreteCipher < CryptoPP::Twofish, 8 > ;
                        break;
                    case 1:
                        this->m_cipher = new ConcreteCipher < CryptoPP::DES, 8 > ;
                        break;
                    case 2:
                        this->m_cipher = new ConcreteCipher < CryptoPP::DES_EDE2, 8 > ;
                        break;
                    case 3:
                        this->m_cipher = new ConcreteCipher < CryptoPP::DES_EDE3, 32 > ;
                        break;
                    case 4:
                        this->m_cipher = new ConcreteCipher < CryptoPP::DES_XEX3, 8 > ;
                        break;
                    case 5:
                        this->m_cipher = new ConcreteCipher < CryptoPP::SKIPJACK, 32 > ;
                        break;
                    case 6:
                        this->m_cipher = new ConcreteCipher < CryptoPP::SAFER_K, 8 > ;
                        break;
                    case 7:
                        this->m_cipher = new ConcreteCipher < CryptoPP::SAFER_SK, 32 > ;
                        break;
                    default:
                        this->m_cipher = new ConcreteCipher < CryptoPP::Twofish, 8 >;
                        break;
                }
            }
            break;
        default:
            this->m_cipher = new ConcreteCipher < CryptoPP::GOST, 2048 > ;
            break;
    }


    this->m_cipher->Init(key, keyLength);
    return true;
}


int CCryptoModulus::Encrypt(void *lpTarget, void *lpSource, int iSize)
{
    if (this->m_cipher)
    {
        return this->m_cipher->Encrypt((const BYTE *)lpSource, iSize, (BYTE *)lpTarget);
    }


    return -1;
}


int CCryptoModulus::Decrypt(void *lpTarget, void *lpSource, int iSize)
{
    if (this->m_cipher)
    {
        return this->m_cipher->Decrypt((const BYTE *)lpSource, iSize, (BYTE *)lpTarget);
    }


    return -1;
}


bool CCryptoModulus::LoadEncryptionKey(char *lpszFileName)
{
    return false;
}


bool CCryptoModulus::LoadDecryptionKey(char *lpszFileName)
{
    return false;
}


CSessionCryptor g_CryptoSessionCS;
CSessionCryptor g_CryptoSessionSC;


CSessionCryptor::CSessionCryptor()
{
    this->m_defaultData = "98135003732920532106543021784355";
}




CSessionCryptor::~CSessionCryptor()
{
    this->cleanup();
}


bool CSessionCryptor::Open(int index)
{
    std::map<int, CSessionCryptor::Cryptor *>::iterator i = this->m_cryptors.find(index);


    if (i != this->m_cryptors.end())
    {
        this->m_cryptors.erase(i);
    }


    CSessionCryptor::Cryptor * cryptor = new CSessionCryptor::Cryptor;
    cryptor->crypto.InitCrypto((BYTE *)this->m_defaultData.c_str(), this->m_defaultData.length());


    this->m_cryptors.insert(std::pair<int, CSessionCryptor::Cryptor *>(index, cryptor));
    return true;
}


void CSessionCryptor::ForceChange(int index, void *data, int iSize)
{
    std::map<int, CSessionCryptor::Cryptor *>::iterator i = this->m_cryptors.find(index);


    if (i == this->m_cryptors.end())
    {
        return;
    }


    i->second->crypto.InitCrypto((BYTE *)data, iSize);
}


int CSessionCryptor::Encrypt(int index, void *lpTarget, void *lpSource, int iSize)
{
    std::map<int, CSessionCryptor::Cryptor *>::iterator i = this->m_cryptors.find(index);


    if (i == this->m_cryptors.end())
    {
        return -1;
    }


    CSessionCryptor::Cryptor *cryptor = i->second;
    int nsize = cryptor->crypto.Encrypt(lpTarget, lpSource, iSize);


    if (lpTarget)
    {
        if (cryptor->encCount == cryptor->crypto.GetMaxRunCount())
        {
            this->updateData(cryptor, (BYTE *)lpSource, iSize);
            this->changeAlgorithm(cryptor);
            cryptor->encCount = 1;
        }


        else
        {
            this->updateData(cryptor, (BYTE *)lpSource, iSize);
            cryptor->encCount++;
        }
    }


    return nsize;
}


int CSessionCryptor::Decrypt(int index, void *lpTarget, void *lpSource, int iSize)
{
    std::map<int, CSessionCryptor::Cryptor *>::iterator i = this->m_cryptors.find(index);


    if (i == this->m_cryptors.end())
    {
        return -1;
    }


    CSessionCryptor::Cryptor *cryptor = i->second;
    int nsize = cryptor->crypto.Decrypt(lpTarget, lpSource, iSize);


    if (lpTarget)
    {
        if (cryptor->decCount == cryptor->crypto.GetMaxRunCount())
        {
            this->updateData(cryptor, (BYTE *)lpTarget, nsize);
            this->changeAlgorithm(cryptor);
            cryptor->decCount = 1;
        }


        else
        {
            this->updateData(cryptor, (BYTE *)lpTarget, nsize);
            cryptor->decCount++;
        }
    }


    return nsize;
}


void CSessionCryptor::Close(int index)
{
    std::map<int, CSessionCryptor::Cryptor *>::iterator i = this->m_cryptors.find(index);


    if (i == this->m_cryptors.end())
    {
        return;
    }


    if (i->second != NULL)
    {
        delete i->second;
    }


    this->m_cryptors.erase(i);
}


int CSessionCryptor::GetEncryptCount(int index)
{
    std::map<int, CSessionCryptor::Cryptor *>::iterator i = this->m_cryptors.find(index);


    if (i == this->m_cryptors.end())
    {
        return 0;
    }


    return i->second->encCount;
}


int CSessionCryptor::GetDecryptCount(int index)
{
    std::map<int, CSessionCryptor::Cryptor *>::iterator i = this->m_cryptors.find(index);


    if (i == this->m_cryptors.end())
    {
        return 0;
    }


    return i->second->decCount;
}


int CSessionCryptor::GetAlgorithm(int index)
{
    std::map<int, CSessionCryptor::Cryptor *>::iterator i = this->m_cryptors.find(index);


    if (i == this->m_cryptors.end())
    {
        return 0;
    }


    return i->second->crypto.GetAlgorithm();
}


void CSessionCryptor::updateData(CSessionCryptor::Cryptor *cryptor, BYTE *buf, int size)
{
    if (size)
    {
        if (size <= 8)
        {
            cryptor->data[buf[0] & 0x7F] = buf[0];
        }


        else
        {
            cryptor->data[buf[0] & 0x7F] = buf[0];
            cryptor->data[buf[1] & 0x7F] = buf[1];
            cryptor->data[buf[2] & 0x7F] = buf[2];
            cryptor->data[buf[3] & 0x7F] = buf[3];
            cryptor->data[buf[4] & 0x7F] = buf[4];
            cryptor->data[buf[5] & 0x7F] = buf[5];
            cryptor->data[buf[6] & 0x7F] = buf[6];
            cryptor->data[buf[7] & 0x7F] = buf[7];
        }
    }
}


void CSessionCryptor::changeAlgorithm(CSessionCryptor::Cryptor *cryptor)
{
    cryptor->crypto.InitCrypto(cryptor->data, 128);
}


void CSessionCryptor::cleanup()
{
    std::map<int, CSessionCryptor::Cryptor *>::iterator i = this->m_cryptors.begin();


    while (i != this->m_cryptors.end())
    {
        if (i->second != NULL)
        {
            delete i->second;
        }


        this->m_cryptors.erase(i);
        i++;
    }
}

.h
Code:
#ifndef SESSIONCRYPTOR_H
#define SESSIONCRYPTOR_H


#include "StdAfx.h"


#pragma once


class AbstractCipher
{
public:
    AbstractCipher()
    {


    }


    virtual ~AbstractCipher()
    {


    }


    int GetMaxRunCount() { return this->m_maxRunCount; }


    virtual bool Init(const BYTE *key, DWORD length) = 0;
    virtual int Encrypt(const BYTE *inBuf, DWORD inLen, BYTE *outBuf) = 0;
    virtual int Decrypt(const BYTE *inBuf, DWORD inLen, BYTE *outBuf) = 0;


    int m_maxRunCount;
};


template <class ALGORITHM, int maxRunCount> class ConcreteCipher : public AbstractCipher
{
public:
    ConcreteCipher()
    {


    }


    virtual ~ConcreteCipher()
    {


    }


    bool Init(const BYTE *key, DWORD length)
    {
        this->enc_.SetKey(key, this->enc_.DEFAULT_KEYLENGTH);
        this->dec_.SetKey(key, this->enc_.DEFAULT_KEYLENGTH);
        this->m_maxRunCount = maxRunCount;


        return true;
    }


    int Encrypt(const BYTE *inBuf, DWORD inLen, BYTE *outBuf)
    {
        int paddingSize = 0;
        int remainder = inLen % this->enc_.BLOCKSIZE;


        if (remainder != 0)
        {
            paddingSize = this->enc_.BLOCKSIZE - remainder;
        }


        int outLen = inLen + paddingSize + 1;


        if (outBuf != NULL)
        {
            BYTE padding[64];
            BYTE obuf[4096];
            BYTE tbuf[4096];


            memset(padding, 0, sizeof(padding));
            memset(tbuf, 0, outLen);
            memset(obuf, 0, outLen);


            memcpy(tbuf, inBuf, inLen);
            memcpy(&tbuf[inLen], padding, paddingSize);


            for (int i = 0; i < outLen; i += this->enc_.BLOCKSIZE)
            {
                this->enc_.ProcessBlock(&tbuf[i], &obuf[i]);
            }


            obuf[outLen - 1] = paddingSize;
            memcpy(outBuf, obuf, outLen);
        }


        return outLen;
    }


    int Decrypt(const BYTE *inBuf, DWORD inLen, BYTE *outBuf)
    {
        int remainder = inLen % this->dec_.BLOCKSIZE;
        int outLen = 0;


        if (remainder == 1)
        {
            int paddingSize = inBuf[inLen - 1];
            outLen = inLen - paddingSize - 1;


            if (outBuf != NULL)
            {
                if (outLen > 0 && outLen <= 4096)
                {
                    BYTE obuf[4096];
                    BYTE ibuf[4096];


                    memset(ibuf, 0, inLen);
                    memset(obuf, 0, inLen);


                    memcpy(ibuf, inBuf, inLen);


                    for (int i = 0; i < outLen; i += this->dec_.BLOCKSIZE)
                    {
                        this->dec_.ProcessBlock(&ibuf[i], &obuf[i]);
                    }


                    memcpy(outBuf, obuf, outLen);
                }
            }


            return outLen;
        }


        return -1;
    }


private:
    typename ALGORITHM::Encryption enc_;
    typename ALGORITHM::Decryption dec_;
};


class CCryptoModulus
{
public:
    CCryptoModulus();
    virtual ~CCryptoModulus();
    
    bool InitCrypto(BYTE *temp, DWORD length);
    bool InitCrypto(DWORD algorithm, BYTE *key, DWORD keyLength);


    DWORD GetAlgorithm() { return this->m_algorithm; }


    int Encrypt(void *lpTarget, void *lpSource, int iSize);
    int Decrypt(void *lpTarget, void *lpSource, int iSize);


    int GetMaxRunCount() { return this->m_cipher->GetMaxRunCount(); }


    bool LoadEncryptionKey(char *lpszFileName);
    bool LoadDecryptionKey(char *lpszFileName);


private:
    AbstractCipher * m_cipher;
    DWORD m_algorithm;
};


class CSessionCryptor
{
public:


    class Cryptor
    {
    public:
        Cryptor()
        {
            this->index = 0;
            this->encCount = 1;
            this->decCount = 1;
            this->callCount = 0;
            memset(this->data, 0x63, sizeof(this->data));
        }


        virtual ~Cryptor()
        {


        }


        int index;
        int encCount;
        int decCount;
        BYTE data[128];
        int callCount;
        CCryptoModulus crypto;
    };


    CSessionCryptor();
    virtual ~CSessionCryptor();


    bool Open(int index);
    void ForceChange(int index, void *data, int iSize);


    int Encrypt(int index, void *lpTarget, void *lpSource, int iSize);
    int Decrypt(int index, void *lpTarget, void *lpSource, int iSize);


    void Close(int index);


    int GetEncryptCount(int index);
    int GetDecryptCount(int index);
    int GetAlgorithm(int index);


    void updateData(CSessionCryptor::Cryptor *cryptor, BYTE *buf, int size);
    void changeAlgorithm(CSessionCryptor::Cryptor *cryptor);


    void cleanup();


private:
    std::map<int, CSessionCryptor::Cryptor *> m_cryptors;
    std::string m_defaultData;
};


extern CSessionCryptor g_CryptoSessionCS;
extern CSessionCryptor g_CryptoSessionSC;


#endif

Credits:

- webzen
- me
- IGCN Team

Big thanks to mirraseq for help with some functions :p
 
Newbie Spellweaver
Joined
Apr 21, 2006
Messages
5
Reaction score
0
Dudi2 you are a god damn pro! :)

Can I ask how did u came up with that algorithm for the server side?

As far as I know encryptions/decryptions those algorithms being used here (AES,DES,MD5, etc) are symmetric, meaning that the same key is used to enc/dec the data. Is this how you came up with the "server side enc/dec" algo? because you reversed the client side algo?

Another question which I would really appreciate if you could answer, where does the "DefaultData" comes from?
Thanks!
 
Newbie Spellweaver
Joined
Sep 19, 2011
Messages
11
Reaction score
1
Thanks a lot for the source! The new version of Global MU uses another encryption layer based on the packet's headcode (the third byte of C1 packets). Can anybody tell me which algorithm is being used there? Or is it a custom one?
 
Back
Top