elitepvpers

elitepvpers (https://www.elitepvpers.com/forum/)
-   Nostale (https://www.elitepvpers.com/forum/nostale/)
-   -   [EU, Client] Cryptography (https://www.elitepvpers.com/forum/nostale/3935929-eu-client-cryptography.html)

Cryless~ 12/04/2015 17:19

[EU, Client] Cryptography
 
Code:

----------------------
.h
-----------------------

#pragma once
#ifndef __CRYPTO_H
#define __CRYPTO_H
#include <string>
#include <vector>

class Crypto
{
public:
        std::vector<unsigned char> encryptGamePacket(const std::string& buf, int session, bool is_session_packet = 0) const;
        std::vector<unsigned char> encryptLoginPacket(const std::string& buf) const;
        std::string decryptLoginPacket(const std::vector<unsigned char>& buf, std::size_t len) const;
        std::vector<std::string> decryptGamePacket(const std::vector<unsigned char>& buf, std::size_t len) const;
        std::string encryptPasswordString(const std::string& password) const;
        std::string createLoginHash(const std::string& user) const;
        std::string createLoginVersion(void) const;
        int randomNumber(int min, int max) const;
private:
        void completeGamePacketEncrypt(std::vector<unsigned char>& buf, int session, bool is_session_packet = 0) const;
};

namespace CryptoVar
{
        extern std::string dxhash;
        extern std::string glhash;
        extern std::string version;
}

#endif

----------------------
.cpp
-----------------------

#include "Crypto.h"

#include <algorithm>
#include <sstream>
#include <random>

#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include <md5.h>
#include <hex.h>
#pragma comment(lib, "cryptlib.lib")


namespace CryptoVar
{
        std::string dxhash;
        std::string glhash;
        std::string version;
}

std::string Crypto::decryptLoginPacket(const std::vector<unsigned char>& buf, std::size_t len) const
{
        std::string output;
        std::transform(buf.begin(), buf.begin() + len, std::back_inserter(output),
                [](unsigned char c)
                {
                        return c - 0xF;
                }
        );
        return output;
}

std::vector<unsigned char> Crypto::encryptLoginPacket(const std::string& buf) const
{
        std::vector<unsigned char> output;
        std::transform(buf.begin(), buf.end(), std::back_inserter(output),
                [](char c)
                {
                        return (c ^ 0xC3) + 0xF;
                }
        );
        return output;
}

std::string Crypto::encryptPasswordString(const std::string& password) const
{
        static unsigned char table[] = {0x2E, 0x2A, 0x17, 0x4F, 0x20, 0x24, 0x47, 0x11, 0x5B, 0x37,
                0x53, 0x43, 0x15, 0x34, 0x45, 0x25, 0x4B, 0x1D, 0x2F, 0x58, 0x2B, 0x32, 0x63};

        std::stringstream output;
        std::size_t random_start_pos = randomNumber(0, 22);
        std::string password_hex_string;
        CryptoPP::StringSource(password,
                true, new CryptoPP::HexEncoder(new CryptoPP::StringSink(password_hex_string)));

        output << std::uppercase << std::hex << int(table[random_start_pos]);
       
        unsigned char first = 0, second = 0;
        for(std::size_t i = 0; i < password_hex_string.length(); ++i, ++i, ++random_start_pos)
        {
                second = table[random_start_pos] & 0xF;
                first = (table[random_start_pos] & 0xF0) >> 4;

                output << std::uppercase << std::hex << int(first);
                output << std::uppercase << password_hex_string.at(i);
                output << std::uppercase << std::hex << int(second);
                output << std::uppercase << password_hex_string.at(i+1);
                if(random_start_pos == 22)
                        random_start_pos = -1;
        }

        return output.str();
}

std::string Crypto::createLoginHash(const std::string& user) const
{
        CryptoPP::Weak1::MD5 hash;
        std::string output;
        std::string login_string_to_hash = CryptoVar::dxhash;
        login_string_to_hash += CryptoVar::glhash;
        login_string_to_hash += user;

        CryptoPP::StringSource(login_string_to_hash,
                true, new CryptoPP::HashFilter(hash, new CryptoPP::HexEncoder(new CryptoPP::StringSink(output))));
        return output;
}

std::string Crypto::createLoginVersion(void) const
{
        std::string test2 = CryptoVar::dxhash;
        std::string test = CryptoVar::version;
        std::stringstream output;
        output << "00" << std::uppercase << std::hex << int(randomNumber(0, 126))
                << std::uppercase << std::hex << int(randomNumber(0, 126))
                << std::uppercase << std::hex << int(randomNumber(0, 126))
                << '\v' << CryptoVar::version;
        return output.str();
}

int Crypto::randomNumber(int min, int max) const
{
        std::random_device seeder;
        std::mt19937 engine(seeder());
        std::uniform_int_distribution<int> generator(min, max);
        return generator(engine);
}

std::vector<std::string> Crypto::decryptGamePacket(const std::vector<unsigned char>& buf, std::size_t len) const
{
        std::vector<std::string> output;
        std::string current_packet;
        static const char keys[] = {' ','-','.','0','1','2','3','4','5','6','7','8','9','n'};
        std::size_t index = 0;
        unsigned char currentByte = 0, length = 0, first = 0, second = 0;

        while(index < len)
        {
                currentByte = buf[index];
                ++index;
                if(currentByte == 0xFF)
                {
                        output.push_back(current_packet);
                        current_packet.clear();
                        continue;
                }

                length = currentByte & 0x7F;
                if(currentByte & 0x80)
                {
                        while(length)
                        {
                                if(index <= len)
                                {
                                        currentByte = buf[index];
                                        ++index;

                                        first = keys[((currentByte & 0xF0u) >> 4) -1];
                                        if(first != 0x6E)
                                                current_packet += first;
                                       
                                        if(length <= 1)
                                                break;

                                        second = keys[(currentByte & 0xF) -1];
                                        if(second != 0x6E)
                                                current_packet += second;
                                       
                                        length -= 2;
                                } else
                                {
                                        --length;
                                }
                        }
                } else
                {
                        while(length)
                        {
                                if(index <= len)
                                {
                                        current_packet += buf[index] ^ 0xFF;
                                        ++index;
                                }

                                --length;
                        }
                }
        }
       
        return output;
}

std::vector<unsigned char> Crypto::encryptGamePacket(const std::string& buf, int session, bool is_session_packet) const
{
        std::size_t packet_length = buf.size();
        std::string packet_mask;
        std::transform(buf.begin(), buf.end(), std::back_inserter(packet_mask),
                [](char c)
        {
                if(c == '#')
                        return '0';

                if(!(c -= 0x20) || (c += 0xF1) < 0 || (c -= 0xB) < 0 || !(c -= 0xC5))
                        return '1';
                return '0';
        }
        );

        std::vector<unsigned char> output;
        std::size_t sequences = 0, sequence_counter = 0;
        std::size_t last_position = 0, current_position = 0, length = 0;
        unsigned char current_byte = 0;
        while(current_position <= packet_length)
        {
                last_position = current_position;
                while(current_position < packet_length && packet_mask[current_position] == '0')
                        ++current_position;

                if(current_position)
                {
                        length = (current_position - last_position);
                        sequences = (length / 0x7E);
                        for(std::size_t i = 0; i < length; ++i, ++last_position)
                        {
                                if(i == (sequence_counter * 0x7E))
                                {
                                        if(!sequences)
                                        {
                                                output.push_back(length - i);
                                        } else
                                        {
                                                output.push_back(0x7E);
                                                --sequences;
                                                ++sequence_counter;
                                        }
                                }

                                output.push_back(buf[last_position] ^ 0xFF);
                        }
                }

                if(current_position >= packet_length)
                        break;

                last_position = current_position;
                while(current_position < packet_length && packet_mask[current_position] == '1')
                        ++current_position;

                if(current_position)
                {
                        length = (current_position - last_position);
                        sequences = (length / 0x7E);
                        for(std::size_t i = 0; i < length; ++i, ++last_position)
                        {
                                if(i == (sequence_counter * 0x7E))
                                {
                                        if(!sequences)
                                        {
                                                output.push_back((length - i) | 0x80);
                                        } else
                                        {
                                                output.push_back(0x7E | 0x80);
                                                --sequences;
                                                ++sequence_counter;
                                        }
                                }

                                current_byte = buf[last_position];
                                switch(current_byte)
                                {
                                case 0x20:
                                        current_byte = 1;
                                        break;
                                case 0x2D:
                                        current_byte = 2;
                                        break;
                                case 0x2E:
                                        current_byte = 3;
                                        break;
                                case 0xFF:
                                        current_byte = 0xE;
                                        break;
                                default:
                                        current_byte -= 0x2C;
                                        break;
                                }

                                if(current_byte != 0x00)
                                {
                                        if(i % 2 == 0)
                                        {
                                                output.push_back(current_byte << 4);
                                        } else
                                        {
                                                output.back() |= current_byte;
                                        }
                                }
                        }
                }
        }

        output.push_back(0xFF);
        completeGamePacketEncrypt(output, session, is_session_packet);
        return output;
}

void Crypto::completeGamePacketEncrypt(std::vector<unsigned char>& buf, int session, bool is_session_packet) const
{
        unsigned char session_number = (((session >> 6) & 0xFF) & 0x80000003);
       
        if(session_number < 0)
                session_number = (((session_number - 1) | 0xFFFFFFFC) + 1);

        unsigned char session_key = (session & 0xFF);

        if(is_session_packet)
                session_number = -1;

        switch (session_number)
        {
        case 0:
                for(std::size_t i = 0; i < buf.size(); ++i)
                        buf[i] = (buf[i] + (session_key + 0x40));
                break;

        case 1:
                for(std::size_t i = 0; i < buf.size(); ++i)
                        buf[i] = (buf[i] - (session_key + 0x40));
                break;

        case 2:
                for(std::size_t i = 0; i < buf.size(); ++i)
                        buf[i] = ((buf[i] ^ 0xC3) + (session_key + 0x40));
                break;

        case 3:
                for(std::size_t i = 0; i < buf.size(); ++i)
                        buf[i] = ((buf[i] ^ 0xC3) - (session_key + 0x40));
                break;

        default:
                for(std::size_t i = 0; i < buf.size(); ++i)
                        buf[i] = buf[i] + 0x0F;
                break;
        }
}


0Lucifer0 12/05/2015 13:51

Good job!

abssy 12/05/2015 21:26

Thanks a lot, I was just searching for this a week ago and you just post it. Amazing job.

PainToTheWorld 12/22/2015 21:06

I've tested all encryptions and it works well . but when I send my session and id / pw to the game server , I get no response . Is there a solution to this problem ?

BladeTiger12 12/22/2015 21:20

Then is your packet build wrong. Can you send code snippet where you send that packet?

Trollcrap- 12/22/2015 23:21

Quote:

Originally Posted by PainToTheWorld (Post 33597245)
I've tested all encryptions and it works well . but when I send my session and id / pw to the game server , I get no response . Is there a solution to this problem ?

Well, it also happens when you send too fast the packets to world server.

PainToTheWorld 12/23/2015 18:17

did it with different delays .. unfortunately it does not work :/

Spoiler:
pChannel = new TConnection();
pChannel->Connect(ServerList.Channels[iIndex].sIP, ServerList.Channels[iIndex].iPort);
pChannel->StartRecv();
pChannel->iSession = ServerList.iSession;

pChannel->SendSession();
Sleep(200);
pChannel->SendGamePacket("ID");
Sleep(200);
pChannel->SendGamePacket("PW");

Spoiler:
int TConnection::SendGamePacket(std::string sPacket)
{
int iResult;

std::vector<unsigned char> sEncrypted = EncryptGamePacket(FormatString("%d %s", iPacketCount, sPacket.c_str()), iSession, false);

iResult = send(soConnect, &sEncrypted.front(), sEncrypted.size(), 0);

if (iResult == SOCKET_ERROR)
{
__Log("send failed with error: %d\n", WSAGetLastError());
closesocket(soConnect);
WSACleanup();
return 1;
}

iPacketCount++;

return iResult;
}

btw is the packet identifier random?

BladeTiger12 12/23/2015 19:21

Yes it's random. (Just generate it 1 time and add for new packet +1)
You have to send the SessionID...(Wait 1sec~)...IP|PW same packet.
Like:

Code:

// You need it like this:

std::vector<byte> bytes;

bytes.push_back(EncryptGame("IDENTIFIER ID")); //EncryptGame("25555 Test")
bytes.push_back(0xFF);
bytes.push_back(EncryptGame("IDENTIFIER PW")); //EncryptGame("25556 Rofl")

send(bytes);


Trollcrap- 12/24/2015 23:14

Quote:

Originally Posted by BladeTiger12 (Post 33602933)
Yes it's random. (Just generate it 1 time and add for new packet +1)
You have to send the SessionID...(Wait 1sec~)...IP|PW same packet.
Like:

Code:

// You need it like this:

std::vector<byte> bytes;

bytes.push_back(EncryptGame("IDENTIFIER ID")); //EncryptGame("25555 Test")
bytes.push_back(0xFF);
bytes.push_back(EncryptGame("IDENTIFIER PW")); //EncryptGame("25556 Rofl")

send(bytes);


There is not a rule about ID|PW, server is async and splits by delimiter.. 1s is right pause ~

lika85 08/09/2016 20:24

While compiling it returns error

"32 2 H:\DevC++\Dev-Cpp\MinGW64\lib\gcc\x86_64-w64-mingw32\4.9.2\include\c++\bits\c++0x_warning.h [Error] #error This file requires compiler and library support for the ISO C++ 2011 standard. This support is currently experimental, and must be enabled with the -std=c++11 or -std=gnu++11 compiler options.

md5.h No such file or directory"

for hex.h too. Missing md5.h and hex.h, where can i find it?

ArtTorn 03/03/2017 15:01

This is still correct way?

FI0w 03/03/2017 15:04

Quote:

Originally Posted by ArtTorn (Post 35791233)
This is still correct way?

Just PW is wrong you dont need to Encrypt/Decrypt it anymore you can just use SHA512 Uppercase :P

Cryless~ 03/08/2017 14:33

Quote:

Originally Posted by xSensitivex (Post 35791241)
Just PW is wrong you dont need to Encrypt/Decrypt it anymore you can just use SHA512 Uppercase :P

openssl

Code:

std::string NTCryptoHelper::ToHex(unsigned char *digest, std::size_t length)
{
        static char table[] = {
                '0', '1', '2', '3', '4', '5', '6', '7',
                '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
        };

        std::string result;
        for (std::size_t i = 0; i < length; i++) {
                result += table[digest[i] / 16];
                result += table[digest[i] % 16];
        }

        return result;
}

Code:

std::string NTCryptoHelper::Sha512(const std::string& plainText)
{
        unsigned char digest[SHA512_DIGEST_LENGTH];
        SHA512((unsigned char*)plainText.c_str(), plainText.length(), digest);
        return ToHex(digest, sizeof(digest));
}


lika85 08/24/2017 12:51

Code:

        public static ArrayList<String> DecryptGamePacketTest(ArrayList<Integer> buf) {
                int len = buf.size();
                ArrayList<String> output = new ArrayList<String>();
                ArrayList<Integer> current_packet = new ArrayList<Integer>();
                char keys[] = { ' ', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'n' };
                int index = 0;
                int currentByte = 0, length = 0, first = 0, second = 0;

                while (index < len) {
                        currentByte = buf.get(index);
                        ++index;
                        if (currentByte == 0xFF) {
                                output.add(ArrayListToString(current_packet));
                                //System.out.println("RECEIVED: "+ArrayListToString(current_packet));
                                current_packet = new ArrayList<Integer>();
                                continue;
                        }

                        length = (currentByte & 0x7F);
                        if (((currentByte & 0x80) & 0xFF) != 0 && currentByte!=0) {
                                while (length != 0) {
                                        if (index < len) {
                                                currentByte = buf.get(index);
                                                ++index;
                                                try{
                                                first = keys[(((currentByte & 0xF0) ) >> 4) - 1];
                                                if (first != 0x6E)
                                                        current_packet.add(first);

                                                if (length <= 1)
                                                        break;
                       
                                                second = keys[(currentByte & 0xF) - 1];
                                                if (second != 0x6E)
                                                        current_packet.add(second);

                                                length -= 2;
                                                }
                                                catch(Exception e){System.out.println("Exception packet: "+ArrayListToString(current_packet));}
                                        } else {
                                                --length;
                                        }
                                }
                        } else {
                                while (length != 0) {
                                        if (index < len) {
                                                //current_packet.add(buf.get(index) ^ 0xFF);¨
                                                current_packet.add(buf.get(index) ^ 0xFF);
                                                ++index;
                                        }

                                        --length;
                                }
                        }
                }

                return output;
        }

This is DecryptGamePacket function i currently use in Java and i have problems with chars with diacritics (ěščřžýáíé). Probably becouse java uses 2 byte chars and in this function it is only 1 byte. But in client diacritics and other shit chars are working. Is it working for you? How this can be fixed? :handsdown:

-Nokis 10/22/2017 20:21

Quote:

Originally Posted by lika85 (Post 36276801)
Code:

        public static ArrayList<String> DecryptGamePacketTest(ArrayList<Integer> buf) {
                int len = buf.size();
                ArrayList<String> output = new ArrayList<String>();
                ArrayList<Integer> current_packet = new ArrayList<Integer>();
                char keys[] = { ' ', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'n' };
                int index = 0;
                int currentByte = 0, length = 0, first = 0, second = 0;

                while (index < len) {
                        currentByte = buf.get(index);
                        ++index;
                        if (currentByte == 0xFF) {
                                output.add(ArrayListToString(current_packet));
                                //System.out.println("RECEIVED: "+ArrayListToString(current_packet));
                                current_packet = new ArrayList<Integer>();
                                continue;
                        }

                        length = (currentByte & 0x7F);
                        if (((currentByte & 0x80) & 0xFF) != 0 && currentByte!=0) {
                                while (length != 0) {
                                        if (index < len) {
                                                currentByte = buf.get(index);
                                                ++index;
                                                try{
                                                first = keys[(((currentByte & 0xF0) ) >> 4) - 1];
                                                if (first != 0x6E)
                                                        current_packet.add(first);

                                                if (length <= 1)
                                                        break;
                       
                                                second = keys[(currentByte & 0xF) - 1];
                                                if (second != 0x6E)
                                                        current_packet.add(second);

                                                length -= 2;
                                                }
                                                catch(Exception e){System.out.println("Exception packet: "+ArrayListToString(current_packet));}
                                        } else {
                                                --length;
                                        }
                                }
                        } else {
                                while (length != 0) {
                                        if (index < len) {
                                                //current_packet.add(buf.get(index) ^ 0xFF);¨
                                                current_packet.add(buf.get(index) ^ 0xFF);
                                                ++index;
                                        }

                                        --length;
                                }
                        }
                }

                return output;
        }

This is DecryptGamePacket function i currently use in Java and i have problems with chars with diacritics (ěščřžýáíé). Probably becouse java uses 2 byte chars and in this function it is only 1 byte. But in client diacritics and other shit chars are working. Is it working for you? How this can be fixed? :handsdown:

The NosTale Client uses the Windows-1252 encoding. You have to encode ‘current_packet’ in win1252.

BlowaXD 10/23/2017 12:32

Quote:

Originally Posted by -Nokis (Post 36418780)
The NosTale Client uses the Windows-1252 encoding. You have to encode ‘current_packet’ in win1252.

It's not a need.

WalrossGreat 10/23/2017 17:20

It is if you want properly encode/decode special characters like łśćń etc, however it's not always the win1252, it depends on your local 8 bit, eg it may be win1250

Bejine 10/23/2017 21:23

it depends on the client, some countries use 1250 (poland), others use 1252 (germany, i think)

lika85 11/05/2017 22:44

Ty guys :handsdown:, now getting packets works, but sending packet is buggy, what should i do to send right encoded packet? Better question is, what is the server encoding? :)

Cryless~ 11/22/2017 11:19

Quote:

Originally Posted by lika85 (Post 36453107)
Ty guys :handsdown:, now getting packets works, but sending packet is buggy, what should i do to send right encoded packet? Better question is, what is the server encoding? :)

Had you found an answer to your question? Let us know about your experience it might help someone else.

lika85 11/22/2017 21:43

Well i fount, that the right encoding for czech server is Latin 1, probably for other servers too, i am encoding the output of decryptGamePacket function, but sending chars from Latin 1 is still not working :(. I tried encode the input to encrypt function, the output too, with different encodings (latin 1,cp1250,cp1252), but the result on nostale server is "?" or space, sometimes when i write character from Latin 1, it cuts the packet, so if i send "say abcčd" i see "abc", but sometimes it is "abc?d" or "abc d"

-Nokis 12/05/2017 00:59

Quote:

Originally Posted by lika85 (Post 36493263)
Well i fount, that the right encoding for czech server is Latin 1, probably for other servers too, i am encoding the output of decryptGamePacket function, but sending chars from Latin 1 is still not working :(. I tried encode the input to encrypt function, the output too, with different encodings (latin 1,cp1250,cp1252), but the result on nostale server is "?" or space, sometimes when i write character from Latin 1, it cuts the packet, so if i send "say abcčd" i see "abc", but sometimes it is "abc?d" or "abc d"

The correct encoding for Czech is Windows-1250.
I think there's a problem with your encryption algorithm.

I suggest to use the C++ crypto for testing: pass a random string to the C++ encryption, compile it, do the same with your Java algorithm and check if the output is the same (encoding here doesn't matter, compare bytes from buffers). If not, compare step by step your algorithm with the C++ one.

lika85 12/05/2017 19:54

Quote:

Originally Posted by -Nokis (Post 36518549)
The correct encoding for Czech is Windows-1250.
I think there's a problem with your encryption algorithm.

I suggest to use the C++ crypto for testing: pass a random string to the C++ encryption, compile it, do the same with your Java algorithm and check if the output is the same (encoding here doesn't matter, compare bytes from buffers). If not, compare step by step your algorithm with the C++ one.

I've solved my problem. I was really dumb :D. I converted the buf string into byte array with the coding, and then converted the byte array to string. In java String has only UTF16 internlal coding. So it is working :cool:.

siuwing123 03/15/2018 13:18

after i include the cryptoPP library and compile.
#error <mutex> is not supported when compiling with /clr or /clr:pure.


All times are GMT +2. The time now is 08:44.

Powered by vBulletin®
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.