Register for your free account! | Forgot your password?

Go Back   elitepvpers > MMORPGs > Nostale
You last visited: Today at 15:45

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



[C#] Login/World Cryptography

Discussion on [C#] Login/World Cryptography within the Nostale forum part of the MMORPGs category.

Reply
 
Old   #1
 
Roxeez's Avatar
 
elite*gold: 0
Join Date: Jun 2019
Posts: 102
Received Thanks: 228
[C#] Login/World Cryptography

Hello everyone, i'm here for sharing a simple translation C++ into C# of Cryless Nostale crypto class, i've just tested it with basic client who just connect/log the selected character in the select server/channel but everything looks ok.

Original c++ code :

C# is not my primary language so maybe some things can probably be done easier.

LoginEncryption.cs:
Code:
public sealed class LoginEncryption
{
    private static readonly Random Random = new Random(DateTime.Now.Millisecond);
    
    private readonly string _dxHash;
    private readonly string _glHash;
    private readonly string _version;

    /// <summary>
    ///     Create a new LoginEncryption instance
    /// </summary>
    /// <param name="dxHash">NostaleClientX.exe hash</param>
    /// <param name="glHash">NostaleClient hash</param>
    /// <param name="version">Version of the client</param>
    public LoginEncryption(string dxHash, string glHash, string version)
    {
        _dxHash = dxHash;
        _glHash = glHash;
        _version = version;
    }

    /// <summary>
    ///     Decrypt the raw packet (byte array) to a readable string
    /// </summary>
    /// <param name="bytes">Bytes to decrypt</param>
    /// <param name="size">Amount of byte to translate</param>
    /// <returns>Decrypted packet as string</returns>
    public string Decrypt(byte[] bytes, int size)
    {
        var output = "";
        for (var i = 0; i < size; i++)
        {
            output += Convert.ToChar(bytes[i] - 0xF);
        }
        return output;
    }

    /// <summary>
    ///     Encrypt the string packet to byte array
    /// </summary>
    /// <param name="value">String to encrypt</param>
    /// <returns>Encrypted packet as byte array</returns>
    public byte[] Encrypt(string value)
    {
        var output = new byte[value.Length + 1];
        for (var i = 0; i < value.Length; i++)
        {
            output[i] = (byte) ((value[i] ^ 0xC3) + 0xF);
        }
        output[output.Length - 1] = 0xD8;
        return output;
    }

    /// <summary>
    ///     Create the NoS0575 login packet
    /// </summary>
    /// <param name="username">Username of the account</param>
    /// <param name="password">Password of the account</param>
    /// <param name="gameforgeId">Gameforge ID</param>
    /// <returns>Packet as string created with the value</returns>
    public string CreateLoginPacket(string username, string password, string gameforgeId = "1234-1234-1234-1234")
    {
        string usernameHash = Cryptography.ToMd5(_dxHash.ToUpper() + _glHash.ToUpper() + username);
        string passwordHash = Cryptography.ToSha512(password);
        string generatedVersion = "00" + Random.Next(0, 126).ToString("X")
                                       + Random.Next(0, 126).ToString("X")
                                       + Random.Next(0, 126).ToString("X")
                                       + "\v" + _version;

        return $"NoS0575 7040266 {username} {passwordHash} {gameforgeId} {generatedVersion} 0 {usernameHash}";
    }

    public override string ToString()
    {
        return $"{nameof(_dxHash)}: {_dxHash}, {nameof(_glHash)}: {_glHash}, {nameof(_version)}: {_version}";
    }
}
WorldEncryption.cs:
Code:
public sealed class WorldEncryption
{
    private static readonly char[] Keys = {' ', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'n'};

    public int EncryptionKey { get; }

    /// <summary>
    /// Create a new WorldEncryption instance
    /// </summary>
    /// <param name="encryptionKey">Encryption key received by LoginServer</param>
    public WorldEncryption(int encryptionKey)
    {
        EncryptionKey = encryptionKey;
    }

    /// <summary>
    ///     Decrypt the raw packet (byte array) to a readable list string
    /// </summary>
    /// <param name="bytes">Bytes to decrypt</param>
    /// <param name="size">Amount of byte to read</param>
    /// <returns>Decrypted packet to string list</returns>
    public List<string> Decrypt(byte[] bytes, int size)
    {
        var output = new List<string>();

        var currentPacket = "";
        var index = 0;

        while (index < size)
        {
            byte currentByte = bytes[index];
            index++;

            if (currentByte == 0xFF)
            {
                output.Add(currentPacket);
                currentPacket = "";
                continue;
            }

            var length = (byte) (currentByte & 0x7F);

            if ((currentByte & 0x80) != 0)
            {
                while (length != 0)
                {
                    if (index <= size)
                    {
                        currentByte = bytes[index];
                        index++;

                        var firstIndex = (byte)(((currentByte & 0xF0u) >> 4) - 1);
                        var first = (byte)(firstIndex != 255 ? firstIndex != 14 ? Keys[firstIndex] : '\u0000' : '?');
                        if (first != 0x6E)
                            currentPacket += Convert.ToChar(first);

                        if (length <= 1)
                            break;
                    
                        var secondIndex = (byte)((currentByte & 0xF) - 1);
                        var second = (byte)(secondIndex != 255 ? secondIndex != 14 ? Keys[secondIndex] : '\u0000' : '?');
                        if (second != 0x6E)
                            currentPacket += Convert.ToChar(second);

                        length -= 2;
                    }
                    else
                    {
                        length--;
                    }
                }
            }
            else
            {
                while (length != 0)
                {
                    if (index < size)
                    {
                        currentPacket += Convert.ToChar(bytes[index] ^ 0xFF);
                        index++;
                    }
                    else if (index == size)
                    {
                        currentPacket += Convert.ToChar(0xFF);
                        index++;
                    }

                    length--;
                }
            }
        }
        return output;
    }

    /// <summary>
    ///     Encrypt the string packet to byte array
    /// </summary>
    /// <param name="value">String to encrypt</param>
    /// <param name="session">Define if it's a session packet or not</param>
    /// <returns>Encrypted packet as byte array</returns>
    public byte[] Encrypt(string value, bool session = false)
    {
        var output = new List<byte>();

        var mask = new string(value.Select(c =>
        {
            var b = (sbyte) c;
            if (c == '#' || c == '/' || c == '%')
                return '0';
            if ((b -= 0x20) == 0 || (b += unchecked((sbyte) 0xF1)) < 0 || (b -= 0xB) < 0 ||
                b - unchecked((sbyte) 0xC5) == 0)
                return '1';
            return '0';
        }).ToArray());

        int packetLength = value.Length;

        var sequenceCounter = 0;
        var currentPosition = 0;

        while (currentPosition <= packetLength)
        {
            int lastPosition = currentPosition;
            while (currentPosition < packetLength && mask[currentPosition] == '0')
                currentPosition++;

            int sequences;
            int length;

            if (currentPosition != 0)
            {
                length = currentPosition - lastPosition;
                sequences = length / 0x7E;
                for (var i = 0; i < length; i++, lastPosition++)
                {
                    if (i == sequenceCounter * 0x7E)
                    {
                        if (sequences == 0)
                        {
                            output.Add((byte) (length - i));
                        }
                        else
                        {
                            output.Add(0x7E);
                            sequences--;
                            sequenceCounter++;
                        }
                    }

                    output.Add((byte) ((byte) value[lastPosition] ^ 0xFF));
                }
            }

            if (currentPosition >= packetLength)
                break;

            lastPosition = currentPosition;
            while (currentPosition < packetLength && mask[currentPosition] == '1')
                currentPosition++;

            if (currentPosition == 0) continue;

            length = currentPosition - lastPosition;
            sequences = length / 0x7E;
            for (var i = 0; i < length; i++, lastPosition++)
            {
                if (i == sequenceCounter * 0x7E)
                {
                    if (sequences == 0)
                    {
                        output.Add((byte) ((length - i) | 0x80));
                    }
                    else
                    {
                        output.Add(0x7E | 0x80);
                        sequences--;
                        sequenceCounter++;
                    }
                }

                var currentByte = (byte) value[lastPosition];
                switch (currentByte)
                {
                    case 0x20:
                        currentByte = 1;
                        break;
                    case 0x2D:
                        currentByte = 2;
                        break;
                    case 0xFF:
                        currentByte = 0xE;
                        break;
                    default:
                        currentByte -= 0x2C;
                        break;
                }

                if (currentByte == 0x00) continue;

                if (i % 2 == 0)
                    output.Add((byte) (currentByte << 4));
                else
                    output[output.Count - 1] = (byte) (output.Last() | currentByte);
            }
        }

        output.Add(0xFF);
        
        var sessionNumber = (sbyte) ((EncryptionKey >> 6) & 0xFF & 0x80000003);

        if (sessionNumber < 0)
            sessionNumber = (sbyte) (((sessionNumber - 1) | 0xFFFFFFFC) + 1);

        var sessionKey = (byte) (EncryptionKey & 0xFF);

        if (session)
            sessionNumber = -1;

        switch (sessionNumber)
        {
            case 0:
                for (var i = 0; i < output.Count; i++)
                    output[i] = (byte) (output[i] + sessionKey + 0x40);
                break;
            case 1:
                for (var i = 0; i < output.Count; i++)
                    output[i] = (byte) (output[i] - (sessionKey + 0x40));
                break;
            case 2:
                for (var i = 0; i < output.Count; i++)
                    output[i] = (byte) ((output[i] ^ 0xC3) + sessionKey + 0x40);
                break;
            case 3:
                for (var i = 0; i < output.Count; i++)
                    output[i] = (byte) ((output[i] ^ 0xC3) - (sessionKey + 0x40));
                break;
            default:
                for (var i = 0; i < output.Count; i++)
                    output[i] = (byte) (output[i] + 0x0F);
                break;
        }
        
        return output.ToArray();
    }
   
}
Cryptography.cs
Code:
public static class Cryptography
{
    public static string ToMd5(string value)
    {
        using (var md5 = MD5.Create())
        {
            return Hash(value, md5);
        }
    }

    public static string ToSha512(string value)
    {
        using (var sha512 = SHA512.Create())
        {
            return Hash(value, sha512);
        }
    }
    
    private static string Hash(string value, HashAlgorithm hash)
    {
        var bytes = hash.ComputeHash(Encoding.ASCII.GetBytes(value));
        var sb = new StringBuilder();
        
        foreach (byte b in bytes)
        {
            sb.Append(b.ToString("X2"));
        }
        return sb.ToString();
    }
}
Roxeez is offline  
Thanks
5 Users
Old 06/07/2019, 17:59   #2
 
elite*gold: 0
Join Date: Apr 2018
Posts: 85
Received Thanks: 78
I looking for it yester day and here it is <3
Rajmund1337 is offline  
Old 06/07/2019, 19:07   #3

 
Blowa's Avatar
 
elite*gold: 148
Join Date: Jan 2010
Posts: 654
Received Thanks: 1,774
Feel free to open a pull request in case you'd like to contribute to an opensource project that will have a package released on nuget.

Blowa is offline  
Old 06/08/2019, 16:13   #4
 
elite*gold: 0
Join Date: Apr 2018
Posts: 85
Received Thanks: 78
I dont know why but after connect to world serwer and send
Code:
Encrypt(sessionid, true)
Encrypt(login, false)
Encrypt(pass, false)
Connection lost
Rajmund1337 is offline  
Old 06/08/2019, 17:29   #5
 
Roxeez's Avatar
 
elite*gold: 0
Join Date: Jun 2019
Posts: 102
Received Thanks: 228
Quote:
Originally Posted by Rajmund1337 View Post
I dont know why but after connect to world serwer and send
Code:
Encrypt(sessionid, true)
Encrypt(login, false)
Encrypt(pass, false)
Connection lost
Parse the base packet using C#, do the same with c++ crypto check if the output are different there is a problem in my crypto, if not it's not the crypto it's your socket
Roxeez is offline  
Old 06/08/2019, 17:37   #6

 
elite*gold: 64
Join Date: May 2011
Posts: 1,229
Received Thanks: 854
Quote:
Originally Posted by Rajmund1337 View Post
I dont know why but after connect to world serwer and send
Code:
Encrypt(sessionid, true)
Encrypt(login, false)
Encrypt(pass, false)
Connection lost
Official, P-Server, Vendetta?
BladeTiger12 is offline  
Thanks
1 User
Old 06/08/2019, 19:12   #7
 
elite*gold: 0
Join Date: Apr 2018
Posts: 85
Received Thanks: 78
P server
Rajmund1337 is offline  
Old 06/08/2019, 20:38   #8

 
elite*gold: 64
Join Date: May 2011
Posts: 1,229
Received Thanks: 854
Did u login before to the Login Server?
BladeTiger12 is offline  
Thanks
1 User
Old 06/08/2019, 20:52   #9
 
elite*gold: 0
Join Date: Apr 2018
Posts: 85
Received Thanks: 78
Yes, after login to Login Server i geting ip and port in to world serwer. After connect to it i sending packets sessionid, login, pass and when i waiting to recive connection is closing. Can you update your discord in profile description? I will send you invite

I creating conection like that:
Code:
clientSocket = new System.Net.Sockets.TcpClient();
clientSocket.Connect(ip, port);
serverStream = clientSocket.GetStream();

//send (sendpacket)
serverStream.Write(sendpacket, 0, sendpacket.Length);
serverStream.Flush();

//recv (recvpacket)
byte[] recvpacket= new byte[(int)clientSocket.ReceiveBufferSize];
serverStream.Read(recvpacket, 0, (int)clientSocket.ReceiveBufferSize);
Rajmund1337 is offline  
Old 06/08/2019, 21:00   #10

 
elite*gold: 64
Join Date: May 2011
Posts: 1,229
Received Thanks: 854
Do you also add the "identifier".
The number before every WorldServer Packet?
Generate a Random Number and increase it after each packet, so:

e.g. u generated: 53535

Send this packets:
53535 23232 (random number + sessionId)
53536 Username
53537 Password

and so on with every other packet that u're sending.
BladeTiger12 is offline  
Thanks
1 User
Old 06/09/2019, 20:59   #11
 
Roxeez's Avatar
 
elite*gold: 0
Join Date: Jun 2019
Posts: 102
Received Thanks: 228
Updated WorldEncryption.cs for fixing a bug i've encountered when using it
Roxeez is offline  
Old 03/15/2020, 22:34   #12
 
elite*gold: 0
Join Date: Apr 2018
Posts: 9
Received Thanks: 5
Quote:
Originally Posted by Roxeez View Post
Updated WorldEncryption.cs for fixing a bug i've encountered when using it
Decrypt:

Code:
if (index <= size)
                    {
                        currentByte = bytes[index];
System.IndexOutOfRangeException

I replaced "index <= size" with "index < size", but sometimes packets are decryptet partialy. E.g.
Quote:
êuæ ûdrop^ê·^d¦µž¢êdâë驹5 ûdrop^꺞d¦µŽ¢êdêë驹5 ýmvnèé¦n§êkæ ýmvoè馾©Nna ýmvoèé§N©n.a mv 3 2145 122 10 5
This should be the drop packet
Anmael is offline  
Old 07/18/2020, 17:30   #13
 
elite*gold: 0
Join Date: Mar 2020
Posts: 1
Received Thanks: 0
IndexOutOfRangeException bugfix

I got System.IndexOutOfRangeException in WorldEncryption.Decrypt

here is working version (I didn't check where the error was, I have rewritten python code for nostale cryptography) I think that it may be simplified:
Code:
/// <summary>
///     Decrypt the raw packet (byte array) to a readable list string
/// </summary>
/// <param name="bytes">Bytes to decrypt</param>
/// <param name="size">Amount of byte to read</param>
/// <returns>Decrypted packet to string list</returns>
public List<string> Decrypt(byte[] bytes, int size)
{
    int index = 0;
    var output = new List<string>();
    string currentPacket = "";
    while (index < size)
    {
        byte currentByte = bytes[index++];
        if (currentByte == 0xFF)
        {
            output.Add(currentPacket);
            currentPacket = "";
            continue;
        }
        int length = currentByte & 0x7F;
        if ((currentByte & 0x80) != 0)
        {
            while (length != 0)
            {
                if (index < size)
                {
                    currentByte = bytes[index++];
                    int firstIndex = (currentByte & 0xF0) >> 4;
                    char first = '?';
                    if (firstIndex != 0)
                    {
                        firstIndex--;
                        first = firstIndex != 14 ? Keys[firstIndex] : '\u0000';
                    }
                    if (first != 0x6E)
                    {
                        currentPacket += first;
                    }
                    if (length <= 1)
                    {
                        break;
                    }
                    int secondIndex = ((currentByte & 0xF));
                    char second = '?';
                    if (secondIndex != 0)
                    {
                        secondIndex--;
                        second = secondIndex != 14 ? Keys[secondIndex] : '\u0000';
                    }
                    if (second != 0x6E)
                    {
                        currentPacket += second;
                    }
                    length -= 2;
                }
                else
                {
                    length--;
                }
            }
        }
        else
        {
            while (length != 0)
            {
                if (index < size)
                {
                    currentPacket += (char)(bytes[index] ^ 0xFF);
                    index++;
                }
                else if (index == size)
                {
                    currentPacket += (char)0xFF;
                    index++;
                }
                length--;
            }
        }
    }
    return output;
}
rutherthera is offline  
Old 07/24/2020, 15:04   #14
 
elite*gold: 0
Join Date: Apr 2018
Posts: 85
Received Thanks: 78
Quote:
Originally Posted by rutherthera View Post
I got System.IndexOutOfRangeException in WorldEncryption.Decrypt

here is working version (I didn't check where the error was, I have rewritten python code for nostale cryptography) I think that it may be simplified:
Code:
/// <summary>
///     Decrypt the raw packet (byte array) to a readable list string
/// </summary>
/// <param name="bytes">Bytes to decrypt</param>
/// <param name="size">Amount of byte to read</param>
/// <returns>Decrypted packet to string list</returns>
public List<string> Decrypt(byte[] bytes, int size)
{
    int index = 0;
    var output = new List<string>();
    string currentPacket = "";
    while (index < size)
    {
        byte currentByte = bytes[index++];
        if (currentByte == 0xFF)
        {
            output.Add(currentPacket);
            currentPacket = "";
            continue;
        }
        int length = currentByte & 0x7F;
        if ((currentByte & 0x80) != 0)
        {
            while (length != 0)
            {
                if (index < size)
                {
                    currentByte = bytes[index++];
                    int firstIndex = (currentByte & 0xF0) >> 4;
                    char first = '?';
                    if (firstIndex != 0)
                    {
                        firstIndex--;
                        first = firstIndex != 14 ? Keys[firstIndex] : '\u0000';
                    }
                    if (first != 0x6E)
                    {
                        currentPacket += first;
                    }
                    if (length <= 1)
                    {
                        break;
                    }
                    int secondIndex = ((currentByte & 0xF));
                    char second = '?';
                    if (secondIndex != 0)
                    {
                        secondIndex--;
                        second = secondIndex != 14 ? Keys[secondIndex] : '\u0000';
                    }
                    if (second != 0x6E)
                    {
                        currentPacket += second;
                    }
                    length -= 2;
                }
                else
                {
                    length--;
                }
            }
        }
        else
        {
            while (length != 0)
            {
                if (index < size)
                {
                    currentPacket += (char)(bytes[index] ^ 0xFF);
                    index++;
                }
                else if (index == size)
                {
                    currentPacket += (char)0xFF;
                    index++;
                }
                length--;
            }
        }
    }
    return output;
}
Can you share your python code? I was looking for it for loong time
Rajmund1337 is offline  
Old 07/25/2020, 23:07   #15
 
romdrak's Avatar
 
elite*gold: 0
Join Date: Oct 2013
Posts: 101
Received Thanks: 156
Quote:
Originally Posted by Rajmund1337 View Post
Can you share your python code? I was looking for it for loong time
I posted python version 2 weeks ago.
romdrak is offline  
Thanks
1 User
Reply


Similar Threads Similar Threads
[PHP - CLIENT] Login Cryptography
11/02/2013 - Nostale - 30 Replies
Hi community ! :pimp: today i open this thread for release the login cryptography written in php.. // encrypt 'NoS0575..' packet function packet_enc($packet) { $str_enc = ""; for($i = 0; $i < strlen($packet); $i++) $str_enc .= chr((ord($packet)^195) + 15);
Client Server Cryptography?
07/14/2009 - Archlord - 7 Replies
i was wondering, the communication between alefcient and the game server is encrypted, and alefclient binary seems to use Cryptography API (CNG) with most of its functions like: BCryptCloseAlgorithmProvider BCryptCreateHash BCryptDecrypt BCryptDeriveKey BCryptDestroyHash BCryptDestroyKey BCryptDestroySecret BCryptDuplicateHash



All times are GMT +1. The time now is 15:46.


Powered by vBulletin®
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2026 elitepvpers All Rights Reserved.