PDA

Просмотр полной версии : [Статья] C# OOG Авторизация


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 интерфейсом

Kitsune
19.07.2012, 19:37
Делал бы уже нормально: с чтением куинтов на тип и размер, на асинхронной модели сокетов с последующей нарезкой и сборкой пакетов.

Ну и сокет использовать лучше, чем обертку над ним под названием tcpclient...

FreePVP)))
19.07.2012, 19:42
Делал бы уже нормально: с чтением куинтов на тип и размер, на асинхронной модели сокетов с последующей нарезкой и сборкой пакетов.

Ну и сокет использовать лучше, чем обертку над ним под названием tcpclient...
Этот класс сделан специально для проверки аккаунтов на валидность
Не вижу смысла делать большее, разве что чек логина еще добавить
Если уж и делать с нарезкой, куинтами и асинхронными сокетами, то в отдельной статье:)

Kitsune
19.07.2012, 20:02
Этот класс сделан специально для проверки аккаунтов на валидность
Нет смысла делать урезки, когда можно сделать что-то полноценное, что можно применять и как проверку логина и все остальное.

FreePVP)))
19.07.2012, 20:07
Нет смысла делать урезки, когда можно сделать что-то полноценное, что можно применять и как проверку логина и все остальное.
Ну знаешь, в своем парсере логинов мне удобнее было использовать этот класс, нежели подключать несколько других классов моей библиотеки

Добавлено через 3 минуты
Да и для того же брутфорса это было бы не очень удобно

chetoss
20.07.2012, 09:20
А не лучше было сделать, чтобы он логинился, если ок, то ловим 53 и берем оттуда лвл персонажей? а запись например: логин,пароль: лвл,лвл,лвл

N00bSa1b0t
20.07.2012, 11:18
Это же пример алгоритма, а не готовая программма.
Как хотите, так дальше и модифицируйте её.

Kitsune
20.07.2012, 19:46
Это же пример алгоритма, а не готовая программма.
Алгоритм описывается просто, без привязки к среде, языку программирования и т.д.
Автор темы предоставил реализацию алгоритма по работе с трафиком игры.
Реализация далека от идеала, поэтому и критикую.

N00bSa1b0t
24.07.2012, 00:15
Реализация далека от идеала, поэтому и критикую.
Я отвечал chetoss :) К Вашим словам у меня претензий никаких нету.

nitrotek
20.08.2012, 00:15
Думаю лишнее, для тех кто хочет этим заняться уже давно всё описано.

gurin
25.08.2012, 13:03
Алгоритм описывается просто, без привязки к среде, языку программирования и т.д.

не могли бы Вы подсказать ссылочку на такой алгоритм?

Kitsune
25.08.2012, 13:09
не могли бы Вы подсказать ссылочку на такой алгоритм?
Речь шла об абстрактном алгоритме, а точнее о том, что правильно называть алгоритмом.

Алгоритм — набор инструкций, описывающих порядок действий исполнителя для достижения результата решения задачи за конечное время.

7_ON
05.09.2012, 19:26
Эта тема уже не раз поднималась, и работа с первыми пакетами не один раз описана. Но было бы очень интересно увидеть процесс выбора персонажа и входа в игру)

nitrotek
15.09.2012, 20:10
Ничего интересного, всё разбирается очень просто. Пишите или ищите снифер и исследуйте пакеты.

[Na`Vi]DendI
20.09.2012, 01:33
А возможно ли как то таким образом спарсить информацию об аккаунте ?
Автор спасибо большое за авторизацию.

FreePVP)))
20.09.2012, 01:56
DendI;3522040"]спарсить информацию об аккаунте
Какую например?

Goooooogle
20.09.2012, 09:57
FreePVP))), Если бы ты сделал гайд по отправке пакетов и инжектам было бы замечательно!

nitrotek
20.09.2012, 12:00
FreePVP))), Если бы ты сделал гайд по отправке пакетов и инжектам было бы замечательно!
понятие инжекта для этого способа неприменимо.

ну а пользовательский пакет давно известен.
22
<Size>
{
<Size>
Opcode
UserData
}

[Na`Vi]DendI
20.09.2012, 19:45
Какую например?
Любую информацию об аккаунте,только начал учить структуру пакетов,могли бы вы сказать что переделать в коде что бы узнать информацию.

FreePVP)))
20.09.2012, 21:11
DendI;3524902"]Любую информацию об аккаунте,только начал учить структуру пакетов,могли бы вы сказать что переделать в коде что бы узнать информацию.
Все
Это лишь авторизация

