Downgrading Albetros Cast5->Blowfish

03/06/2013 02:28 .Xori^#1
So I'm back! This time I'm trying with pro4nevers 5518 source.

My goal? (Downgrade back to patch 5065)

Reason? (Just for the experience)

So far this is what I've done.

Modified Blowfish.cs
Code:
public class Blowfish : IDisposable
    {
        [DllImport("libeay32.dll")]
        public extern static void BF_set_key(IntPtr _key, int len, byte[] data);

        [DllImport("libeay32.dll")]
        public extern static void BF_ecb_encrypt(byte[] in_, byte[] out_, IntPtr schedule, int enc);

        [DllImport("libeay32.dll")]
        public extern static void BF_cbc_encrypt(byte[] in_, byte[] out_, int length, IntPtr schedule, byte[] ivec, int enc);

        [DllImport("libeay32.dll")]
        public extern static void BF_cfb64_encrypt(byte[] in_, byte[] out_, int length, IntPtr schedule, byte[] ivec, ref int num, int enc);

        [DllImport("libeay32.dll")]
        public extern static void BF_ofb64_encrypt(byte[] in_, byte[] out_, int length, IntPtr schedule, byte[] ivec, out int num);

        [StructLayout(LayoutKind.Sequential)]
        struct bf_key_st
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)]
            public UInt32[] P;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
            public UInt32[] S;
        }

        private BlowfishAlgorithm _algorithm;
        private IntPtr _key;
        private byte[] _encryptIv;
        private byte[] _decryptIv;
        private int _encryptNum;
        private int _decryptNum;

        public Blowfish(BlowfishAlgorithm algorithm)
        {
            _algorithm = algorithm;
            _encryptIv = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
            _decryptIv = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
            bf_key_st key = new bf_key_st();
            key.P = new UInt32[16 + 2];
            key.S = new UInt32[4 * 256];
            _key = Marshal.AllocHGlobal(key.P.Length * sizeof(UInt32) + key.S.Length * sizeof(UInt32));
            Marshal.StructureToPtr(key, _key, false);
            _encryptNum = 0;
            _decryptNum = 0;
        }

        public void Dispose()
        {
            Marshal.FreeHGlobal(_key);
        }

        public void SetKey(byte[] data)
        {
            _encryptNum = 0;
            _decryptNum = 0;
            BF_set_key(_key, data.Length, data);
        }

        public byte[] Encrypt(byte[] buffer)
        {
            byte[] ret = new byte[buffer.Length];
            switch (_algorithm)
            {
                case BlowfishAlgorithm.ECB:
                    BF_ecb_encrypt(buffer, ret, _key, 1);
                    break;
                case BlowfishAlgorithm.CBC:
                    BF_cbc_encrypt(buffer, ret, buffer.Length, _key, _encryptIv, 1);
                    break;
                case BlowfishAlgorithm.CFB64:
                    BF_cfb64_encrypt(buffer, ret, buffer.Length, _key, _encryptIv, ref _encryptNum, 1);
                    break;
                case BlowfishAlgorithm.OFB64:
                    BF_ofb64_encrypt(buffer, ret, buffer.Length, _key, _encryptIv, out _encryptNum);
                    break;
            }
            return ret;
        }

        public byte[] Decrypt(byte[] buffer)
        {
            byte[] ret = new byte[buffer.Length];
            switch (_algorithm)
            {
                case BlowfishAlgorithm.ECB:
                    BF_ecb_encrypt(buffer, ret, _key, 0);
                    break;
                case BlowfishAlgorithm.CBC:
                    BF_cbc_encrypt(buffer, ret, buffer.Length, _key, _decryptIv, 0);
                    break;
                case BlowfishAlgorithm.CFB64:
                    BF_cfb64_encrypt(buffer, ret, buffer.Length, _key, _decryptIv, ref _decryptNum, 0);
                    break;
                case BlowfishAlgorithm.OFB64:
                    BF_ofb64_encrypt(buffer, ret, buffer.Length, _key, _decryptIv, out _decryptNum);
                    break;
            }
            return ret;
        }

        public byte[] EncryptIV
        {
            get { return _encryptIv; }
            set { System.Buffer.BlockCopy(value, 0, _encryptIv, 0, 8); }
        }

        public byte[] DecryptIV
        {
            get { return _decryptIv; }
            set { System.Buffer.BlockCopy(value, 0, _decryptIv, 0, 8); }
        }
    }
