Eudemon Packet Encryption 3/30/12

03/31/2012 03:10 InfamousNoone#1
Reversed from client version v1581.

Code:
/*
    EUDEMON PACKET ENCRYPTION/DECRYPTION, MARCH 30th, 2012
    Copyright (C) 2012 InfamousNoone (of http://elitepvpers.com) and http://conquerai.com
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    GNU General Public License: http://www.gnu.org/licenses/
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using ICSPublicLibrary;

namespace EudemonAI.Network
{
    public unsafe class EudemonCryptographer : INetworkEncryption
    {
        //
        //
        //  REVERSED BY InfamousNoone @ E*PVP (http://www.elitepvpers.com)
        //  OF CONQUERAI (http://conquerai.com) -- 2012, March 28th
        //
        //  Special thanks for contributions (from elitepvpers.com):
        //  hio77
        //  funhacker
        //
        //

        [StructLayout(LayoutKind.Explicit)]
        private struct Counter
        {
            [FieldOffset(0)]
            public ushort Value;
            [FieldOffset(0)]
            public byte Low;
            [FieldOffset(1)]
            public byte High;
        }

        private static byte[] skey1 = 
        {
             0x9A,0x71,0x9E,0x65,0xB2,0xE9,0x96,0xBD,0x0A,0xA1,0xCE,0x55,0xA2,0x99,0x46,0x2D
            ,0x7A,0xD1,0xFE,0x45,0x92,0x49,0xF6,0x9D,0xEA,0x01,0x2E,0x35,0x82,0xF9,0xA6,0x0D
            ,0x5A,0x31,0x5E,0x25,0x72,0xA9,0x56,0x7D,0xCA,0x61,0x8E,0x15,0x62,0x59,0x06,0xED
            ,0x3A,0x91,0xBE,0x05,0x52,0x09,0xB6,0x5D,0xAA,0xC1,0xEE,0xF5,0x42,0xB9,0x66,0xCD
            ,0x1A,0xF1,0x1E,0xE5,0x32,0x69,0x16,0x3D,0x8A,0x21,0x4E,0xD5,0x22,0x19,0xC6,0xAD
            ,0xFA,0x51,0x7E,0xC5,0x12,0xC9,0x76,0x1D,0x6A,0x81,0xAE,0xB5,0x02,0x79,0x26,0x8D
            ,0xDA,0xB1,0xDE,0xA5,0xF2,0x29,0xD6,0xFD,0x4A,0xE1,0x0E,0x95,0xE2,0xD9,0x86,0x6D
            ,0xBA,0x11,0x3E,0x85,0xD2,0x89,0x36,0xDD,0x2A,0x41,0x6E,0x75,0xC2,0x39,0xE6,0x4D
        };
        private static byte[] skey2 = { 0x7E, 0xDE, 0xFE, 0x5E };
        private static byte[] skey3 =
        {
             0xE4,0xAF,0x60,0x3B,0xCC,0x37,0x68,0xE3,0x74,0x7F,0x30,0x0B,0xDC,0x47,0xB8,0x73
            ,0x04,0x0F,0x00,0x1B,0xEC,0x97,0x08,0xC3,0x94,0xDF,0xD0,0x6B,0xFC,0x27,0x58,0x53
            ,0x24,0xEF,0xA0,0x7B,0x0C,0x77,0xA8,0x23,0xB4,0xBF,0x70,0x4B,0x1C,0x87,0xF8,0xB3
            ,0x44,0x4F,0x40,0x5B,0x2C,0xD7,0x48,0x03,0xD4,0x1F,0x10,0xAB,0x3C,0x67,0x98,0x93
            ,0x64,0x2F,0xE0,0xBB,0x4C,0xB7,0xE8,0x63,0xF4,0xFF,0xB0,0x8B,0x5C,0xC7,0x38,0xF3
            ,0x84,0x8F,0x80,0x9B,0x6C,0x17,0x88,0x43,0x14,0x5F,0x50,0xEB,0x7C,0xA7,0xD8,0xD3
            ,0xA4,0x6F,0x20,0xFB,0x8C,0xF7,0x28,0xA3,0x34,0x3F,0xF0,0xCB,0x9C,0x07,0x78,0x33
            ,0xC4,0xCF,0xC0,0xDB,0xAC,0x57,0xC8,0x83,0x54,0x9F,0x90,0x2B,0xBC,0xE7,0x18,0x13
        };
        private static byte[] skey4 =
        {
             0x1A,0x50,0x9E,0x44,0x32,0xC8,0x96,0x1C,0x0A,0x80,0xCE,0x54,0x22,0x98,0x46,0x0C
            ,0x7A,0xD0,0xFE,0x44,0x12,0x48,0xF6,0x1C,0x6A,0x00,0x2E,0x14,0x02,0xD8,0xA6,0x0C
            ,0x5A,0x10,0x5E,0x04,0x72,0x88,0x56,0x5C,0x4A,0x40,0x8E,0x14,0x62,0x58,0x06,0x4C
            ,0x3A,0x90,0xBE,0x04,0x52,0x08,0xB6,0x5C,0x2A,0xC0,0xEE,0x54,0x42,0x98,0x66,0x4C
        };

        private byte[] key1, key2, key3, key4;
        private Counter inCounter;
        private Counter outCounter;
        public bool IsServer { get; set; }
        public uint CryptoKey { get; private set; }

        public EudemonCryptographer()
        {
            IsServer = false;
            inCounter = new Counter();
            outCounter = new Counter();

            key1 = Inflate(skey1, 256);
            key2 = Inflate(skey2, 256);
            key3 = Inflate(skey3, 256);
            key4 = Inflate(skey4, 256);
        }
        private byte[] Inflate(byte[] key, int size)
        {
            if (size % key.Length != 0)
                throw new ArgumentException("Cannot inflate, size modulated by the key length must be zero.");
            byte[] inflation = new byte[size];
            for (int i = 0; i < size; i += key.Length)
                Array.Copy(key, 0, inflation, i, key.Length);
            return inflation;
        }
        private byte Nibble(byte arg)
        {
            return (byte)((arg >> 4) | (arg << 4));
        }
        private uint ExchangeLongBits(uint nData, int nBits) 
        {
	        return (nData>>nBits) | (nData<<(32-nBits)); 
        } 
        public void SetKey(uint accountId, uint cryptoId)
        {
            uint key = ExchangeLongBits(accountId, 7) + ExchangeLongBits(cryptoId, 0xb);
            key ^= 0x6279;
            key ^= cryptoId;

            uint num2 = (key & 0xFFFF); // edx
            uint num1 = ((key >> 16) & 0xFFFF); // eax
            num1 *= num1;
            num2 *= num2;

            fixed (void* ptr1 = key1, ptr2 = key2, ptr3 = key3, ptr4 = key4)
            {
                uint* k1 = (uint*)ptr1;
                uint* k2 = (uint*)ptr2;
                uint* k3 = (uint*)ptr3;
                uint* k4 = (uint*)ptr4;

                for (int i = 0; i < 64; i++)
                {
                    k1[i] = k1[i] ^ num2;
                    k2[i] = k2[i] ^ num1;
                    k3[i] = k1[i] ^ k2[i];
                    k4[i] = k1[i] & k2[i];
                }
            }
        }
        public void Decrypt(byte[] @in, int inOffset, byte[] @out, int outOffset, int length)
        {
            if (!IsServer) // Server->Client
            {
                for (int i = 0; i < length; i++)
                {
                    byte al = @in[inOffset + i];
                    al = Nibble(al);
                    al -= skey4[inCounter.High % skey4.Length];
                    al ^= skey2[inCounter.High % skey2.Length];
                    al -= skey3[inCounter.Low % skey3.Length];
                    al ^= skey1[inCounter.Low % skey1.Length];

                    @out[outOffset + i] = al;
                    inCounter.Value++;
                }
            }
            else // Client -> Server
            {
                for (int i = 0; i < length; i++)
                {
                    byte al = @in[inOffset + i];
                    al = Nibble(al);
                    al -= key4[inCounter.High];
                    al ^= key2[inCounter.High];
                    al -= key3[inCounter.Low];
                    al ^= key1[inCounter.Low];

                    @out[outOffset + i] = al;
                    inCounter.Value++;
                }
            }
            //fixed (byte* bf = @out)
            //    App.Log("Decrypt After -- \n" + Packet.CreatePacketStringWithNumbers(bf + outOffset, length));
        }
        public unsafe void Encrypt(byte* @in, byte[] @out, int length)
        {
             //App.Log("Encrypt Before -- \n" + Packet.CreatePacketStringWithNumbers(@in, length));

            if (!IsServer) // Client->Server
            {
                for (int i = 0; i < length; i++)
                {
                    byte al = @in[i];

                    al ^= key1[outCounter.Low];
                    al += key3[outCounter.Low];
                    al ^= key2[outCounter.High];
                    al += key4[outCounter.High];
                    al = Nibble(al);

                    @out[i] = al;
                    outCounter.Value++;
                }
            }
            else // Server -> Client
            {
                for (int i = 0; i < length; i++)
                {
                    byte al = @in[i];

                    al ^= skey1[outCounter.Low % skey1.Length];
                    al += skey3[outCounter.Low % skey3.Length];
                    al ^= skey2[outCounter.High % skey2.Length];
                    al += skey4[outCounter.High % skey4.Length];
                    al = Nibble(al);

                    @out[i] = al;
                    outCounter.Value++;
                }
            }
        }
    }
}
AccountID and CryptoID are sent in the 41F Packet (Server->Client), which is structured:
Code:
            AccountID = msg.ReadInt32();
            CryptoID = msg.ReadInt32();
            int port = msg.ReadInt32();
            int unknown = msg.ReadInt32();
            string host = msg.ReadCString(32);
When the client sends the 41C packet on the game server, it then switches to these new keys. Therefore, packets decrypted from the client must now be decrypted using the new keys generated by SetKey().

Regards,
Infamous Noone.
03/31/2012 04:00 funhacker#2
Sorry I couldn't be of much help, sadly I've no real experience in encryption/decryption or even packets for that matter.
However you may want to put in the main post that this is done on client version 1581, may help further down the track if ever abandoned and TQ changes encryption
04/01/2012 10:53 Kiyono#3
So does this sudden interest in EO mean that you will expand proxy operations to EO?
04/01/2012 18:45 InfamousNoone#4
Quote:
Originally Posted by Kiyono View Post
So does this sudden interest in EO mean that you will expand proxy operations to EO?
If time allows it, likely.
07/24/2018 02:50 hasbul#5
cool