FreePVP)))
29.09.2012, 22:33
Доброго времени суток.
Из названия темы понятно, что речь пойдет о разработке OOG бота
1. Настройка сниффера
Прежде чем начать изучать трафик, надо видеть его структуру
Для этого будем использовать сниффер, перехватчик пакетов
Сниффер - Pandora's Box(сниффер на основе прокси-сервера)
Прокси-клиент - Любой, но я использую Proxifier
Скачать Pandora's Box ([Ссылки могут видеть только зарегистрированные и активированные пользователи]) | VT ([Ссылки могут видеть только зарегистрированные и активированные пользователи] 7e58f1f484adb7/analysis/1348921223/)
Options -> Proxy Settings
Add
Address = 127.0.0.1
Port = 30000 (по умолчанию используется в пандоре)
Protocol = SOCKS Version 4
Ok
Отмечаем галочкой созданную настройку
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
Options -> Proxification Rules
Выбираем второй кружочек
Add
Название выбираем удобное для вас.
Добавляем Application (указываем путь к elementclient.exe)
Port Range = 29000
Ok
Отмечаем галочкой созданную настройку
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
Процесс
Запускаем Pandora's Box
Включаем Proxy
Запускаем PW
2. Структура пакета
Все пакеты отправляются в формате:
CUInt32 Type;
CUInt32 Length;
BYTE[Length] Buffer;
Type - тип пакета(например: 0x01, 0x03)
Length - длина буфера с данными
Buffer - данные(например логин и хеш логина с паролем)
internal uint ReadCUInt32()
{
byte code = ReadByte();
switch (code & 0xE0)
{
case 0xE0:
return BitConverter.ToUInt32(ReadArray(4, true), 0);
case 0xC0:
byte[] bt = ReadArray(3, true);
return BitConverter.ToUInt32(new byte[] { bt[2], bt[1], bt[0], code }, 0) & 0x1FFFFFFF;
case 0x80:
case 0xA0:
return (uint)(BitConverter.ToUInt16(new byte[] { ReadByte(), code }, 0) & 0x3FFF);
}
return (uint)code;
}
internal DataStream WriteCUInt32(uint value)
{
if (value <= 0x7F)
{
WriteByte((byte)value);
return this;
}
if (value <= 0x3FFF)
{
byte[] bt = BitConverter.GetBytes((ushort)(value + 0x8000));
WriteArray(bt, true);
return this;
}
if (value <= 0x1FFFFFFF)
{
byte[] bt = BitConverter.GetBytes((uint)(value + 0xC0000000));
WriteArray(bt, true);
return this;
}
if (value <= 0xFFFFFFFF)
{
List<byte> bt = new List<byte>();
bt.Add(0xE0);
byte[] arrbt = BitConverter.GetBytes((uint)value);
bt.AddRange(arrbt);
WriteArray(bt.ToArray(), true);
return this;
}
return this;
}
3. "Упаковка, шифрация, создание ключей" или "Входим на аккаунт"
Как известно, все серверные пакеты после 0x02 S2C(Server to Client) шифруются и пакуются, а клиентские пакеты только шифруются
Для шифрации используется алгоритм RC4 ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
Для упаковки - MPPC ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
using System;
namespace OOGLibrary.Cryptography
{
class RC4
{
public RC4()
{
for (int i = 0; i < 256; i++)
m_Table[i] = Convert.ToByte(i);
m_Shift1 = 0;
m_Shift2 = 0;
}
public void Shuffle(byte[] Key)
{
byte Shift = 0;
for (int i = 0; i < 256; i++)
{
byte A = Key[i % 16];
Shift += (byte)(A + m_Table[i]);
byte B = m_Table[i];
m_Table[i] = m_Table[Shift];
m_Table[Shift] = B;
}
}
public byte Encode(byte InPacket)
{
m_Shift1++;
byte A = m_Table[m_Shift1];
m_Shift2 += A;
byte B = m_Table[m_Shift2];
m_Table[m_Shift2] = A;
m_Table[m_Shift1] = B;
byte C = (byte)(A + B);
byte D = m_Table[C];
return (byte)(InPacket ^ D);
}
private byte m_Shift1;
private byte m_Shift2;
private byte[] m_Table = new byte[256];
}
}
using System;
using System.Collections.Generic;
namespace OOGLibrary.Cryptography
{
class Unpack
{
UInt32 m_Code1;
UInt32 m_Code2;
UInt32 m_Stage;
UInt32 m_Shift;
byte m_PackedOffset;
readonly List<byte> m_Packed = new List<byte>();
readonly List<byte> m_Unpacked = new List<byte>();
public Unpack()
{
m_PackedOffset = 0;
m_Stage = 0;
}
bool HasBits(UInt32 Count)
{
return (m_Packed.Count * 8 - m_PackedOffset) >= Count;
}
public List<byte> AddByte(byte InB)
{
m_Packed.Add(InB);
var UnpackedChunk = new List<byte>();
for (; ; )
{
if (m_Stage == 0)
{
if (HasBits(4))
{
if (GetPackedBits(1) == 0)
{
// 0-xxxxxxx
m_Code1 = 1;
m_Stage = 1;
continue;
}
else
{
if (GetPackedBits(1) == 0)
{
// 10-xxxxxxx
m_Code1 = 2;
m_Stage = 1;
continue;
}
else
{
if (GetPackedBits(1) == 0)
{
// 110-xxxxxxxxxxxxx-*
m_Code1 = 3;
m_Stage = 1;
continue;
}
else
{
if (GetPackedBits(1) == 0)
{
// 1110-xxxxxxxx-*
m_Code1 = 4;
m_Stage = 1;
continue;
}
else
{
// 1111-xxxxxx-*
m_Code1 = 5;
m_Stage = 1;
continue;
}
}
}
}
}
else
break;
}
else if (m_Stage == 1)
{
if (m_Code1 == 1)
{
if (HasBits(7))
{
byte OutB = (byte)(GetPackedBits(7));
UnpackedChunk.Add(OutB);
m_Unpacked.Add(OutB);
m_Stage = 0;
continue;
}
else
break;
}
else if (m_Code1 == 2)
{
if (HasBits(7))
{
byte OutB = (byte)((GetPackedBits(7)) | 0x80);
UnpackedChunk.Add(OutB);
m_Unpacked.Add(OutB);
m_Stage = 0;
continue;
}
else
break;
}
else if (m_Code1 == 3)
{
if (HasBits(13))
{
m_Shift = GetPackedBits(13) + 0x140;
m_Stage = 2;
continue;
}
else
break;
}
else if (m_Code1 == 4)
{
if (HasBits(8))
{
m_Shift = GetPackedBits(8) + 0x40;
m_Stage = 2;
continue;
}
else
break;
}
else if (m_Code1 == 5)
{
if (HasBits(6))
{
m_Shift = GetPackedBits(6);
m_Stage = 2;
continue;
}
else
break;
}
}
else if (m_Stage == 2)
{
if (m_Shift == 0)
{
// Guess !!!
if (m_PackedOffset != 0)
{
m_PackedOffset = 0;
//m_Packed.PopFront();
m_Packed.RemoveAt(0);
}
m_Stage = 0;
continue;
}
m_Code2 = 0;
m_Stage = 3;
continue;
}
else if (m_Stage == 3)
{
if (HasBits(1))
{
if (GetPackedBits(1) == 0)
{
m_Stage = 4;
continue;
}
else
{
m_Code2++;
continue;
}
}
else
break;
}
else if (m_Stage == 4)
{
UInt32 CopySize = 0;
if (m_Code2 == 0)
CopySize = 3;
else
{
UInt32 Sz = m_Code2 + 1;
if (HasBits(Sz))
CopySize = GetPackedBits(Sz) + (UInt32)(1 << ((Int32)Sz));
else
break;
}
Copy(m_Shift, CopySize, ref UnpackedChunk);
m_Stage = 0;
continue;
}
}
return UnpackedChunk;
}
void Notify(string Msg)
{
//Log.L(Msg);
}
void Copy(UInt32 Shift, UInt32 Size, ref List<byte> UnpackedChunk)
{
for (UInt32 i = 0; i < Size; i++)
{
var PIndex = m_Unpacked.Count - Shift;
if (PIndex < 0)
Notify("Unpack error");
else
{
byte B = m_Unpacked[(Int32)PIndex];
m_Unpacked.Add(B);
UnpackedChunk.Add(B);
}
}
}
UInt32 GetPackedBits(UInt32 BitCount)
{
if (BitCount > 16)
return 0;
if (!HasBits(BitCount))
Notify("Unpack bit stream overflow");
UInt32 AlBitCount = BitCount + m_PackedOffset;
UInt32 AlByteCount = (AlBitCount + 7) / 8;
UInt32 V = 0;
for (UInt32 i = 0; i < AlByteCount; i++)
V |= (UInt32)(m_Packed[(Int32)i]) << (Int32)(24 - i * 8);
V <<= m_PackedOffset;
V >>= (Int32)(32 - BitCount);
m_PackedOffset += (byte)BitCount;
Int32 FreeBytes = m_PackedOffset / 8;
if (FreeBytes != 0)
{
//m_Packed.PopFront(FreeBytes);
m_Packed.RemoveRange(0, FreeBytes);
}
m_PackedOffset %= 8;
return V;
}
}
}
Теперь по порядку
S2C <- 0x01 ServerInfo
C2S -> 0x03 LogginAnnounce
S2C <- 0x02 SMKey
C2S -> 0x02 CMKey
byte KeyLen;
byte[KeyLen] Key;
byte AuthType;
byte[4] ServerVersion;
byte Bonus;
// Интересный факт: в ключе (Key) хранятся данные о бонусах и нагрузке на сервер
Key - ключ, который мы будем использовать для генерации хеша
byte LoginLength;
AString Login;
byte HashLen;
byte[HashLen] Hash;
byte 0x00;
Login - Логин, как вы уже поняли
Hash - хеш в формате HMACMD5(Key){MD5{login+password}};
Hash и Login так же используются при создании ключей RC4
byte EncHashLen;
byte[EncKeyLen] EncHash;
byte Force;
0x02 SMKey - тот самый пакет, после которого клиент начинает шифровать трафик, а сервер паковать алгоритмом MPPC и шифровать
EncHash - хеш, который нужен для создания ключа для RC4, который в свою очередь используется для шифрации трафика
Этот ключ генерируется следующим образом - HMACMD5(Login){Hash + EncHash}
Hash - тот самый хеш из 0x03 LogginAnnounce
byte DecHashLen;
byte[DecKeyLen] DecHash;
byte Force;
DecHash - Рандомный хеш, с помощью которого, как я уже описывал чуть выше, генерируется ключ для RC4, которым клиент будет расшифровывать трафик
Force - флаг(Усиленный вход)
Этот пакет, как и все последующие, шифруется
Задавайте вопросы, пишите замечания
Скачать Pandora's Box ([Ссылки могут видеть только зарегистрированные и активированные пользователи]) | VT ([Ссылки могут видеть только зарегистрированные и активированные пользователи] 7e58f1f484adb7/analysis/1348921223/)
Скачать PacketDeclaration.xml ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
Из названия темы понятно, что речь пойдет о разработке OOG бота
1. Настройка сниффера
Прежде чем начать изучать трафик, надо видеть его структуру
Для этого будем использовать сниффер, перехватчик пакетов
Сниффер - Pandora's Box(сниффер на основе прокси-сервера)
Прокси-клиент - Любой, но я использую Proxifier
Скачать Pandora's Box ([Ссылки могут видеть только зарегистрированные и активированные пользователи]) | VT ([Ссылки могут видеть только зарегистрированные и активированные пользователи] 7e58f1f484adb7/analysis/1348921223/)
Options -> Proxy Settings
Add
Address = 127.0.0.1
Port = 30000 (по умолчанию используется в пандоре)
Protocol = SOCKS Version 4
Ok
Отмечаем галочкой созданную настройку
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
Options -> Proxification Rules
Выбираем второй кружочек
Add
Название выбираем удобное для вас.
Добавляем Application (указываем путь к elementclient.exe)
Port Range = 29000
Ok
Отмечаем галочкой созданную настройку
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
Процесс
Запускаем Pandora's Box
Включаем Proxy
Запускаем PW
2. Структура пакета
Все пакеты отправляются в формате:
CUInt32 Type;
CUInt32 Length;
BYTE[Length] Buffer;
Type - тип пакета(например: 0x01, 0x03)
Length - длина буфера с данными
Buffer - данные(например логин и хеш логина с паролем)
internal uint ReadCUInt32()
{
byte code = ReadByte();
switch (code & 0xE0)
{
case 0xE0:
return BitConverter.ToUInt32(ReadArray(4, true), 0);
case 0xC0:
byte[] bt = ReadArray(3, true);
return BitConverter.ToUInt32(new byte[] { bt[2], bt[1], bt[0], code }, 0) & 0x1FFFFFFF;
case 0x80:
case 0xA0:
return (uint)(BitConverter.ToUInt16(new byte[] { ReadByte(), code }, 0) & 0x3FFF);
}
return (uint)code;
}
internal DataStream WriteCUInt32(uint value)
{
if (value <= 0x7F)
{
WriteByte((byte)value);
return this;
}
if (value <= 0x3FFF)
{
byte[] bt = BitConverter.GetBytes((ushort)(value + 0x8000));
WriteArray(bt, true);
return this;
}
if (value <= 0x1FFFFFFF)
{
byte[] bt = BitConverter.GetBytes((uint)(value + 0xC0000000));
WriteArray(bt, true);
return this;
}
if (value <= 0xFFFFFFFF)
{
List<byte> bt = new List<byte>();
bt.Add(0xE0);
byte[] arrbt = BitConverter.GetBytes((uint)value);
bt.AddRange(arrbt);
WriteArray(bt.ToArray(), true);
return this;
}
return this;
}
3. "Упаковка, шифрация, создание ключей" или "Входим на аккаунт"
Как известно, все серверные пакеты после 0x02 S2C(Server to Client) шифруются и пакуются, а клиентские пакеты только шифруются
Для шифрации используется алгоритм RC4 ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
Для упаковки - MPPC ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
using System;
namespace OOGLibrary.Cryptography
{
class RC4
{
public RC4()
{
for (int i = 0; i < 256; i++)
m_Table[i] = Convert.ToByte(i);
m_Shift1 = 0;
m_Shift2 = 0;
}
public void Shuffle(byte[] Key)
{
byte Shift = 0;
for (int i = 0; i < 256; i++)
{
byte A = Key[i % 16];
Shift += (byte)(A + m_Table[i]);
byte B = m_Table[i];
m_Table[i] = m_Table[Shift];
m_Table[Shift] = B;
}
}
public byte Encode(byte InPacket)
{
m_Shift1++;
byte A = m_Table[m_Shift1];
m_Shift2 += A;
byte B = m_Table[m_Shift2];
m_Table[m_Shift2] = A;
m_Table[m_Shift1] = B;
byte C = (byte)(A + B);
byte D = m_Table[C];
return (byte)(InPacket ^ D);
}
private byte m_Shift1;
private byte m_Shift2;
private byte[] m_Table = new byte[256];
}
}
using System;
using System.Collections.Generic;
namespace OOGLibrary.Cryptography
{
class Unpack
{
UInt32 m_Code1;
UInt32 m_Code2;
UInt32 m_Stage;
UInt32 m_Shift;
byte m_PackedOffset;
readonly List<byte> m_Packed = new List<byte>();
readonly List<byte> m_Unpacked = new List<byte>();
public Unpack()
{
m_PackedOffset = 0;
m_Stage = 0;
}
bool HasBits(UInt32 Count)
{
return (m_Packed.Count * 8 - m_PackedOffset) >= Count;
}
public List<byte> AddByte(byte InB)
{
m_Packed.Add(InB);
var UnpackedChunk = new List<byte>();
for (; ; )
{
if (m_Stage == 0)
{
if (HasBits(4))
{
if (GetPackedBits(1) == 0)
{
// 0-xxxxxxx
m_Code1 = 1;
m_Stage = 1;
continue;
}
else
{
if (GetPackedBits(1) == 0)
{
// 10-xxxxxxx
m_Code1 = 2;
m_Stage = 1;
continue;
}
else
{
if (GetPackedBits(1) == 0)
{
// 110-xxxxxxxxxxxxx-*
m_Code1 = 3;
m_Stage = 1;
continue;
}
else
{
if (GetPackedBits(1) == 0)
{
// 1110-xxxxxxxx-*
m_Code1 = 4;
m_Stage = 1;
continue;
}
else
{
// 1111-xxxxxx-*
m_Code1 = 5;
m_Stage = 1;
continue;
}
}
}
}
}
else
break;
}
else if (m_Stage == 1)
{
if (m_Code1 == 1)
{
if (HasBits(7))
{
byte OutB = (byte)(GetPackedBits(7));
UnpackedChunk.Add(OutB);
m_Unpacked.Add(OutB);
m_Stage = 0;
continue;
}
else
break;
}
else if (m_Code1 == 2)
{
if (HasBits(7))
{
byte OutB = (byte)((GetPackedBits(7)) | 0x80);
UnpackedChunk.Add(OutB);
m_Unpacked.Add(OutB);
m_Stage = 0;
continue;
}
else
break;
}
else if (m_Code1 == 3)
{
if (HasBits(13))
{
m_Shift = GetPackedBits(13) + 0x140;
m_Stage = 2;
continue;
}
else
break;
}
else if (m_Code1 == 4)
{
if (HasBits(8))
{
m_Shift = GetPackedBits(8) + 0x40;
m_Stage = 2;
continue;
}
else
break;
}
else if (m_Code1 == 5)
{
if (HasBits(6))
{
m_Shift = GetPackedBits(6);
m_Stage = 2;
continue;
}
else
break;
}
}
else if (m_Stage == 2)
{
if (m_Shift == 0)
{
// Guess !!!
if (m_PackedOffset != 0)
{
m_PackedOffset = 0;
//m_Packed.PopFront();
m_Packed.RemoveAt(0);
}
m_Stage = 0;
continue;
}
m_Code2 = 0;
m_Stage = 3;
continue;
}
else if (m_Stage == 3)
{
if (HasBits(1))
{
if (GetPackedBits(1) == 0)
{
m_Stage = 4;
continue;
}
else
{
m_Code2++;
continue;
}
}
else
break;
}
else if (m_Stage == 4)
{
UInt32 CopySize = 0;
if (m_Code2 == 0)
CopySize = 3;
else
{
UInt32 Sz = m_Code2 + 1;
if (HasBits(Sz))
CopySize = GetPackedBits(Sz) + (UInt32)(1 << ((Int32)Sz));
else
break;
}
Copy(m_Shift, CopySize, ref UnpackedChunk);
m_Stage = 0;
continue;
}
}
return UnpackedChunk;
}
void Notify(string Msg)
{
//Log.L(Msg);
}
void Copy(UInt32 Shift, UInt32 Size, ref List<byte> UnpackedChunk)
{
for (UInt32 i = 0; i < Size; i++)
{
var PIndex = m_Unpacked.Count - Shift;
if (PIndex < 0)
Notify("Unpack error");
else
{
byte B = m_Unpacked[(Int32)PIndex];
m_Unpacked.Add(B);
UnpackedChunk.Add(B);
}
}
}
UInt32 GetPackedBits(UInt32 BitCount)
{
if (BitCount > 16)
return 0;
if (!HasBits(BitCount))
Notify("Unpack bit stream overflow");
UInt32 AlBitCount = BitCount + m_PackedOffset;
UInt32 AlByteCount = (AlBitCount + 7) / 8;
UInt32 V = 0;
for (UInt32 i = 0; i < AlByteCount; i++)
V |= (UInt32)(m_Packed[(Int32)i]) << (Int32)(24 - i * 8);
V <<= m_PackedOffset;
V >>= (Int32)(32 - BitCount);
m_PackedOffset += (byte)BitCount;
Int32 FreeBytes = m_PackedOffset / 8;
if (FreeBytes != 0)
{
//m_Packed.PopFront(FreeBytes);
m_Packed.RemoveRange(0, FreeBytes);
}
m_PackedOffset %= 8;
return V;
}
}
}
Теперь по порядку
S2C <- 0x01 ServerInfo
C2S -> 0x03 LogginAnnounce
S2C <- 0x02 SMKey
C2S -> 0x02 CMKey
byte KeyLen;
byte[KeyLen] Key;
byte AuthType;
byte[4] ServerVersion;
byte Bonus;
// Интересный факт: в ключе (Key) хранятся данные о бонусах и нагрузке на сервер
Key - ключ, который мы будем использовать для генерации хеша
byte LoginLength;
AString Login;
byte HashLen;
byte[HashLen] Hash;
byte 0x00;
Login - Логин, как вы уже поняли
Hash - хеш в формате HMACMD5(Key){MD5{login+password}};
Hash и Login так же используются при создании ключей RC4
byte EncHashLen;
byte[EncKeyLen] EncHash;
byte Force;
0x02 SMKey - тот самый пакет, после которого клиент начинает шифровать трафик, а сервер паковать алгоритмом MPPC и шифровать
EncHash - хеш, который нужен для создания ключа для RC4, который в свою очередь используется для шифрации трафика
Этот ключ генерируется следующим образом - HMACMD5(Login){Hash + EncHash}
Hash - тот самый хеш из 0x03 LogginAnnounce
byte DecHashLen;
byte[DecKeyLen] DecHash;
byte Force;
DecHash - Рандомный хеш, с помощью которого, как я уже описывал чуть выше, генерируется ключ для RC4, которым клиент будет расшифровывать трафик
Force - флаг(Усиленный вход)
Этот пакет, как и все последующие, шифруется
Задавайте вопросы, пишите замечания
Скачать Pandora's Box ([Ссылки могут видеть только зарегистрированные и активированные пользователи]) | VT ([Ссылки могут видеть только зарегистрированные и активированные пользователи] 7e58f1f484adb7/analysis/1348921223/)
Скачать PacketDeclaration.xml ([Ссылки могут видеть только зарегистрированные и активированные пользователи])