Changed gamekey under Common.cs

Code:
public const string GameKey = "DR654dt34trg4UI6";
Modified PasswordCrytorgraphy.cs

Code:
public sealed class ConquerPasswordCryptographer
    {
        #region Key 1
        public static byte[] Key1 = new byte[] { 
            0x9d, 0x90, 0x83, 0x8a, 0xd1, 140, 0xe7, 0xf6, 0x25, 40, 0xeb, 130, 0x99, 100, 0x8f, 0x2e, 
            0x2d, 0x40, 0xd3, 250, 0xe1, 0xbc, 0xb7, 230, 0xb5, 0xd8, 0x3b, 0xf2, 0xa9, 0x94, 0x5f, 30, 
            0xbd, 240, 0x23, 0x6a, 0xf1, 0xec, 0x87, 0xd6, 0x45, 0x88, 0x8b, 0x62, 0xb9, 0xc4, 0x2f, 14, 
            0x4d, 160, 0x73, 0xda, 1, 0x1c, 0x57, 0xc6, 0xd5, 0x38, 0xdb, 210, 0xc9, 0xf4, 0xff, 0xfe, 
            0xdd, 80, 0xc3, 0x4a, 0x11, 0x4c, 0x27, 0xb6, 0x65, 0xe8, 0x2b, 0x42, 0xd9, 0x24, 0xcf, 0xee, 
            0x6d, 0, 0x13, 0xba, 0x21, 0x7c, 0xf7, 0xa6, 0xf5, 0x98, 0x7b, 0xb2, 0xe9, 0x54, 0x9f, 0xde, 
            0xfd, 0xb0, 0x63, 0x2a, 0x31, 0xac, 0xc7, 150, 0x85, 0x48, 0xcb, 0x22, 0xf9, 0x84, 0x6f, 0xce, 
            0x8d, 0x60, 0xb3, 0x9a, 0x41, 220, 0x97, 0x86, 0x15, 0xf8, 0x1b, 0x92, 9, 180, 0x3f, 190, 
            0x1d, 0x10, 3, 10, 0x51, 12, 0x67, 0x76, 0xa5, 0xa8, 0x6b, 2, 0x19, 0xe4, 15, 0xae, 
            0xad, 0xc0, 0x53, 0x7a, 0x61, 60, 0x37, 0x66, 0x35, 0x58, 0xbb, 0x72, 0x29, 20, 0xdf, 0x9e, 
            0x3d, 0x70, 0xa3, 0xea, 0x71, 0x6c, 7, 0x56, 0xc5, 8, 11, 0xe2, 0x39, 0x44, 0xaf, 0x8e, 
            0xcd, 0x20, 0xf3, 90, 0x81, 0x9c, 0xd7, 70, 0x55, 0xb8, 0x5b, 0x52, 0x49, 0x74, 0x7f, 0x7e, 
            0x5d, 0xd0, 0x43, 0xca, 0x91, 0xcc, 0xa7, 0x36, 0xe5, 0x68, 0xab, 0xc2, 0x59, 0xa4, 0x4f, 110, 
            0xed, 0x80, 0x93, 0x3a, 0xa1, 0xfc, 0x77, 0x26, 0x75, 0x18, 0xfb, 50, 0x69, 0xd4, 0x1f, 0x5e, 
            0x7d, 0x30, 0xe3, 170, 0xb1, 0x2c, 0x47, 0x16, 5, 200, 0x4b, 0xa2, 0x79, 4, 0xef, 0x4e, 
            13, 0xe0, 0x33, 0x1a, 0xc1, 0x5c, 0x17, 6, 0x95, 120, 0x9b, 0x12, 0x89, 0x34, 0xbf, 0x3e
         };
        #endregion
        #region Key 2
        public static byte[] Key2 = new byte[] { 
            0x62, 0x4f, 0xe8, 0x15, 0xde, 0xeb, 4, 0x91, 0x1a, 0xc7, 0xe0, 0x4d, 0x16, 0xe3, 0x7c, 0x49, 
            210, 0x3f, 0xd8, 0x85, 0x4e, 0xdb, 0xf4, 1, 0x8a, 0xb7, 0xd0, 0xbd, 0x86, 0xd3, 0x6c, 0xb9, 
            0x42, 0x2f, 200, 0xf5, 190, 0xcb, 0xe4, 0x71, 250, 0xa7, 0xc0, 0x2d, 0xf6, 0xc3, 0x5c, 0x29, 
            0xb2, 0x1f, 0xb8, 0x65, 0x2e, 0xbb, 0xd4, 0xe1, 0x6a, 0x97, 0xb0, 0x9d, 0x66, 0xb3, 0x4c, 0x99, 
            0x22, 15, 0xa8, 0xd5, 0x9e, 0xab, 0xc4, 0x51, 0xda, 0x87, 160, 13, 0xd6, 0xa3, 60, 9, 
            0x92, 0xff, 0x98, 0x45, 14, 0x9b, 180, 0xc1, 0x4a, 0x77, 0x90, 0x7d, 70, 0x93, 0x2c, 0x79, 
            2, 0xef, 0x88, 0xb5, 0x7e, 0x8b, 0xa4, 0x31, 0xba, 0x67, 0x80, 0xed, 0xb6, 0x83, 0x1c, 0xe9, 
            0x72, 0xdf, 120, 0x25, 0xee, 0x7b, 0x94, 0xa1, 0x2a, 0x57, 0x70, 0x5d, 0x26, 0x73, 12, 0x59, 
            0xe2, 0xcf, 0x68, 0x95, 0x5e, 0x6b, 0x84, 0x11, 0x9a, 0x47, 0x60, 0xcd, 150, 0x63, 0xfc, 0xc9, 
            0x52, 0xbf, 0x58, 5, 0xce, 0x5b, 0x74, 0x81, 10, 0x37, 80, 0x3d, 6, 0x53, 0xec, 0x39, 
            0xc2, 0xaf, 0x48, 0x75, 0x3e, 0x4b, 100, 0xf1, 0x7a, 0x27, 0x40, 0xad, 0x76, 0x43, 220, 0xa9, 
            50, 0x9f, 0x38, 0xe5, 0xae, 0x3b, 0x54, 0x61, 0xea, 0x17, 0x30, 0x1d, 230, 0x33, 0xcc, 0x19, 
            0xa2, 0x8f, 40, 0x55, 30, 0x2b, 0x44, 0xd1, 90, 7, 0x20, 0x8d, 0x56, 0x23, 0xbc, 0x89, 
            0x12, 0x7f, 0x18, 0xc5, 0x8e, 0x1b, 0x34, 0x41, 0xca, 0xf7, 0x10, 0xfd, 0xc6, 0x13, 0xac, 0xf9, 
            130, 0x6f, 8, 0x35, 0xfe, 11, 0x24, 0xb1, 0x3a, 0xe7, 0, 0x6d, 0x36, 3, 0x9c, 0x69, 
            0xf2, 0x5f, 0xf8, 0xa5, 110, 0xfb, 20, 0x21, 170, 0xd7, 240, 0xdd, 0xa6, 0xf3, 140, 0xd9
         };

        #endregion

        public class ConquerCipher
        {
            private OpenSSL.Blowfish _blowfish = new OpenSSL.Blowfish(OpenSSL.BlowfishAlgorithm.CFB64);

            public ConquerCipher(string key)
            {
                this._blowfish.SetKey(Encoding.ASCII.GetBytes(key));
            }

            public void Decrypt(byte[] packet, byte Out)
            {
                byte[] src = this._blowfish.Decrypt(packet);
                Buffer.BlockCopy(src, 0, packet, 0, src.Length);
            }

            public void Encrypt(byte[] packet, byte Out)
            {
                byte[] src = this._blowfish.Encrypt(packet);
                Buffer.BlockCopy(src, 0, packet, 0, src.Length);
            }

            public OpenSSL.Blowfish Blowfish
            {
                get
                {
                    return this._blowfish;
                }
                set
                {
                    this._blowfish = value;
                }
            }
        }
    }