[Na`Vi]DendI
23.09.2012, 18:15
Все
Это лишь авторизация
Еще очень интересно,как узнать к примеру IP и порт куда коннектиться ? И какие пакеты за что отвечают ?

VeTaL_UA
23.09.2012, 23:53
DendI;3539081"]Еще очень интересно,как узнать к примеру IP и порт куда коннектиться ?
Заходишь в папку с клиентом, дальше по пути *папка с ПВ*->element->userdata->server, там файл serverlist.txt. Открывешь его и видишь его содержимое:
Феникс 29000:link12.pwonline.ru 13
Адара 29000:link11.pwonline.ru 12
Антарес 29000:link10.pwonline.ru 11
Пегас 29000:link9.pwonline.ru 10
Астра 29000:link8.pwonline.ru 9
Процион 29000:link7.pwonline.ru 8
Альтаир 29000:link6.pwonline.ru 7
Таразед 29000:link5.pwonline.ru 6
Мира 29000:link4.pwonline.ru 5
Сириус 29000:link3.pwonline.ru 4
Вега 29000:link2.pwonline.ru 3
Орион 29000:link1.pwonline.ru 2
Порт равен 29000, а айпи узнаёшь пинговкой.
DendI;3539081"]И какие пакеты за что отвечают ?
Пакет логина за логин, пакет входа в игру за вход в игру и так по аналогии...

[Na`Vi]DendI
24.09.2012, 15:09
Заходишь в папку с клиентом, дальше по пути *папка с ПВ*->element->userdata->server, там файл serverlist.txt. Открывешь его и видишь его содержимое:
Феникс 29000:link12.pwonline.ru 13
Адара 29000:link11.pwonline.ru 12
Антарес 29000:link10.pwonline.ru 11
Пегас 29000:link9.pwonline.ru 10
Астра 29000:link8.pwonline.ru 9
Процион 29000:link7.pwonline.ru 8
Альтаир 29000:link6.pwonline.ru 7
Таразед 29000:link5.pwonline.ru 6
Мира 29000:link4.pwonline.ru 5
Сириус 29000:link3.pwonline.ru 4
Вега 29000:link2.pwonline.ru 3
Орион 29000:link1.pwonline.ru 2
Порт равен 29000, а айпи узнаёшь пинговкой.

Пакет логина за логин, пакет входа в игру за вход в игру и так по аналогии...
Спасибо за пояснения и детали, то есть у них нет свои значений, ну типо пост даты ? или они все одинаковые ?

АпАпАпчих
10.11.2012, 15:12
return new HMACMD5(md5.ComputeHash(logwithpass)).ComputeHash( key);

Запутался в этой строчке. Что тут должно вернуться? мы получаем хэш из logwithpass при помощи md5.ComputeHash(logwithpass), а дальше HMACMD5(полученный хэш).ComputeHash(key) - что делает? хочу понять для написания подобного на delphi. Я C# не знаю, как и в прочем си. Не пойму, что такое HMACMD5? Можно пожалуйста объяснить поподробнее или просто выдать результаты переменных/функций пошаговые после каждой строчки? C# нет и в ближайшее время не будет, просмотреть выполнение не смогу, а хочется разобраться(

DelFast
13.11.2012, 13:07
Странно, уже не логинит.. Проблема с пакетами?

Ilyialat
17.11.2012, 18:06
Даа, уже не работает =( Я сначала свой код написал, смотрю, не пашет. Скачал готовый проект, тоже не пашет... Ип-адресы правильно указал, кстати, можно ещё адрес серва через WPE PRO узнавать. Надеюсь, автор найдёт, в чём проблема =) Играю на РУОФЕ, Феникс.

FreePVP)))
17.11.2012, 18:37
Даа, уже не работает =( Я сначала свой код написал, смотрю, не пашет. Скачал готовый проект, тоже не пашет... Ип-адресы правильно указал, кстати, можно ещё адрес серва через WPE PRO узнавать. Надеюсь, автор найдёт, в чём проблема =) Играю на РУОФЕ, Феникс.
Изменения в пакете я описал еще около полугода назад
Вам нужно лишь воспользоваться чудесной вещью - google.com

Ilyialat
17.11.2012, 18:55
Спасибо большое =) А можно ли на пакетах написать полноценного бота? Или это очень трудоёмко?
Ничего не нагуглил =( Если вам не трудно, дайте ссылку, пожалуйста)

FreePVP)))
17.11.2012, 21:46
Спасибо большое =) А можно ли на пакетах написать полноценного бота? Или это очень трудоёмко?
Ничего не нагуглил =( Если вам не трудно, дайте ссылку, пожалуйста)
Конечно трудоемко
Дам подсказку - ищите в разделе общения разработчиков

Ilyialat
17.11.2012, 22:21
Рыться пришлось =( Но нашёл =) Как я понял, PWPacketListener не подходит мне, нужна PandoraBox, например узнать, какие предметы у меня в инвентаре лежат...и ещё на дешифровку настроить... Ох, тяжелое дело это =) Но оно того стоит =)
Заметил особенность - ник нужно вводить в нижнем регистре

