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.


All times are GMT +2. The time now is 02:09.

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