l2-unlegits/docs/demo_la2endecGS4m2.fsc
alexey.min 043d61633e Docs
2012-02-06 09:14:22 +00:00

374 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 'demo_la2-endecGS-4m.fsc' Пример ручного декодирования LA2 GS пакетов. Стандартный алгоритм.
// внутреннее автоопределение и автодекодирование НЕ ИСПОЛЬЗУЕТСЯ, оно отключается (в скрипте).
//
// определение трафика и де/кодирование GS полностью на скрипте.
//
// используются события-функции 'OnDeCode' и 'OnEnCode'
//
// (для работы скрипта необходим WP507F или выше)
// в примере используется ручная де/кодировка GS пакетов C4/C5/Interlude стандартным алгоритмом.
// скрипт FS
//
// 'demo_la2-endecGS-4m.fsc' это модификация скрипта 'demo_la2-endecGS-4.fsc'
// добавлено определение Камаэль в GS3 (для WP507F надо скачать файлы определения)
//
var // будем использовать локальные ключи, а не внутренние (хотя можно любые, без разницы)
Kci : string;// - для пакетов пришедших с клиента.
Kco : string;// - для пакетов уходящих на клиент.
Ksi : string;// - для пакетов пришедших с сервера.
Kso : string;// - для пакетов уходящих на сервер.
s,s1 : string;
scode : boolean;
le : integer;
ID : integer;
CharName : string;
_seed : integer; // random generator seed for mixing id tables
_1_byte_table : string;
_2_byte_table : string;
_id_mix : boolean;
temp_seed : integer;
Procedure OnCreate; // функция-событие на старт скрипта.
// эта ф-ция вызывается автоматически при старте скрипта (еще до первого пакета)
// ф-ция не обязательна и может отсутствовать в скриптах ...
begin
//
// Поотключаем все АВТОопределения и АВТОдекодирования:
//
gSys.TrafType := 0; // отключим внутреннее автоопределение трафика
gSys.tPDecode := 0; // отключим внутреннее автодекодирование
gSys.tKeyType := 0; // отключим начальное автоопределение ключа
gSys.Protocol := -1; // протокол соединения (у LS и GS разные ! обычно говорят о протоколе GS)
gSys.isGS := 0;
gSys.isLS := 0;
gSys.tParse := 2; // тип парсинга пакетов (двухбайтовый размер в начале пакета)
//
scode := false; // флаг де/кодирования GS
Kci := ''; Kco := ''; Ksi := ''; Kso := '';
//
CharName := '';
_id_mix := false;
end;
Procedure OnDestroy; // функция-событие на закрытие скрипта и соединения.
// эта ф-ция вызывается автоматически при закрытие соединения
// ф-ция не обязательна и может отсутствовать в скриптах ...
begin
end;
procedure _DecGS(var bf : string;var ke : string; tk : integer); // декодируем пакет GS, это обычная ф-ция
var i,j,b,i1,sz,sk : integer;
begin
// стандартный алгоритм XOR декодирования GS la2
sz := length(bf);
sk := length(ke);
if sk = 0 then exit;
b := 0; j := 1;
for i := 3 to sz do begin
i1 := ord(bf[i]);
bf[i] := chr(i1 xor ord(ke[j]) xor b);
b := i1;
Inc(j,1);
if j > sk then j := 1;
end;
case tk of
1: PInt(ke,GInt(ke,1,4)+sz-2,1,4);
2: PInt(ke,GInt(ke,1,8)+sz-2,1,8);
3: PInt(ke,GInt(ke,9,8)+sz-2,9,8);
4: PInt(ke,GInt(ke,9,8)+sz-2,9,8);
end;
end;
procedure _EncGS(var bf : string;var ke : string; tk : integer); // кодируем пакет GS, это обычная ф-ция
var i,j,b,i1,sz,sk : integer;
begin
// стандартный алгоритм XOR кодирования GS la2
sz := length(bf);
sk := length(ke);
if sk = 0 then exit;
i1 := 0; j := 1;
for i := 3 to sz do begin
bf[i] := chr(ord(bf[i]) xor ord(ke[j]) xor i1);
i1 := ord(bf[i]);
Inc(j,1);
if j > sk then j := 1;
end;
case tk of
1: PInt(ke,GInt(ke,1,4)+sz-2,1,4);
2: PInt(ke,GInt(ke,1,8)+sz-2,1,8);
3: PInt(ke,GInt(ke,9,8)+sz-2,9,8);
4: PInt(ke,GInt(ke,9,8)+sz-2,9,8);
end;
end;
procedure _pseudo_srand(seed : integer);
begin
_seed := seed;
end;
function _pseudo_rand: integer;
var
a : integer;
begin
a := (Int64(_seed) * $343fd + $269EC3) and $FFFFFFFF;
_seed := a;
result := (_seed shr $10) and $7FFF;
// writelogln(format('rand = %x; seed = %x',[result, _seed]));
end;
procedure _init_tables(seed: integer);
var
i : integer;
x : byte;
rand_pos : integer;
cur_pos : integer;
begin
_1_byte_table := '';
_2_byte_table := '';
for i := 0 to $D0 do begin
_1_byte_table := _1_byte_table + chr(i);
end;
for i := 0 to $4D do begin
_2_byte_table := _2_byte_table + chr(i) + #$0;
end;
_pseudo_srand(seed);
for i := 2 to $D1 do begin
rand_pos := (_pseudo_rand mod i) + 1;
x := GInt(_1_byte_table, rand_pos, 1);
PInt(_1_byte_table, GInt(_1_byte_table, i, 1), rand_pos, 1);
PInt(_1_byte_table, x, i, 1);
end;
cur_pos := 3;
for i := 2 to $4E do begin
rand_pos := _pseudo_rand mod i;
x := GInt(_2_byte_table, rand_pos * 2 + 1 , 2);
PInt(_2_byte_table, GInt(_2_byte_table, cur_pos, 2), rand_pos * 2 + 1, 2);
PInt(_2_byte_table, x, cur_pos, 2);
cur_pos := cur_pos + 2;
end;
cur_pos := Pos(#$12, _1_byte_table);
x := GInt(_1_byte_table, $13, 1);
PInt(_1_byte_table, $12, $13, 1);
PInt(_1_byte_table, x, cur_pos, 1);
cur_pos := Pos(#$B1, _1_byte_table);
x := GInt(_1_byte_table, $B2, 1);
PInt(_1_byte_table, $B1, $B2, 1);
PInt(_1_byte_table, x, cur_pos, 1);
end;
procedure _decode_ID(var buff:string);
var
p: integer;
begin
// writelogln(format('changing %x -> %x',[GInt(buff, 3, 1),GInt(_1_byte_table, GInt(buff, 3, 1)+1, 1) ]));
PInt(buff, GInt(_1_byte_table, GInt(buff, 3, 1) + 1, 1) , 3, 1);
if GInt(buff, 3, 1) = $D0 then begin
PInt(buff, GInt(_2_byte_table, GInt(buff, 4, 1) * 2 + 1, 1) , 4, 1);
end;
end;
procedure _encode_ID(var buff:string);
var
p: integer;
begin
if GInt(buff, 3, 1) = $D0 then begin
p:= pos(Chr(GInt(buff, 4, 1)), _2_byte_table);
PInt(buff, ((p + 1) shr 1) - 1, 4, 1);
end;
p := pos(Chr(GInt(buff, 3, 1)), _1_byte_table);
// writelogln(format('changing %x -> %x',[GInt(buff, 3, 1), p-1]));
PInt(buff, p-1, 3, 1);
end;
Procedure Trafic_Detector;
var _s : string;
begin
// Определим тип трафика и тип сервера.
// Внимание! при необходимости, определение трафика можно дополнить новыми вариантами =)
//
le := length(_dBuff); // _dBuff - входной пакет (так как эта ф-ция вызвана из OnDeCode)
//
// Определим тип трафика (в этом примере нас интересует только LA2)
//
if (gSys.TrafType = 0) and (_gAbsNumPkt = 1) then begin
case le of
$0B,$9B,$AB,$BA: begin
gSys.TrafType := 2; // LA2
gSys.isLS := 1; // LS
if le = $0B then gSys.tKeyType := 1 else gSys.tKeyType := 2; // это для LS, пока не потребуется
end;
$107: begin
gSys.TrafType := 2; // LA2
gSys.isGS := 1; // GS
end;
$07: begin
gSys.TrafType := 2; // LA2
gSys.isGS := 2; // GS
end;
else if le >= $107 then begin // более надежный способ определить LA2 GS
if pos(HStr('05 52 05 54 07 51 51 55 07 02 53 53 00 52 05 52'),_dBuff) <> 0 then begin
// если найдена строчка '.R.T.QQU..SS.R.R' (древний рим епть =) тогда это гейм сервер ла2.
gSys.TrafType := 2; // LA2
gSys.isGS := 2; // GS
end;
if StrCmp(copy(_dBuff, 3, 5), HStr('0E 3C 03 00 00')) then begin
writelogln('CT1 traffic detected');
gSys.TrafType := 2; // LA2
gSys.isGS := 3; // GS CT1
end;
end;
end;
//
if gSys.TrafType = 2 then gSys.SetIDposlen(3,1); // ID в пакетах LA2 находится в третьем байте, размер 1 байт.
if gSys.isGS > 0 then gSys.Protocol := gInt(_dBuff,4,4); // берем номер протокола GS
// продублим протоколы CT1
if gSys.Protocol >= 828 then gSys.isGS := 3; // GS CT1
// GS CT1+ пока такой же
end;
//
// определим ключ GS:
//
if (_gAbsNumPkt = 2) then
if (gSys.TrafType = 2) and (gSys.isGS > 0) and _dFromServ then begin
if (le = 12) then gSys.tKeyType := 1; // 12 байтный пакет - тип 1. GSDecodeJ (90% l2j)
if (le = 16) then gSys.tKeyType := 2; // 16 байтный пакет - тип 2. GSDecode (sub-off servers)
if (le = $1A) then gSys.tKeyType := 3; // часть фри серверов Interlude
if (le = $1C) then gSys.tKeyType := 3; // часть фри серверов Interlude
if (le = $15) then gSys.tKeyType := 3; // официальные сервера Interlude (на данный момент)
if (le = $19) then gSys.tKeyType := 4; // официальные сервера CT1+
if gSys.tKeyType > 0 then begin
s := copy(_dBuff,5,16);
if gSys.tKeyType < 3 then s := copy(s,1,4)+Hstr('A1 6C 54 87'); // C4/C5/псевдо интерлюд.
if gSys.tKeyType = 3 then s := copy(s,1,8)+Hstr('C8 27 93 01 A1 6C 31 97'); // интерлюд.
if gSys.tKeyType = 4 then begin
s := copy(s,1,8)+Hstr('C8 27 93 01 A1 6C 31 97');
_id_mix := true;
// ScriptTimeOut(30000);
_init_tables(GInt(_dBuff, $16, 4));
writelogln('kamael+ trafic detected');
// writelog('one byte table');
// writelogHexB(_1_byte_table);
// writelog('two byte table');
// writelogHexB(_2_byte_table);
// gSys.sleep(10000);
// ScriptTimeOut(1000);
end;
Kci := s; Kco := s; Ksi := s; Kso := s;
scode := true; // запустим де/кодирование GS
writelogln('key:');writeloghexb(s);
end else writelogln('неопознанный ключевой пакет GS LA2 протокола')
end;
//
end;
Procedure OnDeCode; // функция-событие на декодирование пакета.
// эта ф-ция вызывается при получении пакета, ДО автоматического декодирования (если включено) и ДО записей в автологгер основных пакетов
// ф-ция не обязательна и может отсутствовать в скриптах ...
// входной буфер '_dBuff', выходной '_dOutBuff', флаг направления '_dFromServ' (не перепутайте)
begin
//writelogln('OnDeCode ..');
// _dBuff - входной пакет для декодирования
if _gAbsNumPkt < 6 then Trafic_Detector; // определим трафик и тип сервера (в пределах 5 первых пакетов, для LA2 этого хватит)
//
if scode and (_gAbsNumPkt > 2) then begin
// декодируем пакет, ручная реализация стандартного алгоритма.
if _dFromServ then
_DecGS(_dBuff,Ksi,gSys.tKeyType)
else begin
_DecGS(_dBuff,Kci,gSys.tKeyType);
writelogHexB(_dBuff);
if _id_mix then _decode_id(_dBuff);
end;
_dOutBuff := _dBuff;
end;
end;
Procedure OnEnCode; // функция-событие на кодирование пакета.
// эта ф-ция вызывается перед отсылкой пакета, после записи в автологгер основных пакетов и после автоматического кодирования (если включено)
// так же ф-ция вызывается автоматически при использовании gSys.EnSendC и gSys.EnSendS
// !! Внимание !!
// в OnEnCode запрещено использовать ф-ции gSys.EnSendC и gSys.EnSendS во избежание замкнутой рекурсии
// ф-ция не обязательна и может отсутствовать в скриптах ...
// входной буфер '_dBuff', выходной '_dOutBuff', флаг направления '_dFromServ' (не перепутайте)
begin
//writelogln('OnEnCode ..');
if scode and (_gAbsNumPkt > 2) then begin
// кодируем пакет, ручная реализация стандартного алгоритма
if _dFromServ then
_EncGS(_dBuff,Kco,gSys.tKeyType)
else begin
if _id_mix then _encode_id(_dBuff);
_EncGS(_dBuff,Kso,gSys.tKeyType);
end;
_dOutBuff := _dBuff;
//if _dFromServ then gSys.EnSendC(_dBuff) else gSys.EnSendS(_dBuff);
//gBlockPacket;
end;
end;
procedure SendMsg(msg : string); // ф-ция для вывода любого сообщения в чат клиента (другим не видно)
begin
if (gSys.isGS > 0) and (gSys.TrafType = 2) then // проверка на трафик LA2 GS
gSys.EnSendC(FormatPck('%cddss',[#$4A,0,10,#0,msg+#0]));
end;
BEGIN
// Тело скрипта
// вызывается при получении пакета прошедшего все декодировоки и автодекодировки
// как обычно, здесь пакет приходит в _gBuff и если его надо изменить, то пишем в _gOutBuff
// ...
if length(_gBuff) < 3 then exit;
ID := gInt(_gBuff,3,1);
//if (ID = $FE) or (ID = $D0) then ID := gInt(_gBuff,3,2);
//
// в качестве примера
if ID = $15 then begin // возьмем имя выбранного персонажа
//GetAnsiStr(copy(_gBuff,4,
ScanPck5(_gBuff,4,'s',CharName, null, null, null, null);
CharName := trim(CharName);
Writelogln('Выбранный перс: '+CharName);
end;
//
// CT1+ table reset on char selected
if ID = $0B then begin
ScanPck5(_gBuff, 4, 's-99-99-04d' ,CharName, temp_seed, null, null, null);
writelogln(format('reseting id tables on CharSelected; Seed = %x',[temp_seed]));
_init_tables(temp_seed);
end;
// ... далее пакет логируется и уходит на автокодировку затем на кодировку (OnEnCode) и отправляется.
END.