Добавлено через 21 час 55 минут
Практически закончил работу с RC4. А что дальше делать?... Это что, надо писать dll для снифера определенного? Который будет на ходу дешифровать траффик?... Или самому нужные пакеты вылавливать?...

Добавлено через 22 часа 1 минуту
По крайней мере, пока что мне нужен пакет захода чаром в игру... А потом понадобится инвентарь, список скилов, статы, чат и т.д

tianddu
04.03.2013, 10:24
кстати в исходнике и в спойлере отсутствует PWAuth
Send.AddRange(new byte[]{4,0,0,0,0});
если его вставить в исходник то авторизация проходит успешно ;)
и еще по поводу этой части кода, есть вопрос, почему в коде мы пишем 4,0,0,0,0 - а через пандору если смотреть(если заходить через обычный клиент) то концовка пакета такая 04 FF FF FF FF , или последние 4 байта не важны?
p.S. и в добавок не понятны
public PWAuth(string ip) : this(ip, 29000) { }
public PWAuth(string ip, int port)
что именно делает 1ая строчка? ведь без нее код компилируется и спокойно исполняется...

Bludun
04.04.2013, 16:55
Автору большое спасибо за эту тему, очень помогла. Не достающие 4 0 0 0 0 дописал и все заработало, но столкнулся с интересным моментом, природу которого я объяснить не могу, но хочется понять в чем дело.

Так не работает


PackedSend.Add((byte)loginbt.Length);
PackedSend.AddRange(loginbt);
PackedSend.Add((byte)hash.Length);
PackedSend.AddRange(hash);
PackedSend.Add(0x00);

PackedSend.Insert(0, (byte)PackedSend.Count);
PackedSend.Insert(0, 0x03);

PackedSend.Add(4);
PackedSend.Add(255);
PackedSend.Add(255);
PackedSend.Add(255);
PackedSend.Add(255);


а вот так все в порядке


PackedSend.Add((byte)loginbt.Length);
PackedSend.AddRange(loginbt);
PackedSend.Add((byte)hash.Length);
PackedSend.AddRange(hash);
PackedSend.Add(0x00);

PackedSend.Add(4);
PackedSend.Add(255);
PackedSend.Add(255);
PackedSend.Add(255);
PackedSend.Add(255);

PackedSend.Insert(0, (byte)PackedSend.Count);
PackedSend.Insert(0, 0x03);


Почему при использовании Insert до добавление не достающей части пакета методом Add, пакет в "Pandora Box" отправляется без не достающих 4 255 255 255 255 ? При отладке видно, что при обоих вариантах List идентичны друг другу. И метод Send возвращает одинаковое кол-во байт.

Добавлено через 13 часов 33 минуты
Разобрался.
PackedSend.Insert(0, (byte)PackedSend.Count); необходимо добавлять в конце формирования пакета т.к. этой строкой определяется размер пакета. Иначе размер будет не верен.

wajskopf
18.07.2013, 23:51
Скажите где правда? на другом форуме нашел структуру пакета 0х03 такую:

Пакет С0x03

Значение: Отправка данных для авторизации

Code
Размер Пример Описание
[ 1] 03 // Тип пакета
[ 1] 00 // Размер данных
[ V] nn xx xx xx .. .. // Логин
[ V] nn xx xx xx .. .. // Хеш "логин+пароль" ключем key1
[ 1] 00 // Поле появилось в версии 1.4.4,обычно 0

Здесь не вписываются длина логина и хэша в пакет. Как правильно все-таки?

Спасибо

nitrotek
19.07.2013, 00:05
[ V] nn xx xx xx .. .. // Логин
[ V] nn xx xx xx .. .. // Хеш "логин+пароль" ключем key1
а nn это что по вашему?

wajskopf
19.07.2013, 00:24
Почему сервер не отсылает ответ на пакет 0х03? ведь даже если пакет построен неверно, по идее должен быть ответ типа 0х05 - Логин отклонен. Или я ошибаюсь?
Проблема сервер молчит после того как я отсылаю пакет 0х03...(((

nitrotek
19.07.2013, 01:04
Молчит скорее всего потому что неверно сформирован размер пакета и сервер ждёт недостающих байт

wajskopf
19.07.2013, 09:43
Поясните пожалуйста строку
byte[] logwithpass = Encoding.ASCII.GetBytes(login + pass);
login + pass - это конкатенация строк? или суммирование их в шестнадцатеричном представлении?

nitrotek
19.07.2013, 10:29
login + pass - это конкатенация строк? или суммирование их в шестнадцатеричном представлении?
Конкатенация + login в нижнем регистре.

qqsda
08.04.2019, 13:47
На 1,5,5 таким способою все еще можно логинится? А то что бы я не делал пишет что не правильный логин или пароль