PDA

Просмотр полной версии : Сериализация Пакета перед отправкой Lineage 2


0day
25.10.2020, 22:53
Не много погуглив, перерыв весь этот форум (возможно упустил что-то), касаемо получения правильного адреса функции SendPacket. Теперь хочу поэкспериментировать над сие чудом, и что собственно скрывается за капотом.

Сразу скажу, что я не гуру и не знаток C++, перейдя с Delphi на C++ у меня есть какой-то опыт работы с типами данных. И я не прошу Вас писать что-то за меня.

Взятый пример из форума функции SendPacket для использования.

void hSendPck(unsigned int This, char* Format, ...) {

unsigned char buf[1024];
int size = 0, len;
wchar_t* wstr;

va_list args;
va_start(args, Format);

while (*Format != 0)
{
switch (*Format)
{
case 'c':
*(unsigned char*)(buf + size) = va_arg(args, unsigned char);
size++;
break;
case 'h':
*(unsigned short int*) (buf + size) = va_arg(args, unsigned short int);
size += 2;
break;
case 'd':
*(unsigned int*)(buf + size) = va_arg(args, unsigned int);
size += 4;
break;
case 'Q':
*(unsigned __int64*)(buf + size) = va_arg(args, unsigned __int64);
size += 8;
break;
case 'b':
len = va_arg(args, unsigned int);
memcpy(buf + size, va_arg(args, void*), len);
size += len;
break;
case 'S':
wstr = va_arg(args, wchar_t*);
if (wstr == 0)
{
len = 2;
*(unsigned short int*) (buf + size) = 0;
}
else
{
len = wcslen(wstr) * 2 + 2;
memcpy(buf + size, wstr, len);
}
size += len;
break;

}
Format++;
}
va_end(args);

return (*true_SendPacket)(This, "b", size, (int)buf);
}


Нашёл пример из статьи о структуре и сериализации данных здесь https://beej.us/guide/bgnet/html/#serialization

Я увидел хороший пример из этого источника о создании пакета с помощью этого, как и моём случае с отправкой SendPacket.


unsigned int pack(unsigned char *buf, char *format, ...)
{
va_list ap;

signed char c; // 8-bit
unsigned char C;

int h; // 16-bit
unsigned int H;

long int l; // 32-bit
unsigned long int L;

long long int q; // 64-bit
unsigned long long int Q;

float f; // floats
double d;
long double g;
unsigned long long int fhold;

char *s; // strings
unsigned int len;

unsigned int size = 0;

va_start(ap, format);

for(; *format != '\0'; format++) {
switch(*format) {
case 'c': // 8-bit
size += 1;
c = (signed char)va_arg(ap, int); // promoted
*buf++ = c;
break;

case 'C': // 8-bit unsigned
size += 1;
C = (unsigned char)va_arg(ap, unsigned int); // promoted
*buf++ = C;
break;

case 'h': // 16-bit
size += 2;
h = va_arg(ap, int);
packi16(buf, h);
buf += 2;
break;

case 'H': // 16-bit unsigned
size += 2;
H = va_arg(ap, unsigned int);
packi16(buf, H);
buf += 2;
break;

case 'l': // 32-bit
size += 4;
l = va_arg(ap, long int);
packi32(buf, l);
buf += 4;
break;

case 'L': // 32-bit unsigned
size += 4;
L = va_arg(ap, unsigned long int);
packi32(buf, L);
buf += 4;
break;

case 'q': // 64-bit
size += 8;
q = va_arg(ap, long long int);
packi64(buf, q);
buf += 8;
break;

case 'Q': // 64-bit unsigned
size += 8;
Q = va_arg(ap, unsigned long long int);
packi64(buf, Q);
buf += 8;
break;

case 'f': // float-16
size += 2;
f = (float)va_arg(ap, double); // promoted
fhold = pack754_16(f); // convert to IEEE 754
packi16(buf, fhold);
buf += 2;
break;

case 'd': // float-32
size += 4;
d = va_arg(ap, double);
fhold = pack754_32(d); // convert to IEEE 754
packi32(buf, fhold);
buf += 4;
break;

case 'g': // float-64
size += 8;
g = va_arg(ap, long double);
fhold = pack754_64(g); // convert to IEEE 754
packi64(buf, fhold);
buf += 8;
break;

case 's': // string
s = va_arg(ap, char*);
len = strlen(s);
size += len + 2;
packi16(buf, len);
buf += 2;
memcpy(buf, s, len);
buf += len;
break;
}
}

va_end(ap);

return size;
}



Моя проблема в состоит в непонимании, где указывать ID пакета? Буду пробовать повторить пакет (взятый с L2PHX) по следующему примеру:
2F 2B 05 00 00 00 00 00 00 00

Тип: 0x2F (RequestMagicSkillUse)
Pазмер: 10+2
0002 d MagicID: 1323 (0x0000052B)
0006 d CtrlPressed: 0 (0x00000000)
0010 c ShiftPressed: 0 (0x00)


Я понимаю, что должен сделать приблизительно вот как?
Но в каком месте ID пакета указывать?