Commented out the packet type 1059 (password seed) in LoginServer.cs

Code:
public override bool OnClientConnect(ref IClientWrapper client)
        {
            try
            {
                client = new Player(client.Client);
                //TODO: Add ipban/connection limit/ddos protection 
                Player user = client as Player;
                //user.PasswordSeed = new Random().Next(100000, 90000000);
                //PasswordSeedPacket seed = new PasswordSeedPacket(user.PasswordSeed);
                //user.Send(&seed, seed.Size);
                // return false to deny
            }
            catch (Exception E) { Console.WriteLine(E); }
            return true;
        }
Modified packet type in LoginServer.cs

Code:
case 1051:

byte i = 0;
                        user.Username = Encoding.ASCII.GetString(buffer, 4, 16).Trim(new char[] { (char)0x0000 });
                        user.Password = "";
                        while (i < 16)
                        {
                            user.Password += buffer[i + 16].ToString("X2");
                            i = (byte)(i + 1);
                        }

                        var account = ServerDatabase.Context.Accounts.GetByName(user.Username);
I'm currently stumped, seems that the password encryption is working out to well.

Upon logging in I'm getting disconnected with wrong user/pass

"[Albetros] LoginServer: 'test' '0000000000000000000000000000' Connecting."

What am I missing?
03/06/2013 02:47 nTL3fTy#2
5065 password encryption is RC5.
03/06/2013 03:09 .Xori^#3
Quote:
Originally Posted by nTL3fTy View Post
5065 password encryption is RC5.
Okay changed it to RC5,

