L2PacketHackФорум программы L2PacketHack, всё что с ней связано находится тут.
Правила раздела!
Категорически запрещено:
* Просить пакеты/скрипты на шмот/адену/другие вещи;
* Просить (помочь) взломать/сломать сервак/аккаунт или раздеть/наказать кого-то на любом серваке;
* Просить научить работать с программой, если не понимаете элементарных вещей в отношениях клиент-сервер (так как, если вы их понимаете, то вопросов по основным возможностям программы быть недолжно);
* Все посты не удовлетворяющие этим запретам, а так же не относящиеся к теме будут удаляться при первой возможности.
* Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы.
* Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.
* Данные запреты неподлежат обсуждению.
PS. По статьям: зайди к нам на сайт, там на главной странице ссылки есть.
Хочется поблагодарить следующих людей: TechnoWiz@rd, за базовую информацию для понимания протокола Lineage, а также KyberPrizrak за доработку исходной статьи.
Т.е. лучше изучать по статье KyberPrizrak? Его статья более расширенная? На глаз пока сложно определить отличия
UPD1
Еще один момент смущает: статья KyberPrizrak исправляющая баги (со слов KyberPrizrak) статьи TechnoWiz@rd имеет последнее редактирование 06.07.2007, в то время как статья TechnoWiz@rd была отредактирована 23.11.2007
Какой же версии описания протокола стоит доверять больше?
Последний раз редактировалось Linux, 01.06.2016 в 15:54.
Т.е. лучше изучать по статье KyberPrizrak? Его статья более расширенная? На глаз пока сложно определить отличия
это очень давно было, но на сколько помню у KyberPrizrak более верный протокол описан (как и по последовательности пакетов, так и по их содержимому)
Цитата:
Какой же версии описания протокола стоит доверять больше?
доверять можно только самостоятельно проснифанному трафику, т.к. часть очень важных пакетов (RequestAuthLogin, NetPing, s:8D) могут меняться от сервера к серверу
Цитата:
Сообщение от Linux
По собранной статистике можно понять поведение двух разных подключений? Где в моем случае была допущена ошибка, из-за чего до Game-Server так и не доходила программа?
у тебя пакет RequestAuthLogin значительно меньше, чем в оригинальном клиенте, расшифровывай пакет, смотри что внутри, возможно поймешь, что нужно передавать
у тебя пакет RequestAuthLogin значительно меньше, чем в оригинальном клиенте, расшифровывай пакет, смотри что внутри, возможно поймешь, что нужно передавать
1.1) А не подскажете чем расшифровывать или по какому алгоритму?
В проекте идет метод BlowFish.pas (by xkor) с комплектом методов:
procedure AddCheckSumm(var data: array of byte; Len: Integer);
function RepairKey(key: String): String;
function RSAEncrypt(a,p,rsakey: String): String;
function AntiXOR(s: String): String;
Что из этого подойдет?
1.2) А зачем пакет расшифровывать если пакет идет на сервер?
2) Добавил сравнительную таблицу пакетов (compare_LS.png), учитывая что первые 2 байта это размер, с 3-го кодируются данные, как по размеру пакета понять какой это пакет: Init, RequestAuthLogin и т.д.??
1.1) А не подскажете чем расшифровывать или по какому алгоритму?
По умолчанию трафик в Ла2 шифруется с помощью BF (также для RequestAuthLogin используется RSA и для части пакетов еще какой-то xor). Ответить на вопрос "чем и как" в данном случае, к сожалению, не смогу, т.к. ответ потянет минимум на полновесную статью, а то и на главу какой-нить книги, к тому же не являюсь большим специалистом в этой области.
Цитата:
Что из этого подойдет?
Все из перечисленных функций как раз занимаются расшифровкой пакетов.
Цитата:
1.2) А зачем пакет расшифровывать если пакет идет на сервер?
Это как проход через часового: ты должен знать пароль для прохода и лучший вариант его узнать - подслушать пароль.
Цитата:
как по размеру пакета понять какой это пакет
никак, в данном случае можно догадаться по очередности пакетов, на сколько помню очередность такая:
1) сервер присылает Init
2) клиент отвечает AuthGameGuard
3) сервер GGAuth
4) клиент RequestAuthLogin
5) сервер LoginOk либо LoginFail
6) клиент RequestServerList
7) сервер ServerList
8) клиент RequestServerLogin
9) сервер PlayOk либо PlayFail
плюс в эти пакеты могут вмешиваться NetPing и 8D
PS. В очередности могу немного ошибиться, за подробностями обращайся к статьям KyberPrizrak.
const
MAX_PKT_SIZE = $FFFF;
var
BLOWFISH_KEY: String; { 6B60CB5B82CE90B1CC2B6C556C6C6C6C }
MainBuff: array[0..MAX_PKT_SIZE-1] of byte;
RecvPktCount: Integer;
DecodeBF: boolean;
L2BF: L2BlowFish;
L2BF_Key: String;
RSA_Key: String;
SessionID: Integer;
GGAuthResponse: Integer;
SessionKey1_1, SessionKey1_2: Integer;
SessionKey2_1, SessionKey2_2: Integer;
procedure TfrmMain.WMSOCK_RS_EVENT(var Msg: TMessage);
var
SocketError: Integer;
Len: Integer;
begin
SocketError := WSAGetSelectError(Msg.lParam);
if (SocketError<>0) then begin
if SocketError=10053 then
ToLog('[WMSOCK_RS_EVENT]: WSAECONNABORTED (#10053) - Software caused connection abort')
else
ToLog('[WMSOCK_RS_EVENT]: WS_ERROR (#number): '+IntToStr(SocketError));
ToLog('');
L2SocketClose(RS_Socket);
Exit;
end;
case WSAGetSelectEvent(Msg.lParam) of
FD_Read:
begin
FillChar(MainBuff, Length(MainBuff), 0);
Len := recv(RS_Socket, MainBuff, MAX_PKT_SIZE, 0);
inc(RecvPktCount);
LS_Parser(MainBuff, Len);
end;
FD_Close:
L2SocketClose(RS_Socket);
end;
end;
procedure TfrmMain.LS_Parser(buff: Array of Byte; Len: Integer);
var
temp: array [0..MAX_PKT_SIZE-1] of char;
s: string;
i: integer;
begin
if RecvPktCount=1 then
begin
if buff[2]=0 then
DecodeBF := False
else
DecodeBF := True;
end;
if DecodeBF then
begin
Move(buff[2],temp[0],Len-2);
L2BF.bfDecode(temp,Len-2);
if temp[0]=#0 then
begin
SetLength(s,Len-2);
for i:=0 to Len-3 do
s[i+1]:=temp[i];
s:=AntiXor(s);
for i:=0 to Len-3 do
temp[i]:=s[i+1];
end;
Move(temp[0],buff[2],Len-2);
end;
PacketView(buff,Len,True);
case buff[2] of
$00:
LS_Init(buff,Len);
$0B:
LS_GGAuth(buff,Len);
$01:
LS_LoginFail(buff,Len);
$03:
LS_LoginOk(buff,Len);
$04:
LS_ServerList(buff,Len);
$06:
LS_PlayFail(buff,Len);
$07:
LS_PlayOk(buff,Len);
end;
end;
Порядок авторизации на Login-Server:
1) Init().................................................. .[OK] { Server->Client } Оффтоп
Код:
procedure TfrmMain.LS_Init(buff: array of byte; Len: integer);
begin
if Len<8 then exit;
Move(buff[3], SessionID, 4);
if Len>=155 then
begin
SetLength(RSA_key,128);
Move(buff[11],RSA_key[1],128); { RSA Key changed }
end;
if Len>=171 then
begin
SetLength(L2BF_key,16);
Move(buff[155],L2BF_key[1],16); { BlowFish Key changed }
L2BF.Init(L2BF_key);
end;
end;
{ sending RequestGGAuth packet }
RequestGGAuth();
procedure TfrmMain.RequestGGAuth;
var
Data: array[0..39] of byte;
begin
FillChar(data, Length(data), 0);
data[0]:=7;
Move(SessionID, data[1], 4);
AddCheckSumm(data, 24);
PacketSend(data, 40);
end;
3) LS_GGAuth().........................................[OK] { Server->Client } сервер ответил, что авторизация на GG прошла успешно Оффтоп
Код:
procedure TfrmMain.LS_GGAuth(buff: array of byte; Len: integer);
begin
if Len>=7 then
Move(buff[3], GGAuthResponse, 4);
RequestAuthLogin(Login, Password);
end;
procedure TfrmMain.RequestAuthLogin(_login, _password: string);
var
data: array[0..175] of byte;
crypt_text: string;
begin
FillChar(data,Length(data),0);
crypt_text := RSAEncrypt(_login, _password, RepairKey(RSA_key));
Move(crypt_text[1], data[1], 128);
Move(GGAuthResponse, data[129], 4);
data[149]:=8;
AddCheckSumm(Data, 160);
PacketSend(Data, 176);
end;
5) LoginOK().............................................[OK] { Server->Client } проверка Логина и Пароля прошла успешно, +Session_Key_1 Оффтоп
Код:
procedure TfrmMain.LS_LoginOk(buff: array of byte; Len: integer);
begin
if Len<12 then exit;
ToLog('[LS_LoginOk]: Login Successful!');
ToLog('');
Move(buff[3],SessionKey1_1,4);
Move(buff[7],SessionKey1_2,4);
RequestServerList();
end;
Далее описывать код пока не буду - до тех пунктов даже не доходит программа
7) ServerList()..........................................[NoNe] { Server->Client } передается список рабочих серверов
8) RequestServerLogin()............................[NoNe] { Client->Server } запрос игры на выбранном сервере
9) PlayOK()..............................................[NoNe] { Server->Client } подключение к Game-Server разрешено, +Session_Key_2
Лог выполнения программы - L2Connect_Log.txt
Вопросы:
1) Ну раз программа прошла GGAuth и LoginOK значит с шифрованием все нормально: иначе бы в методе LS_Parser программа не смогла бы определить тип пакета ( [LS_Parser]: buff[2]=0x0B ) и не продвинулась бы дальше по цепочке. Что не так в пакете RequestServerList? в упор не пойму, где я ошибся
Последний раз редактировалось Linux, 07.06.2016 в 17:42.
Linux, шифрование начинается с пакета RequestServerList
А конкретнее: какой алгоритм, какие параметры?
В этих двух статьях я не нашел ссылки, что пакет RequestServerList как-то по особому должен шифроваться
Какие из приведенных ниже методов из этих статей надо применить к пакету?
Вспомогательные методы - Шифрация пакетов GameServer (KyberPrizrak) Оффтоп
Код:
char key_sc[8];
char key_cs[8];
void Encrypt(char *raw, int size) {
int temp = 0;
for (int i=0; i<size; i++) {
int temp2 = raw[i] &0xff;
raw[i] = (char)(temp2 ^ (key_cs[i&7] &0xff) ^ temp);
temp = raw[i];
}
int old = key_cs[0] &0xff;
old |= key_cs[1] << 8 &0xff00;
old |= key_cs[2] << 0x10 &0xff0000;
old |= key_cs[3] << 0x18 &0xff000000;
old += size;
key_cs[0] = (char)(old &0xff);
key_cs[1] = (char)(old >> 0x08 &0xff);
key_cs[2] = (char)(old >> 0x10 &0xff);
key_cs[3] = (char)(old >> 0x18 &0xff);
}
void Decode(char *input, int size) {
int i = 0;
int j = 0;
for(int k=0; k < size; k++) {
int i1 = input[k] & 0xFF;
input[k] = (char)(i1 ^ key_sc[j++] & 0xFF ^i);
i = i1;
if(j>7) j=0;
}
long l = key_sc[0] & 0xFF;
l |= key_sc[1] << 8 & 0xFF00;
l |= key_sc[2] << 16 & 0xFF0000;
l |= key_sc[3] << 24 & 0xFF000000;
l += size;
key_sc[0] = (char)(l & 255L);
key_sc[1] = (char)(l >> 8 & 255L);
key_sc[2] = (char)(l >> 16 & 255L);
key_sc[3] = (char)(l >> 24 & 255L);
}
void Key(char* packet, int len) {
key_sc[0] = packet[4];
key_sc[1] = packet[5];
key_sc[2] = packet[6];
key_sc[3] = packet[7];
key_sc[4] = (char)0xA1;
key_sc[5] = (char)0x6C;
key_sc[6] = (char)0x54;
key_sc[7] = (char)0x87;
for(i=0;i<8;i++) {
key_cs[i] = key_sc[i];
}
}
// вычисляет чексумму и вставляет её в пакет
// как пользоватся: add_ckecksum(CelyjPaket+2, DlinaPaketa-(2+4+4));
void add_ckecksum(unsigned char *raw, int count) {
unsigned long chksum = 0;
unsigned long ecx;
int i = 0;
for(i = 0; i < count; i += 4) {
ecx = (unsigned long)raw[i];
ecx |= (unsigned long)raw[i + 1]<<8;
ecx |= (unsigned long)raw[i + 2]<<16;
ecx |= (unsigned long)raw[i + 3]<<24;
chksum ^= ecx;
}
memcpy(raw+count, (char *)&chksum, 4);
}
void RSAKeyDecode(char* RSAKey) {
for (int i = 0; i < 64; i++) {
RSAKey[i + 64] ^= RSAKey[i];
}
for (int i = 0; i < 4; i++) {
RSAKey[i + 13] ^= RSAKey[i + 52];
}
for (int i = 0; i < 64; i++) {
RSAKey[i] ^= RSAKey[i + 64];
}
for (int i = 0; i < 4; i++) {
BYTE tmp = RSAKey[i];
RSAKey[i] = RSAKey[i + 77];
RSAKey[i + 77] = tmp;
}
}
Вспомогательные методы - Шифрация пакетов GameServer (TechnoWiz@rd) Оффтоп
Код:
Каждый пакет состоит из размера пакета (2 байта), типа пакета (1 байт) и блока параметров (переменная длина). В дополнение к этому, в пакетах Login-Server, в конце добавляется контрольная сумма и дополняется нулями так, чтобы размер пакета был кратен 8-ми байтам. Контрольная сумма может быть рассчитана следующей функцией:
unsigned long checksum( unsigned char *packet, int count ) {
long chksum = 0L;
for( int i = 0; i < count; i += 4 )
chksum ^= *((unsigned long *)&raw[i]);
return chksum;
};
Все последующие пакеты будут шифроваться динамическим BlowFish ключом. Пакет LoginRequest дополнительно шифруется по алгоритму RSA. Ключ состоит из следующих частей: B = 1024, E = 65537, N = передается в пакете Init. Вместе эти 3 части составляют целый RSA ключ. Байты N в пакете зашифрованы функцией:
void scrambleMod( char *n ) {
typedef unsigned char byte;
int i;
for( i=0; i<4; i++ ) {
byte temp = n[0x00 + i];
n[0x00 + i] = n[0x4d + i];
n[0x4d + i] = temp;
};
// step 2 xor first 0x40 bytes with last 0x40 bytes
for( i=0; i<0x40; i++ ) {
n[i] = (byte)(n[i] ^ n[0x40 + i]);
};
// step 3 xor bytes 0x0d-0x10 with bytes 0x34-0x38
for( i=0; i<4; i++ ) {
n[0x0d + i] = (byte)(n[0x0d + i] ^ n[0x34 + i]);
};
// step 4 xor last 0x40 bytes with first 0x40 bytes
for( i=0; i<0x40; i++ ) {
n[0x40 + i] = (byte)(n[0x40 + i] ^ n[i]);
};
};
Для расшифровки можно воспользоваться следующей функцией:
void unscrambleMod( char *n )
{
typedef unsigned char byte;
int i;
// step 4 xor last 0x40 bytes with first 0x40 bytes
for( i=0; i<0x40; i++ ) {
n[0x40 + i] = (byte)(n[0x40 + i] ^ n[i]);
};
// step 3 xor bytes 0x0d-0x10 with bytes 0x34-0x38
for( i=0; i<4; i++ ) {
n[0x0d + i] = (byte)(n[0x0d + i] ^ n[0x34 + i]);
};
// step 2 xor first 0x40 bytes with last 0x40 bytes
for( i=0; i<0x40; i++ ) {
n[i] = (byte)(n[i] ^ n[0x40 + i]);
};
for( i=0; i<4; i++ ) {
byte temp = n[0x00 + i];
n[0x00 + i] = n[0x4d + i];
n[0x4d + i] = temp;
};
};
Для шифрования пакетов Game-Server используется алгоритм XOR. Ключ XOR генерируется случайно и передается клиенту в пакете CryptInit. Функции шифрования и дешифрации приведены ниже:
/* Декодирует данные */
void decrypt( unsigned char *data, unsigned int len, unsigned char *Key ){
int temp = 0;
for( unsigned int i = 0; i < len; ++i ) {
int temp2 = data[i] & 0xff;
data[i] = (temp2 ^ (Key[i & 15] & 0xff) ^ temp);
temp = temp2;
};
int old = Key[8] & 0xff;
old |= (Key[9] << 0x08) & 0xff00;
old |= (Key[10] << 0x10) & 0xff0000;
old |= (Key[11] << 0x18) & 0xff000000;
old += len;
Key[8] = old &0xff;
Key[9] = (old >> 0x08) & 0xff;
Key[10] = (old >> 0x10) & 0xff;
Key[11] = (old >> 0x18) & 0xff;
};
/* Кодирует данные */
void encrypt( unsigned char *data, unsigned int len, unsigned char *Key ){
int temp = 0;
for( unsigned int i = 0; i < len; i++) {
int temp2 = data[i] & 0xff;
data[i] = (temp2 ^ (Key[i & 15] & 0xff) ^ temp);
temp = data[i];
};
int old = Key[8] & 0xff;
old |= (Key[9] << 0x08) & 0xff00;
old |= (Key[10] << 0x10) & 0xff0000;
old |= (Key[11] << 0x18) & 0xff000000;
old += len;
Key[8] = old &0xff;
Key[9] = (old >> 0x08) & 0xff;
Key[10] = (old >> 0x10) & 0xff;
Key[11] = (old >> 0x18) & 0xff;
};
С каждым кодированным/декодированным пакетом ключ изменяется на длину пакета, поэтому нужно использовать два отдельных экземпляра ключа – один для шифрования исходящих пакетов, второй для расшифровки входящих.
Все пакеты шифруются начиная с 3-го байта, т.е. размер пакета никогда не шифруется.
Может ктото поделиться порядком подключения на ЛС/ГС по-байтно, что делается с каждым байтом?
Может кто поделиться рабочим примером кода подключения к ЛС/ГС? можно в личку если что
Последний раз редактировалось Linux, 07.06.2016 в 18:19.