Задумал я написать бота, и потребовались мне форматы пакетов. Гуглить было лень, поэтому я смотрел их прям в клиенте. Потом мне стало лень запускать ольку, и я написал прогу которая пишет пакеты с файл. Пока что прога перехватывает пакеты отправляемые клиентом на ГС, и перехватывает сетевые логи клиента.
Кусок лога программы
Код:
send: id=0E (cdbd)
00) c: 0E
01) d: 00000015
02) b: (size=0x100)
0000: 09 07 54 56 03 09 0B 01 07 02 54 54 56 07 00 02 | ..TV......TTV...
0010: 55 56 00 51 00 53 57 04 07 55 08 54 01 07 01 53 | UV.Q.SW..U.T...S
0020: 00 56 55 56 01 06 05 04 51 03 08 51 08 51 56 04 | .VUV....Q..Q.QV.
0030: 54 06 55 08 02 09 51 56 01 53 06 55 04 53 00 56 | T.U...QV.S.U.S.V
0040: 56 53 01 09 02 09 01 51 54 51 09 55 56 09 03 04 | VS.....QTQ.UV...
0050: 07 05 55 04 06 55 04 06 09 04 51 01 08 08 06 05 | ..U..U....Q.....
0060: 52 06 04 01 07 54 03 06 52 55 06 55 55 51 01 02 | R....T..RU.UUQ..
0070: 04 54 03 55 54 01 57 51 55 05 52 05 54 07 51 51 | .T.UT.WQU.R.T.QQ
0080: 55 07 02 53 53 00 52 05 52 07 01 54 00 03 05 05 | U..SS.R.R..T....
0090: 08 06 05 05 06 03 00 0D 08 01 07 09 03 51 03 07 | .............Q..
00A0: 53 09 51 06 07 54 0A 50 56 02 52 04 05 55 51 02 | S.Q..T.PV.R..UQ.
00B0: 53 00 08 54 04 52 56 06 02 09 00 08 03 53 56 01 | S..T.RV......SV.
00C0: 05 00 55 06 08 56 04 0D 06 07 52 06 07 04 0A 06 | ..U..V....R.....
00D0: 01 04 54 04 00 05 02 04 54 00 09 52 53 05 04 01 | ..T.....T..RS...
00E0: 04 05 05 01 52 51 52 0D 06 51 08 09 54 53 00 0D | ....RQR..Q..TS..
00F0: 01 02 03 54 53 01 05 03 08 56 54 07 02 54 0B 06 | ...TS....VT..T..
03) d: E63DEFEB
-end-
log: (Send)Send Protocol Version:21
log: (Receive)VersionCheck
send: id=2B (cSdddddQ)
00) c: 2B
01) S: "login"
02) d: 001340AC
03) d: 000048D2
04) d: 001340AC
05) d: 251F7A19
06) d: 87546CA1
07) Q: 0000001D7D98B543
-end-
log: (Send)C_LOGIN Packet
log: (Receive)CharacterSelectionInfo count:-1
send: id=12 (cdhddd)
00) c: 12
01) d: 00000000
02) h: 0000
03) d: 00000000
04) d: 00000000
05) d: 00000000
-end-
log: (Send)CharacterSelect
Как оно работает.
Для отправки пакетов клиент вызывает метод
cpp Код:
void __cdecl SomeObj::SendPacket(const char* format,...);
где SomeObj - какойто объект клиента (в разных версиях клиента - разный),
format - ASCII строка с форматом пакета, например "cddd",
... - переменный список параметров.
Например пакет NetPing отправляется так:
someobj.SendPacket("cddd",0xB1,ping_id,0,0x1800);
Таких методов несколько, 1-2 для ЛС, и один для ГС; их задача - собрать пакет в буфер, зашифровать, и отправить используя ws2_32.send().
Этот метод, как и все остальные методы связанные с сетью, живет в engine.dll, но не экспортируется. Поэтому чтобы его перехватить, надо либо хардкодить смещения в структурах, либо сразу захардкодить ее адрес в engine.dll. В любом случае код будет привязан к конкретной версии клиента. Так как SendPacket - это виртуальный метод, удобно его перехватывать подменяя его указатель в таблице виртуальных методов (ВМТ).
При каждой отправке и приеме пакета, клиент записывает сообщение в свой внутренний лог. Для этого он использует метод core.dll
cpp Код:
void __cdecl FOutputDevice::Logf(unsigned short const * format, ...)
синтаксис этой функции эквивалентен printf.
Таких функций несколько (Logf,Logf2,...), первая из них используется клиентом для логирования сетевых событий, другие для логирования ошибок типа "текстура ххх не найдена". Так как метод экспортируемый, то его адрес можно узнать для любого клиента, но проще перехватить его подменив адрес в таблице импорта, если engine.dll распакован.
Logf вызывается после SendPacket, и после обработки принятого пакета, поэтому например для NetPing будет такой порядок сообщений:
Код:
// принят пакет от ГС NetPingPacket
// вызвана функция обработки NetPingPacket
// вызван метод отправки UNetworkHandler::NetPing(int)
send: id=B1 (cddd) // вызвана SendPacket
00) c: B1
01) d: 4A38193F
02) d: 00000001
03) d: 00001800
-end-
log: (Send)NetPing // вызван Logf в методе отправки пакета UNetworkHandler::NetPing(int)
log: (Receive)NetPingPacket // вызван Logf в функции обработки NetPingPacket
Как получить адреса SendPacket и Logf.
Если использовать IDA - посмотреть откуда вызывается ws2_32.send(), это и будет SendPacket; затем надо посмотреть DATA XREF - это будет адрес указателя в ВМТ. Если использовать ollydbg - то все проще, надо поставить бряк на ws2_32.send(), и посмотреть кто его вызывает.
Адрес Logf можно взять прямо из таблицы экспорта core.dll, и перехватить его сплайсингом, но проще посмотреть в ИДА или ольке адрес указателя в таблице импорта engine.dll, и перехватывать указатель.
В аттаче - исходники (С++, компилятор MSVC2008) для сервера ла2 abyss, клиент - gracia2, engine.dll распакован.
Список файлов:
Код:
init.cpp // модуль инициализации длл, установки перехватов
installer.cpp // модуль цепляющий длл к клиенту
install.def // список экспортов для installer.cpp
SendDump.cpp //основной модуль дампа пакетов
SendDump.h // заголовочный файл SendDump.cpp
tools.h // вспомогательный модуль дампа буфера
SendPacketDump.vcproj //проект MSVC2008
Приветствуются любые разумные замечания, предложения, вопросы и т.п.
Не приветствуются вопросы типа "как это переписать на делфи", "что такое std::basic_ostream", "где взять адреса для С4".