// '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.