hSendPck(unh, "ddc", 1323, 0, 0);


PS: Прошу прощения за такие вопросы, укажите на ошибки/ что я пропустил.
Исключительно в образовательных целях :))

SeregaZ
25.10.2020, 23:36
пока ждем, что папки скажут... но у меня вопрос :) это получается ты посылаешь пакет путем использования какой-то функции? а напрямую сразу данные нельзя слать? типа выделяешь кусочек памяти, туда втуливаешь этот самый пакет как есть 2F 2B 05 00 00 00 00 00 00 00 и потом посылаешь.

0day
26.10.2020, 02:52
пока ждем, что папки скажут... но у меня вопрос :) это получается ты посылаешь пакет путем использования какой-то функции? а напрямую сразу данные нельзя слать? типа выделяешь кусочек памяти, туда втуливаешь этот самый пакет как есть 2F 2B 05 00 00 00 00 00 00 00 и потом посылаешь.

Пакет как есть можно отправить функцией send() из стандартного набора WinSock, предварительно хукнув её. Вариант этот сразу отпадает из-за необходимости шифрования пакет(а/ов) с помощью Blowfish (XOR/RSA), а так же поиск ключа для шифрования.

Если открыть L2 через IDA и поставить бряки на функции из класса UNetworkHandler можно невооруженным глазом увидеть вызовы SendPacket. Получить адрес функции, затем из своего кода шаманить с ней.

Вопрос лишь в правильной структуре и формата пакета, чтобы его принял сервер или клиент.

ScythLab
28.10.2020, 21:26
hSendPck(unh, "cddc", 0x2F, 1323, 0, 0);

Elecktron
10.11.2020, 08:36
зачем такой огород? что нужно? отправить пакет средствами клиента?
вот код:
MOV ECX,[UNetHandler]
mov ESI,ECX
MOV ECX,DWORD PTR DS:[ESI+0x48]
MOV EDX,DWORD PTR DS:[ECX]
PUSH answ
PUSH 0x43
PUSH mask //; ASCII "cd"
PUSH ECX
CALL [EDX+0x6c]
add esp,0x10

ScythLab
10.11.2020, 13:17
Elecktron, ну да, твой код конечно значительно проще, особенно если учесть:
- необходимость понимания ассемблера, чтобы разобраться с косяками
- могут быть исключения (как думаешь многие смогут понять почему и сам то сможешь ответить на этот вопрос?)
- могут быть исключения если в mask будет не 2 символа (тоже сильно сомневаюсь, что многие поймут почему)
- небольшая придирка: подходит для хроник GF+ (на C4 и IT не
сработает)

А так в целом твой вариант значительно проще :D

Elecktron
10.11.2020, 21:56
это код для ц4.
если человек захочет что-либо понять - он спросит.
и самое главное - мой код рабочий.

//01 - movebackwardtolocation
void __cdecl MoveBackwardToLocation(DWORD MoveToX,DWORD MoveToY,DWORD MoveToZ,DWORD CurrentX,DWORD CurrentY,DWORD CurrentZ)
{
char *mask = "cddddddd";
asm
{
mov ECX,[UNetworkHandler]
mov ESI,ECX
mov ECX,DWORD PTR DS:[ESI+0x48]
mov EDX,DWORD PTR DS:[ECX]
mov EAX,0x01
push EAX
mov EAX,CurrentZ
push EAX
mov EAX,CurrentY
push EAX
mov EAX,CurrentX
push EAX
mov EAX,MoveToZ
push EAX
mov EAX,MoveToY
push EAX
mov EAX,MoveToX
push EAX
push $1
push mask
push ECX
call DWORD PTR DS:[EDX+0x64]
add ESP,0x28
}

ScythLab
11.11.2020, 01:44
мой код рабочийЖаль только, что в твоем коде на кой-то ляд используется регистр ESI и ему не восстанавливается его изначальное значение, что в некоторых случаях МОЖЕТ привести к исключению или ошибках в других частях программы. Вот именно поэтому и нужно знать ассемблер, чтобы не писать код с так называемым "непредсказуемым поведением".
И ассемблер полезно использовать там, где в нем есть необходимость, а не бездумно пихать куда попало (особенно если до конца не понимаешь как он работает), да и твой код прекрасно записывается на с++:

#define PACK_MTL 0x01
void Init()
{
ClientSocket = *(DWORD*)((DWORD)UNetworkHandler + 0x48);
true_SendPacket = *(_SendPacket*)((*(DWORD*)ClientSocket) + SendPackOffset);
}

void MoveBackwardToLocation(DWORD MoveToX,DWORD MoveToY,DWORD MoveToZ,DWORD CurrentX,DWORD CurrentY,DWORD CurrentZ)
{
true_SendPacket(ClientSocket, "cddddddd", PACK_MTL, MoveToX, MoveToY, MoveToZ, CurrentX, CurrentY, CurrentZ, 1);
}

при этом в с++ варианте код реально рабочий, универсальный, не приводит к глупым ошибкам, когда у тебя меняется маска, да и явно выглядит попроще, неправда ли?