FreePVP)))
18.07.2012, 23:44
Доброго времени суток, в этой статье я опишу процесс авторизации в Perfect World без клиента игры
Пугаться не стоит, так как это очень просто
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
Для начала я опишу пакеты, которые мы будем отправлять/получать:
S-C 0x01 - ServerInfo
C-S 0x03 - LogginAnnounce
S-C 0x02/0x05 - SMKey/ServerError
Как только мы подключимся к серверу, он вышлет нам пакет 0x01
В этом пакете будет храниться ключ для генерации хеша, который мы передадим серверу в пакете 0x03
Затем сервер вышлет нам пакет 0x02 или же 0x05
0x02 - Связка верна
0x05 - Связка неверна
Для начала создадим класс PWAuth.cs, в котором будут следующие переменный:
string IP;
int Port;
public bool Result { get; private set; }// Результат авторизации(связка(логин+пароль) верна/не верна)
public bool Connected { get; private set; }// Результат подключения к серверу(подключиться удалось/не удалось)
Так же мы должны подключить следующие библиотеки:
using System.Net.Sockets;
using System.Security.Cryptography;
Удобнее будет передавать IP и Порт классу при его создании, для этого создадим пару методов
public PWAuth(string ip) : this(ip, 29000) { }
public PWAuth(string ip, int port)
{
IP = ip;
Port = port;
}
При формировании пакета 0x03(LogginAnnounce) нам понадобится генерация хеша md5 (логин+пароля) в обертке HMACMD5
Для этого мы будем использовать следующий метод:
private byte[] GetHash(byte[] key, string login, string pass)
{
byte[] logwithpass = Encoding.ASCII.GetBytes(login + pass);
MD5 md5 = MD5.Create();
return new HMACMD5(md5.ComputeHash(logwithpass)).ComputeHash( key);
}
Мы закончили подготавливать класс и начнем писать основной код в методе Check, которому мы передадим логин и пароль:
public bool Check(string login, string pass)
Этот метод будет возвращать результат(Result)
Чтобы не было путаницы, в самом начале метода сделаем следующее
Result = false;
Connected = false;
Далее приступим к созданию сокета:
TcpClient tcp = new TcpClient();
try
{
tcp.Connect(IP, Port);
}
catch { return false; }
Socket skt = tcp.Client;
Connected = skt.Connected;
Я обернул метод tcp.Connect(IP, Port); в блок try затем, чтобы при неудачном подключении не вылетал Exception
Если же мы подключились к серверу, то можно начинать чтение пакета, который вышлет нам сервер(причему все следующие действия мы будем проводить в цикле while (skt.Connected))
Все полученные данные мы запишем в массив байт
byte[] buf = new byte[0xFF];
skt.Receive(buf);
buf[0] - Тип пакета
buf[1] - Длина пакета
Все остальное - пакет
Если тип пакета равен 0x01(if (buf[0] == 0x01)), то нам нужно сформировать пакет 0x03 и отправить его серверу
if (buf[0] == 0x01)
{
List<byte> Send = new List<byte>();
byte[] key = new byte[0x10];
for (int i = 0; i < 0x10; i++) key[i] = buf[i + 3];
byte[] loginbt = Encoding.GetEncoding(1251).GetBytes(login);
byte[] hash = GetHash(key, login, pass);
Send.Add((byte)loginbt.Length);
Send.AddRange(loginbt);
Send.Add((byte)hash.Length);
Send.AddRange(hash);
Send.Add(0x00);
Send.AddRange(new byte[]{4,0,0,0,0});
Send.Insert(0,(byte)Send.Count);
Send.Insert(0, 0x03);
skt.Send(Send.ToArray());
continue;
}
Оператор continue ([Ссылки могут видеть только зарегистрированные и активированные пользователи](v=vs.90).aspx)
В коде чуть выше мы проделали следующие действия:
Вычитали ключ из данных, которые прислал нам сервер
Перевели логин в байт
Сгенерировали хеш
Записали длину логина и логин в массиве байт
Записали длину хеша и сам хеш
Записали длину пакета в начало массива
Записали тип пакета(0x03) в начало массива
Отправили пакет
Следующий пакет, который отправит нам сервер, будет для нас результатом авторизации и если тип пакета не равен 0x01, то будет выполнен код после условия выше:
Result = buf[0] == 0x02;
break;
Оператор break; ([Ссылки могут видеть только зарегистрированные и активированные пользователи](v=vs.90))
И под конец метода мы закроем сокет и вернем результат:
skt.Close();
return Result;
В итоге у нас должен получиться следующий класс:
using System.Collections.Generic;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
namespace PWCheckAccount
{
class PWAuth
{
string IP;
int Port;
public bool Result { get; private set; }// Результат авторизации(связка(логин+пароль) верна/не верна)
public bool Connected { get; private set; }// Результат подключения к серверу(подключиться удалось/не удалось)
public PWAuth(string ip) : this(ip, 29000) { }
public PWAuth(string ip, int port)
{
IP = ip;
Port = port;
}
public bool Check(string login, string pass)
{
Result = false;
Connected = false;
TcpClient tcp = new TcpClient();
try
{
tcp.Connect(IP, Port);
}
catch { return false; }
Socket skt = tcp.Client;
Connected = skt.Connected;
while (skt.Connected)
{
byte[] buf = new byte[0xFF];
skt.Receive(buf);
if (buf[0] == 0x01)
{
List<byte> Send = new List<byte>();
byte[] key = new byte[0x10];
for (int i = 0; i < 0x10; i++) key[i] = buf[i + 3];
byte[] loginbt = Encoding.GetEncoding(1251).GetBytes(login);
byte[] hash = GetHash(key, login, pass);
Send.Add((byte)loginbt.Length);
Send.AddRange(loginbt);
Send.Add((byte)hash.Length);
Send.AddRange(hash);
Send.Add(0x00);
Send.Insert(0,(byte)Send.Count);
Send.Insert(0, 0x03);
skt.Send(Send.ToArray());
continue;
}
Result = buf[0] == 0x02;
break;
}
skt.Close();
return Result;
}
private byte[] GetHash(byte[] key, string login, string pass)
{
byte[] logwithpass = Encoding.ASCII.GetBytes(login + pass);
MD5 md5 = MD5.Create();
return new HMACMD5(md5.ComputeHash(logwithpass)).ComputeHash( key);
}
}
}
Пример работы с классом:
PWAuth auth = new PWAuth(tbIP.Text, (int)updPort.Value);
auth.Check(tbLogin.Text, tbPass.Text);
if (!auth.Connected)
{
MessageBox.Show("Ошибка при подключении");
return;
}
MessageBox.Show(auth.Result ? "Успешная авторизация" : "Логин или пароль не верны");
В аттаче лежит готовый проект с GUI интерфейсом
Пугаться не стоит, так как это очень просто
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
Для начала я опишу пакеты, которые мы будем отправлять/получать:
S-C 0x01 - ServerInfo
C-S 0x03 - LogginAnnounce
S-C 0x02/0x05 - SMKey/ServerError
Как только мы подключимся к серверу, он вышлет нам пакет 0x01
В этом пакете будет храниться ключ для генерации хеша, который мы передадим серверу в пакете 0x03
Затем сервер вышлет нам пакет 0x02 или же 0x05
0x02 - Связка верна
0x05 - Связка неверна
Для начала создадим класс PWAuth.cs, в котором будут следующие переменный:
string IP;
int Port;
public bool Result { get; private set; }// Результат авторизации(связка(логин+пароль) верна/не верна)
public bool Connected { get; private set; }// Результат подключения к серверу(подключиться удалось/не удалось)
Так же мы должны подключить следующие библиотеки:
using System.Net.Sockets;
using System.Security.Cryptography;
Удобнее будет передавать IP и Порт классу при его создании, для этого создадим пару методов
public PWAuth(string ip) : this(ip, 29000) { }
public PWAuth(string ip, int port)
{
IP = ip;
Port = port;
}
При формировании пакета 0x03(LogginAnnounce) нам понадобится генерация хеша md5 (логин+пароля) в обертке HMACMD5
Для этого мы будем использовать следующий метод:
private byte[] GetHash(byte[] key, string login, string pass)
{
byte[] logwithpass = Encoding.ASCII.GetBytes(login + pass);
MD5 md5 = MD5.Create();
return new HMACMD5(md5.ComputeHash(logwithpass)).ComputeHash( key);
}
Мы закончили подготавливать класс и начнем писать основной код в методе Check, которому мы передадим логин и пароль:
public bool Check(string login, string pass)
Этот метод будет возвращать результат(Result)
Чтобы не было путаницы, в самом начале метода сделаем следующее
Result = false;
Connected = false;
Далее приступим к созданию сокета:
TcpClient tcp = new TcpClient();
try
{
tcp.Connect(IP, Port);
}
catch { return false; }
Socket skt = tcp.Client;
Connected = skt.Connected;
Я обернул метод tcp.Connect(IP, Port); в блок try затем, чтобы при неудачном подключении не вылетал Exception
Если же мы подключились к серверу, то можно начинать чтение пакета, который вышлет нам сервер(причему все следующие действия мы будем проводить в цикле while (skt.Connected))
Все полученные данные мы запишем в массив байт
byte[] buf = new byte[0xFF];
skt.Receive(buf);
buf[0] - Тип пакета
buf[1] - Длина пакета
Все остальное - пакет
Если тип пакета равен 0x01(if (buf[0] == 0x01)), то нам нужно сформировать пакет 0x03 и отправить его серверу
if (buf[0] == 0x01)
{
List<byte> Send = new List<byte>();
byte[] key = new byte[0x10];
for (int i = 0; i < 0x10; i++) key[i] = buf[i + 3];
byte[] loginbt = Encoding.GetEncoding(1251).GetBytes(login);
byte[] hash = GetHash(key, login, pass);
Send.Add((byte)loginbt.Length);
Send.AddRange(loginbt);
Send.Add((byte)hash.Length);
Send.AddRange(hash);
Send.Add(0x00);
Send.AddRange(new byte[]{4,0,0,0,0});
Send.Insert(0,(byte)Send.Count);
Send.Insert(0, 0x03);
skt.Send(Send.ToArray());
continue;
}
Оператор continue ([Ссылки могут видеть только зарегистрированные и активированные пользователи](v=vs.90).aspx)
В коде чуть выше мы проделали следующие действия:
Вычитали ключ из данных, которые прислал нам сервер
Перевели логин в байт
Сгенерировали хеш
Записали длину логина и логин в массиве байт
Записали длину хеша и сам хеш
Записали длину пакета в начало массива
Записали тип пакета(0x03) в начало массива
Отправили пакет
Следующий пакет, который отправит нам сервер, будет для нас результатом авторизации и если тип пакета не равен 0x01, то будет выполнен код после условия выше:
Result = buf[0] == 0x02;
break;
Оператор break; ([Ссылки могут видеть только зарегистрированные и активированные пользователи](v=vs.90))
И под конец метода мы закроем сокет и вернем результат:
skt.Close();
return Result;
В итоге у нас должен получиться следующий класс:
using System.Collections.Generic;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
namespace PWCheckAccount
{
class PWAuth
{
string IP;
int Port;
public bool Result { get; private set; }// Результат авторизации(связка(логин+пароль) верна/не верна)
public bool Connected { get; private set; }// Результат подключения к серверу(подключиться удалось/не удалось)
public PWAuth(string ip) : this(ip, 29000) { }
public PWAuth(string ip, int port)
{
IP = ip;
Port = port;
}
public bool Check(string login, string pass)
{
Result = false;
Connected = false;
TcpClient tcp = new TcpClient();
try
{
tcp.Connect(IP, Port);
}
catch { return false; }
Socket skt = tcp.Client;
Connected = skt.Connected;
while (skt.Connected)
{
byte[] buf = new byte[0xFF];
skt.Receive(buf);
if (buf[0] == 0x01)
{
List<byte> Send = new List<byte>();
byte[] key = new byte[0x10];
for (int i = 0; i < 0x10; i++) key[i] = buf[i + 3];
byte[] loginbt = Encoding.GetEncoding(1251).GetBytes(login);
byte[] hash = GetHash(key, login, pass);
Send.Add((byte)loginbt.Length);
Send.AddRange(loginbt);
Send.Add((byte)hash.Length);
Send.AddRange(hash);
Send.Add(0x00);
Send.Insert(0,(byte)Send.Count);
Send.Insert(0, 0x03);
skt.Send(Send.ToArray());
continue;
}
Result = buf[0] == 0x02;
break;
}
skt.Close();
return Result;
}
private byte[] GetHash(byte[] key, string login, string pass)
{
byte[] logwithpass = Encoding.ASCII.GetBytes(login + pass);
MD5 md5 = MD5.Create();
return new HMACMD5(md5.ComputeHash(logwithpass)).ComputeHash( key);
}
}
}
Пример работы с классом:
PWAuth auth = new PWAuth(tbIP.Text, (int)updPort.Value);
auth.Check(tbLogin.Text, tbPass.Text);
if (!auth.Connected)
{
MessageBox.Show("Ошибка при подключении");
return;
}
MessageBox.Show(auth.Result ? "Успешная авторизация" : "Логин или пароль не верны");
В аттаче лежит готовый проект с GUI интерфейсом