#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
// сокеты / io
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
// Blowfish
#include <openssl/blowfish.h>
#define L2_BF_KEY "\x6B\x60\xCB\x5B\x82\xCE\x90\xB1\xCC\x2B\x6C\x55\x6C\x6C\x6C\x6C"
// Параметры логин сервера
//#define L2_LOGIN_SERVER "127.0.0.1"
#define L2_LOGIN_SERVER "login.nanogame.org"
//#define L2_LOGIN_SERVER "195.211.222.195"
#define L2_LOGIN_PORT 2106
enum ERRORS {
E_OK = 0,
E_OPEN_SOCKET,
E_RESOLVE,
E_INVALID_ADDR_TYPE,
E_CONNECT
};
typedef struct l2_packet {
unsigned short length;
unsigned char *data;
} _l2_packet;
#pragma pack(push, 1)
typedef struct L2LoginInitPacket {
unsigned int session_id; // ID сессии
unsigned int protocol_version; // ID сессии
unsigned char rsa_key[128]; // RSA ключ
unsigned int gameguard[4]; // Хуитка
unsigned char blowfish_key[16]; // ключ
} _L2LoginInitPacket;
#pragma pack(pop)
void dump(unsigned char *data, unsigned int len);
// l2 packets
void set_l2_blowfish_key(const unsigned char *key, unsigned int len, bool is_static);
void init_packet(l2_packet *p);
void free_packet(l2_packet *p);
bool recv_l2_packet(int sock, l2_packet *p);
bool send_l2_packet(int sock, unsigned char *data, unsigned short len);
void create_packet(l2_packet *p, unsigned char *data, unsigned int len);
unsigned int encode_xor(unsigned char *data, unsigned int size, unsigned int k);
void print_bin(unsigned char *data, unsigned int len);
bool is_static_bf_key = true;
BF_KEY l2_blowfisdh_key;
int main() {
int sock;
struct sockaddr_in addr;
l2_packet p;
init_packet(&p);
try {
addr.sin_family = AF_INET;
addr.sin_port = htons(L2_LOGIN_PORT);
addr.sin_addr.s_addr = inet_addr(L2_LOGIN_SERVER); // проверяем, IP ли это
if (addr.sin_addr.s_addr == INADDR_NONE) { // нифига не IP
// Резолвим домен
struct hostent *h;
if (!(h = gethostbyname(L2_LOGIN_SERVER))) {
perror("gethostbyname error");
throw E_RESOLVE;
}
// Проверяем, AF_INET это или нет
if (h->h_addrtype != AF_INET) {
perror("Login server addr is not AF_INET o_O");
throw E_INVALID_ADDR_TYPE;
}
// Задём IP
addr.sin_addr.s_addr = ((struct in_addr *) h->h_addr_list[0])->s_addr;
}
// Открываем сокет
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("open socket error");
throw E_OPEN_SOCKET;
}
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
perror("connect socket error");
throw E_CONNECT;
}
printf("connected!\n");
/*
<dump 184 bytes>
// ID
00
// Session
27 40 56 09
// Revision
21 C6 00 00
// RSA
D2 F8 2B A1 EB B4 01 D1 EE D9 98 0E 84 E4 58 65
1D D5 45 A1 99 35 E7 65 BD 2B C1 87 D8 C7 E4 01
E5 D3 F2 D8 E3 47 D5 36 0E D9 18 70 7F 99 B1 97
F7 EA 75 D8 74 6C 7A 5C 73 A1 37 92 F0 23 A5 22
3F DF 36 28 7C 41 01 CE 98 97 1F 3A 67 79 EB 59
31 F6 7F 76 70 BB 93 01 C3 1B 09 C2 D4 76 30 3B
E5 64 0E FC 9B EE 6D C6 49 2D 20 43 50 15 A5 7E
15 95 E2 20 F5 9D D3 17 7F 5C 0F E0 E9 34 D0 49
// GG
4E 95 DD 29
FC 9C C3 77
20 B6 4D 97
F7 E0 45 07
// Key
CC F6 29 04 DC D8 70 AD F9 35 0D 13 7E C9 A0 1F
// NULL
00
// WTF
FF 6B 17 48 B5 9E 5D 84 07 2E 00 00 00 00
</dump>
Blowfish: CCF62904DCD870ADF9350D137EC9A01F
*/
/*
connected!
153
<dump 184 bytes>
00 EF 4A 0B 0D 21 C6 00 00 BE 07 55 6D 19 D5 21
9B 75 AE 9F 7C A2 5A AB 51 BC 0D F0 79 CF 4C DA
A5 3A BA 47 6D E9 FC 76 31 48 FE 18 08 77 57 A7
4C A2 DA 63 E5 2D 41 A3 AF 61 38 84 BB 19 58 8E
35 3C 74 58 72 60 43 C8 2B AE 1B E7 B2 AE E8 C5
59 EA CA 30 F3 F2 9E 01 E8 3C 65 44 A6 AA 91 FA
38 C2 C1 4F 18 B4 FD A2 42 0E 0C 2D EE AE 9B CD
81 20 4A C6 A0 3D C9 F3 B2 B5 AC 70 58 66 36 46
B6 F7 71 A2 0B 42 45 28 50 4E 65 22 29 FC 60 FC
77 20 49 B2 97 37 1F B2 07
EE 8F 1F D7 99 AE 28 9F 8C 16 D7 2D C7 71 76 68
FF
5F D1 0D 1A 28 B2 98 73 EF 6E 00 00 00 00
</dump>
Blowfish: EE8F1FD799AE289F8C16D72DC7717668
GG0: 2922654E
GG1: 77FC60FC
GG2: 97B24920
GG3: 07B21F37
session_id = 0D0B4AEF
protocol_version = 0000C621
<dump 21 bytes>
07 4A 0B 0D 21 92 90 4D 18 30 B5 7C 96 61 41 47
05 07 96 FB 00
</dump>
<dump 34 bytes>
22 00 D7 BF BC B9 60 CD 82 0B C7 19 25 C1 0B 81
FF 55 BC C5 B5 7C A9 8A 93 3B 58 BB BF 60 03 9E
B1 AA
</dump>
*/
//const unsigned char key[] = "\xEE\x8F\x1F\xD7\x99\xAE\x28\x9F\x8C\x16\xD7\x2D\xC7\x71\x76\x68";
const unsigned char key[] = "\x68\x76\x71\xC7\x2D\xD7\x16\x8C\x9F\x28\xAE\x99\xD7\x1F\x8F\xEE";
set_l2_blowfish_key((unsigned char *) key, 16, true);
// for (unsigned int offset = 0; offset < p->length; offset += 8)
// BF_decrypt((BF_LONG *)(p->data + offset), &l2_blowfisdh_key);
// set_l2_blowfish_key((unsigned char *) L2_BF_KEY, sizeof(L2_BF_KEY) - 1, true);
//0x2a, 0x00,
unsigned char lal[] = { 0xe4, 0x50, 0x64, 0xe9, 0x7b, 0x4f, 0xa6, 0x43, 0xe3, 0x7b, 0x9b, 0xa1, 0x07, 0xa1, 0x93, 0x6a, 0xe3, 0x7b, 0x9b, 0xa1, 0x07, 0xa1, 0x93, 0x6a, 0x2e, 0x56, 0xa2, 0x2e, 0x22, 0x72, 0xa8, 0x6b, 0xe3, 0x7b, 0x9b, 0xa1, 0x07, 0xa1, 0x93, 0x6a };
std::cout << (float)sizeof(lal)/8 << "\n";
for (unsigned int offset = 0; offset < sizeof(lal); offset += 8)
BF_decrypt((BF_LONG *)(lal + offset), &l2_blowfisdh_key);
dump(lal, sizeof(lal));
// 90 B2 C4 36 3A DB F2 56 C9 A0 B1 90 0F F7 D5 28
// C9 A0 B1 90 0F F7 D5 28 1A E6 B2 A0 1B CA 8E CA
// C9 A0 B1 90 0F F7 D5 28
exit(1);
// Login Server
// Задаём стандартный ключ логина
set_l2_blowfish_key((unsigned char *) L2_BF_KEY, sizeof(L2_BF_KEY) - 1, true);
while (true) {
if (!recv_l2_packet(sock, &p) || !p.length)
continue;
switch (p.data[0]) {
case 0x00:
{
// Расшифровуем и парсим пакет
encode_xor(p.data + 1, p.length - 4, *(unsigned int *) (p.data + (p.length - 4)));
dump(p.data, p.length);
// Парсим пакет
L2LoginInitPacket *l2_init = (L2LoginInitPacket *) (p.data + 1);
printf("Blowfish: ");
print_bin(l2_init->blowfish_key, 16);
printf("\n");
printf("GG0: %08X\n", l2_init->gameguard[0]);
printf("GG1: %08X\n", l2_init->gameguard[1]);
printf("GG2: %08X\n", l2_init->gameguard[2]);
printf("GG3: %08X\n", l2_init->gameguard[3]);
printf("\n");
printf(
"session_id = %08X\n"
"protocol_version = %08X\n",
l2_init->session_id,
l2_init->protocol_version
);
// Устанавливаем принятый ключ
set_l2_blowfish_key((unsigned char *) l2_init->blowfish_key, 16, false);
// Шлём RequestGGAuth
// type | session_id | GG data0 | GG data1 | GG data2 | GG data3
unsigned char *packet = (unsigned char *) malloc(4 * 4 + 4 + 1 + 8); //
packet[0] = 0x07;
packet[1] = p.data[2];
packet[2] = p.data[3];
packet[3] = p.data[4];
packet[4] = p.data[5];
int off = 4;
packet[1+off] = 0x92;
packet[2+off] = 0x90;
packet[3+off] = 0x4d;
packet[4+off] = 0x18;
packet[5+off] = 0x30;
packet[6+off] = 0xb5;
packet[7+off] = 0x7c;
packet[8+off] = 0x96;
packet[9+off] = 0x61;
packet[10+off] = 0x41;
packet[11+off] = 0x47;
packet[12+off] = 0x05;
packet[13+off] = 0x07;
packet[14+off] = 0x96;
packet[15+off] = 0xfb;
packet[16+off] = 0x00;
packet[17+off] = 0x00;
packet[18+off] = 0x00;
packet[19+off] = 0x00;
packet[20+off] = 0x00;
packet[21+off] = 0x00;
packet[22+off] = 0x00;
packet[23+off] = 0x00;
packet[24+off] = 0x00;
// *(unsigned int *) (packet + 1) = l2_init->session_id; // 21
// *(unsigned int *) (packet + 5) = l2_init->gameguard[0];
// *(unsigned int *) (packet + 9) = l2_init->gameguard[1];
// *(unsigned int *) (packet + 13) = l2_init->gameguard[2];
// *(unsigned int *) (packet + 17) = l2_init->gameguard[3];
send_l2_packet(sock, packet, 4 * 4 + 4 + 1 + 8);
free(packet);
}
break;
default:
dump(p.data, p.length);
break;
}
}
//22 00
//BF 3F ED 84 94 5A 44 CB B4 E8 6A 23 75 C7 43 5B 5B 1C 95 F5 B0 07 ED A1
//01 51 F8 63 00 00 00 00
/*
@Override
protected void write()
{
writeC(0x00); // init packet id
writeD(_sessionId); // session id
writeD(0x0000c621); // protocol revision
writeB(_publicKey); // RSA Public Key
// unk GG related?
writeD(0x29DD954E);
writeD(0x77C39CFC);
writeD(0x97ADB620);
writeD(0x07BDE0F7);
writeB(_blowfishKey); // BlowFish key
writeC(0x00); // null termination ;)
}
*/
} catch (int err_code) {
}
free_packet(&p);
if (sock > -1)
close(sock);
return 0;
}
void set_l2_blowfish_key(const unsigned char *key, unsigned int len, bool is_static) {
BF_set_key(&l2_blowfisdh_key, len, key);
is_static_bf_key = is_static;
}
// Функции работы с пакетами L2
bool recv_l2_packet(int sock, l2_packet *p) {
free_packet(p);
if (recv(sock, &p->length, 2, 0) == 2) {
p->length -= 2; // Отнимем длину длины
if (p->length % 8 != 0) {
fprintf(stderr, "Invalid packet length!!! (%d %% 8 == %d)\n", p->length, p->length % 8);
return false;
}
p->data = (unsigned char *) malloc(p->length);
if ((recv(sock, p->data, p->length, 0)) != p->length) {
perror("recv");
free(p->data);
p->data = NULL;
return false;
}
/*
free(p->data);
unsigned char test_data[] = {0xba, 0x00, 0x2e, 0x28, 0x3a, 0x3a, 0xf6, 0xbc, 0xeb, 0x94, 0xb3, 0xc4, 0xe6, 0x23, 0x8b, 0xdb, 0x3c, 0xd3, 0xed, 0x42, 0x78, 0xf7, 0xf8, 0xce, 0x06, 0xd1, 0x91, 0x3d, 0xd8, 0x3e, 0xcf, 0x25, 0xba, 0x2b, 0x80, 0xd8, 0x4a, 0x71, 0xf6, 0x6e, 0x31, 0x18, 0xef, 0x78, 0x2f, 0x8d, 0xa3, 0xee, 0xed, 0xdc, 0x1b, 0xad, 0x60, 0xe9, 0x6f, 0x10, 0xf4, 0xab, 0x25, 0xb7, 0x51, 0x51, 0x18, 0xc6, 0xa5, 0x6a, 0x45, 0x90, 0x1a, 0xda, 0x84, 0xc3, 0xee, 0xd1, 0x79, 0x5a, 0x6e, 0x0a, 0x87, 0xdf, 0x4a, 0x18, 0x99, 0x29, 0xcb, 0xcb, 0x8c, 0x74, 0xb7, 0x3b, 0xb2, 0xa8, 0xed, 0x36, 0xd5, 0x51, 0x96, 0xac, 0xd8, 0x69, 0xec, 0x98, 0x5f, 0x3f, 0x10, 0x89, 0xa8, 0xd8, 0x8c, 0x7f, 0x3e, 0x10, 0xb3, 0x37, 0xcd, 0x94, 0xbf, 0x79, 0x33, 0x37, 0x0a, 0x7d, 0xff, 0x66, 0x13, 0x4e, 0xe7, 0xca, 0x29, 0xb1, 0x67, 0x87, 0xb8, 0x6e, 0xce, 0xaf, 0x7e, 0xbc, 0x5d, 0x32, 0x69, 0x42, 0x06, 0x4c, 0x7f, 0x56, 0xd2, 0xfb, 0xc0, 0x36, 0x19, 0x6d, 0x8c, 0x18, 0x7a, 0x51, 0x0a, 0x2d, 0x3a, 0xbb, 0xc0, 0x58, 0x4e, 0xee, 0x11, 0x11, 0xe7, 0xa7, 0x35, 0x46, 0x5d, 0xb6, 0xb6, 0xe7, 0x34, 0x85, 0x02, 0x96, 0xe2, 0x13, 0xbc, 0xb5, 0xbb, 0x52, 0xfb, 0x5a};
p->length = (test_data[0] | test_data[1] << 8) - 2;
p->data = (unsigned char*)malloc(sizeof(test_data)-2);
unsigned char *lal =(unsigned char *) test_data; lal++; lal++;
memcpy(p->data, lal, sizeof(test_data)-2);
// */
for (unsigned int offset = 0; offset < p->length; offset += 8)
BF_decrypt((BF_LONG *)(p->data + offset), &l2_blowfisdh_key);
}
return true;
}
bool send_l2_packet(int sock, unsigned char *data, unsigned short len) {
unsigned short packet_data_additional_len = 8 - (len % 8);
unsigned int packet_data_len = 2 + len + packet_data_additional_len + 8;
unsigned char *packet_data = (unsigned char *) malloc(packet_data_len); // size + data + checksum
dump(data, len);
packet_data[0] = packet_data_len & 0xFF;
packet_data[1] = packet_data_len >> 8 & 0xFF;
memcpy(packet_data + 2, data, len);
if (packet_data_additional_len)
memset(packet_data + 2 + len, 0, packet_data_additional_len);
unsigned long chk = 0;
unsigned int ecx = 0;
unsigned long chk2 = 0;
unsigned int ecx2 = 0;
for (unsigned short i = 0; i < packet_data_len-8; i += 1) {
//chk ^= packet_data[i];
ecx2 = *(unsigned int *) (packet_data + i);
chk2 ^= ecx2;
ecx = packet_data[i];
ecx |= packet_data[i + 1] << 8;
ecx |= packet_data[i + 2] << 16;
ecx |= packet_data[i + 3] << 24;
chk ^= ecx;
}
// ........
// ................
/*
packet_data[packet_data_len - 16] = chk & 0xFF;
packet_data[packet_data_len - 15] = chk >> 8 & 0xFF;
packet_data[packet_data_len - 14] = chk >> 16 & 0xFF;
packet_data[packet_data_len - 13] = chk >> 24 & 0xFF;
packet_data[packet_data_len - 12] = 0;
packet_data[packet_data_len - 11] = 0;
packet_data[packet_data_len - 10] = 0;
packet_data[packet_data_len - 9] = 0;
packet_data[packet_data_len - 8] = 0;
packet_data[packet_data_len - 7] = 0;
packet_data[packet_data_len - 6] = 0;
packet_data[packet_data_len - 5] = 0;
packet_data[packet_data_len - 4] = 0;
packet_data[packet_data_len - 3] = 0;
packet_data[packet_data_len - 2] = 0;
packet_data[packet_data_len - 1] = 0;
*/
unsigned long c = encode_xor(packet_data + 2, len + packet_data_additional_len, 0);
memcpy(packet_data + (packet_data_len - 8), &chk, sizeof(unsigned long));
printf("XOR: %16lX\n", chk);
printf("XOR: %16lX\n", chk2);
printf("XOR: %16lX\n", c);
// packet_data[packet_data_len - 8] = 0;
// packet_data[packet_data_len - 7] = 0;
// packet_data[packet_data_len - 6] = 0;
// packet_data[packet_data_len - 5] = 0;
// packet_data[packet_data_len - 4] = chk & 0xFF;
// packet_data[packet_data_len - 3] = chk >> 8 & 0xFF;
// packet_data[packet_data_len - 2] = chk >> 16 & 0xFF;
// packet_data[packet_data_len - 1] = chk >> 24 & 0xFF;
/*
packet_data[packet_data_len - 4] = 0;
packet_data[packet_data_len - 3] = 0;
packet_data[packet_data_len - 2] = 0;
packet_data[packet_data_len - 1] = 0;
packet_data[packet_data_len - 8] = chk >> 24 & 0xFF;
packet_data[packet_data_len - 7] = chk >> 16 & 0xFF;
packet_data[packet_data_len - 6] = chk >> 8 & 0xFF;
packet_data[packet_data_len - 5] = chk >> 0 & 0xFF;
*/
// 42
//memcpy(packet_data + (packet_data_len - 8), &chk, 4);
//memset(packet_data + (packet_data_len - 4), 0, 4);
for (unsigned short offset = 2; offset < packet_data_len; offset += 8)
BF_encrypt((BF_LONG *)(packet_data + offset), &l2_blowfisdh_key);
// for (unsigned short offset = 2; offset < packet_data_len; offset += 8)
// BF_decrypt((BF_LONG *)(packet_data + offset), &l2_blowfisdh_key);
if (send(sock, packet_data, packet_data_len, 0) != packet_data_len) {
perror("send");
free(packet_data);
return false;
}
dump(packet_data, packet_data_len);
free(packet_data);
return true;
}
void create_packet(l2_packet *p, unsigned char *data, unsigned int len) {
p->data = (unsigned char *) malloc(len);
memcpy(p->data, data, len);
p->length = len;
}
void init_packet(l2_packet *p) {
memset(p, 0, sizeof(l2_packet));
}
void free_packet(l2_packet *p) {
p->length = 0;
if (p->data) {
free(p->data);
p->data = NULL;
}
}
/*
// unk GG related?
writeD(0x29DD954E);
writeD(0x77C39CFC);
writeD(0x97ADB620);
writeD(0x07BDE0F7);
writeB(_blowfishKey); // BlowFish key
writeC(0x00); // null termination ;)
00
4D 9C E8 45
21 C6 00 00
D2 F8 2B A1 EB B4 01 D1 EE D9 98 0E 84 E4 58 65
1D D5 45 A1 99 35 E7 65 BD 2B C1 87 D8 C7 E4 01
E5 D3 F2 D8 E3 47 D5 36 0E D9 18 70 7F 99 B1 97
F7 EA 75 D8 74 6C 7A 5C 73 A1 37 92 F0 23 A5 22
3F DF 36 28 7C 41 01 CE 98 97 1F 3A 67 79 EB 59
31 F6 7F 76 70 BB 93 01 C3 1B 09 C2 D4 76 30 3B
E5 64 0E FC 9B EE 6D C6 49 2D 20 43 50 15 65 7E
15 95 12 20 F5 9D 2F 17 7F 5C F1 E0 E9 B4 2F 49
4E 55 22 29
FC 60 FC 77
A0 51 B2 97
07 11 B2 07
BB 17 F3 7D 20 1B C0 E3 B7 4D 14 89 D0 BC 2A 85
E1 F9 4C B9 DD 2E 4D 76 59 86 59 00 00 00 00
Формат:
9B 00 // Длина
00 // Тип
XX XX XX XX // ID сессии
XX XX XX XX // Ревизия протокола
[начало зашифрованного RSA-ключа]
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
[конец зашифрованного RSA-ключа]
XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX // неизвестно для чего это (в яве тут нули)
*/
unsigned int encode_xor(unsigned char *data, unsigned int size, unsigned int k) {
for (int *i = (int *) (data + size - 1); i >= (int *)data; --i)
k -= (*(unsigned int *) i = *(unsigned int *) i ^ k);
return k;
}
void dump(unsigned char *data, unsigned int len) {
printf("<dump %d bytes>\n", len);
printf(" ");
for (unsigned int i = 0; i < len; ++i) {
printf("%02X ", data[i]);
if ((i + 1) % 16 == 0)
printf("\n ");
}
printf("\n</dump>\n");
}
void print_bin(unsigned char *data, unsigned int len) {
for (unsigned int i = 0; i < len; ++i)
printf("%02X", data[i]);
}