Code:
switch (BitConverter.ToUInt16(buffer, 2))
                {
                    //auth request
                    case 1060:
                    case 1086:
                    case 1051:
                        user.Username = Encoding.ASCII.GetString(buffer, 4, 16).Trim(new char[] { (char)0x0000 });
                        user.Password = "";
         
                        byte[] pw = new byte[16];
                        Buffer.BlockCopy(buffer, 20, pw, 0, 16);
                        RC5 Crypto = new RC5(new Byte[] { 0x3C, 0xDC, 0xFE, 0xE8, 0xC4, 0x54, 0xD6, 0x7E, 0x16, 0xA6, 0xF8, 0x1A, 0xE8, 0xD0, 0x38, 0xBE }); // mm wait, first data is key what';s the key? O_Ocheck it out in other server
                        pw = Crypto.Decrypt(pw);

                        user.Password = Encoding.ASCII.GetString(pw).Trim('\0');
                        user.Password = Md5_Hash.CalculateMD5(ref user.Password);

                        var account = ServerDatabase.Context.Accounts.GetByName(user.Username);

Now I'm getting, same disconnection but the the password display has changed.

"LoginServer: 'test', '3DCCF174AD9299F1D5E240814C2380B2' Connecting."

#Thank you nTL3fTy, converting the password encryption to rc5 did the trick. It's now reading the pass correctly.

Now my goal is to successfully get into the game.

Upon logging in my client crashed, thats due to packets being off right?
03/06/2013 05:07 Spirited#4
Quote:
Originally Posted by .Xori^ View Post
Okay changed it to RC5,

Code:
switch (BitConverter.ToUInt16(buffer, 2))
                {
                    //auth request
                    case 1060:
                    case 1086:
                    case 1051:
                        user.Username = Encoding.ASCII.GetString(buffer, 4, 16).Trim(new char[] { (char)0x0000 });
                        user.Password = "";
         
                        byte[] pw = new byte[16];
                        Buffer.BlockCopy(buffer, 20, pw, 0, 16);
                        RC5 Crypto = new RC5(new Byte[] { 0x3C, 0xDC, 0xFE, 0xE8, 0xC4, 0x54, 0xD6, 0x7E, 0x16, 0xA6, 0xF8, 0x1A, 0xE8, 0xD0, 0x38, 0xBE }); // mm wait, first data is key what';s the key? O_Ocheck it out in other server
                        pw = Crypto.Decrypt(pw);

                        user.Password = Encoding.ASCII.GetString(pw).Trim('\0');
                        user.Password = Md5_Hash.CalculateMD5(ref user.Password);

                        var account = ServerDatabase.Context.Accounts.GetByName(user.Username);

Now I'm getting, same disconnection but the the password display has changed.

"LoginServer: 'test', '3DCCF174AD9299F1D5E240814C2380B2' Connecting."

#Thank you nTL3fTy, converting the password encryption to rc5 did the trick. It's now reading the pass correctly.

Now my goal is to successfully get into the game.

Upon logging in my client crashed, thats due to packets being off right?
It might be crashing because your game server cipher is wrong. It crashes when the length of the packet in offset 0 + 8 is smaller than the size of the packet; so if your cipher is wrong, it'll try reading in a wrong number as the packet length. Breakpoint it and see if the first packet comes in alright and such.
03/06/2013 06:18 .Xori^#5
Quote:
Originally Posted by Fаng View Post
It might be crashing because your game server cipher is wrong. It crashes when the length of the packet in offset 0 + 8 is smaller than the size of the packet; so if your cipher is wrong, it'll try reading in a wrong number as the packet length. Breakpoint it and see if the first packet comes in alright and such.
I keep getting a console message of "Unknown Login packet type: 1052"..
03/06/2013 07:02 Spirited#6
Quote:
Originally Posted by .Xori^ View Post
I keep getting a console message of "Unknown Login packet type: 1052"..
If that's the account server, it doesn't matter at all. It's just an old packet that sends the version of conquer in binary (1.0 or 2.0). It does matter in the game server though.
03/06/2013 10:22 .Xori^#7
Take a look at what I got so far and tell me what could be going wrong.
And tell me if I have the right idea.

1. -> Server receives an authentication packet

[Modified LoginServer.cs]
Code:
public override void OnPacketReceive(IClientWrapper client, byte[] buffer)
        {
            try
            {
                if (buffer.Length < 4)
                    return;
                var Hero = client as Player;
                if (Hero == null) return;

                lock (Hero.Cryptographer)
                {
                    Hero.Cryptographer.Decrypt(buffer, buffer.Length);
                }
                    Console.WriteLine(BitConverter.ToUInt16(buffer, 2));
                    switch (BitConverter.ToUInt16(buffer, 2))
                    {
                        case 1051://auth request
                        case 1060:
                        case 1086:
                        Hero.Username = Encoding.ASCII.GetString(Kernel.ReadByteArray(buffer, 4, 16)).TrimEnd('\0');
                        Hero.Password = "";

                        byte[] pw = new byte[16];
                        Buffer.BlockCopy(buffer, 20, pw, 0, 16);
                        RC5 Crypto = new RC5(new Byte[] { 0x3C, 0xDC, 0xFE, 0xE8, 0xC4, 0x54, 0xD6, 0x7E, 0x16, 0xA6, 0xF8, 0x1A, 0xE8, 0xD0, 0x38, 0xBE }); // mm wait, first data is key what';s the key? O_Ocheck it out in other server
                        pw = Crypto.Decrypt(pw);

                        Hero.Password = Encoding.ASCII.GetString(pw).Trim('\0');
                        Console.WriteLine("user: " + Hero.Username + ", pass: " + Hero.Password);

                        MySqlHandler.MySqlCommand cmd = new MySqlHandler.MySqlCommand(MySqlHandler.MySqlCommandType.SELECT);
                        cmd.Select("accounts").Where("username", Hero.Username);
                        MySqlReader reader = new MySqlReader(cmd);
                        if (reader.Read())
                        {
                            Hero.UID = reader.ReadUInt32("UID");
                            Console.WriteLine(reader.ReadUInt32("UID"));
                         
                        }
                            
                        var account = ServerDatabase.Context.Accounts.GetByName(Hero.Username);

                        if (account != null)
                        {
                            if (account.Permission == PlayerPermission.Banned)
                            {
                                Console.WriteLine("Failed connection with credentials: " + account.Username + "," + account.Password + ", Reason: Banned.");
                                Hero.Send(AuthResponsePacket.AuthResponse(22));
                                Hero.Disconnect();
                                return;
                            }

                            else if (Game.Kernel.Clients.ContainsKey(account.UID))
                            {
                                Game.Kernel.Clients[account.UID].Disconnect(false);
                                Hero.Send(AuthResponsePacket.AuthResponse(22));
                                return;
                            }

                            else if (Hero.Password == account.Password && account.Permission > PlayerPermission.Error)
                            {
                                if (client.Client.RemoteEndPoint.ToString().Split(':')[0] != "127.0.0.1")
                                {
                                    Console.WriteLine(Hero.UID + Hero.Username + Hero.Password + " is attempting to connect to host IP: " + GameIP.ToString().Split(':')[0]);
                                    Console.WriteLine(GameIP + Hero.UID + "255" + "5816");
                                    Hero.Send(AuthResponsePacket.AuthResponse(GameIP, Hero.UID, 255, 5816));

                                }
                            }
                            else
                            {
                                Console.WriteLine("Failed connection with credentials: " + account.Username + "," + account.Password + ", Reason: Invalid Account.");
                                Hero.Send(AuthResponsePacket.AuthResponse(1));
                                Hero.Disconnect();
                                return;
                            }
                        }
                        
                        break;
                        default:
                        Console.WriteLine(Kernel.ProgramTitle + " Unknown Login packet type: " + BitConverter.ToUInt16(buffer, 2));
                        break;
                    }
                }
            
            catch { }
        }
2. -> Client requests an authentication response from the server with the information sent in "(user/pass/UID)"

[Modified AuthResponse.cs]
Code:
public unsafe struct AuthResponsePacket
    {
        public static byte[] AuthResponse(string ServerIP, uint Key1, uint Key2, ushort Port)
        {
            byte[] Packet = new byte[33];
            WriteUshort(32, Packet, 0);
            WriteUshort(1055, Packet, 2);
            WriteUInt(Key2, Packet, 4);
            WriteUInt(Key1, Packet, 8);
            WriteString(ServerIP, Packet, 12);
            WriteUshort(Port, Packet, 28);
            Console.WriteLine("Returning AuthResponse Packet: why is it failing at packet return?");
            return Packet;
        }
    }
3. -> Server handles login request packet?

[Modified PacketHandler.cs]
Code:
case 1052://login request
                            {
                                Console.WriteLine("Working");
                                user.UID = BitConverter.ToUInt32(packet, 8);
                                uint Key2 = BitConverter.ToUInt32(packet, 4);

                                if (Kernel.Clients.ContainsKey(user.UID)) { Kernel.Clients[user.UID].Disconnect(true); user.Client.Disconnect(false); return; }
                                if (user.UID == 0 || user.Permission == PlayerPermission.Banned)
                                {
                                    user.Disconnect(false);
                                    return;
                                }
                                break;
                            }
//Still Working on this, But I can't even get to this part..

Lol that is what I've got so far, I'm really trying hard guys. Need some more guidance, My clients crashing upon login. Did some breakpoints and it's crashing on the Packet return. Look at the code below.


Code:
Hero.Send(AuthResponsePacket.AuthResponse(GameIP, Hero.UID, 255, 5816));
//This is the function that triggers the coding below.

public static byte[] AuthResponse(string ServerIP, uint Key1, uint Key2, ushort Port)
        {
            byte[] Packet = new byte[33];
            WriteUshort(32, Packet, 0);
            WriteUshort(1055, Packet, 2);
            WriteUInt(Key2, Packet, 4);
            WriteUInt(Key1, Packet, 8);
            WriteString(ServerIP, Packet, 12);
            WriteUshort(Port, Packet, 28);
            Console.WriteLine("Returning AuthResponse Packet");
            return Packet;//It's crashing right here..
        }