PDA

Просмотр полной версии : Самостоятельное декодирование dat без l2encdec


Hint
04.10.2011, 13:21
Скрипт, который декодирует файлы клиента (RSA и gzip). Аналог l2encdec.
В интернете описания алгоритма не нашел, поэтому пришлось разбираться самому (плюс искать ключи). Ставил хуки на gmpz_powm и gmpz_export в libgmp-3, смотрел что передает и получает l2encdec.

Если бы люди не зажимали информацию (тогда, когда это не бьет по карману), то все бы мы тратили намного меньше времени впустую.

О чем я говорю:
http://www.l2wh.com/forum/showthread.php?t=1903
Нельзя было просто скопировать ключи?) Теперь мы должны искать бинарник за 24-01-2008. И речь ведь не о исходниках l2encdec.

<?php

/**
* L2 file decoder (4 october 2011).
* @author Hint aka Ilya
*/

function decode($filename, $original = true)
{
$file = @file_get_contents($filename);
if (!$file) return false;
if (strlen($file) < 28 + 128) return false;
$head = mb_convert_encoding(substr($file, 0, 28), 'UTF-8', 'UTF-16LE');
if ($head != 'Lineage2Ver413') return false;

$blocks = intval((strlen($file) - 28) / 128);
if ($blocks < 1) return false;

$size = $blocks * 128;

$file = substr($file, 28, $size);

if ($original)
{
$s = base64_decode('l985hHLd9zfvCgzRfo0XLw/vFmGjiorh1ugpvBxuTDz8GSkt2p75AXXkbnOUoYhQtkF9A75u6 idNPtHd5bXXvecswKC3HQNghlVjOIF5OgLJpn2e8rRet8CNS+M pCDzkUOaPeGe2dJMU1AUR0JvFdEVRuqhqidw4Ej3BZo/XLYM=');
$key = gmp_init('0x' . bin2hex($s));
$exp = gmp_init('0x35');
}
else
{
$s = base64_decode('dbTW3lwBZUQGihrPElhp9D0uCfxVuLHiiVV tr5uHV2NVk0RiiLNlPaHOkch7saXBjxYyNJXFXX1ywIkKg/ab/R/ZQ06xwC8+Rnnt+kMwkxkHASnCZ8hWBNh7tluuIF3jcHrx0hCIg au1Z8Oz0GmuZ8OkxqOqk9JkE9TGYJSuIDk=');
$key = gmp_init('0x' . bin2hex($s));
$exp = gmp_init('0x1d');
}

$data = '';
for ($i = 0; $i < $blocks; ++$i)
{
$block = substr($file, $i * 128, 128);
$res = gmp_powm('0x' . bin2hex($block), $exp, $key);
if (!is_resource($res)) return false;
$hex = gmp_strval($res, 16);
if (strlen($hex) != 250) return false;
$s = pack('H*' , $hex);

$size = ord($s[0]);
if ($size > strlen($s) - 1) return false;

if ($size != 0x7c)
{
$p = strlen($s) - $size;
while ($p > 2 && $s[$p - 1] != "\0") --$p;
$s = substr($s, $p, $size);
}
else
{
$s = substr($s, -$size);
}
$data .= $s;
}

$a = unpack('L', $data);
if (!is_array($a) || !isset($a[1])) return false;

$size = intval($a[1]);
$data = substr($data, 4);
$result = gzuncompress($data);
if (strlen($result) != $size) return false;

return $result;
}

// Example
$s = decode('l2.ini', true);
if ($s === false) die('Error!');
file_put_contents('dec-l2.ini', $s);
?>

xkor
04.10.2011, 15:23
Hint, ну тыб тут спросил, я бы выложил сорци своего недоделанного аналога l2encdec, и кстати для его написания мне публично доступной инфы из инета вполне хватило

вот мой модуль для шифрования:
unit l2CryptRSA;

interface

procedure DecodeRSA(var data; const size: Integer);
procedure EncodeRSA(var data; const size: Integer);
function DecryptStr413(const data: string): string;

implementation

uses
ZLibEx;

const
bufLen = 128;

type
mpz_t = record
mp_alloc,
mp_size: Integer;
mp_d: Pointer;
end;

TRSABlocks = array[0..0] of array[1..bufLen] of Byte;

const
//priv. exponent (d)
priv_key: array[0..127] of Char =
#$65#$DE#$C1#$81#$50#$95#$1E#$C9#$3E#$22#$DB#$C7#$ 16#$B8#$2D#$6F+
#$FC#$E2#$46#$F7#$53#$85#$A2#$5C#$FE#$5D#$AD#$18#$ 76#$95#$A1#$31+
#$B6#$C1#$93#$D4#$CB#$62#$15#$8C#$9A#$DA#$6D#$3A#$ 25#$A7#$43#$4E+
#$F0#$C2#$14#$1C#$3D#$FE#$C6#$63#$82#$1F#$54#$AE#$ D9#$43#$10#$83+
#$4D#$B3#$77#$0A#$E1#$2F#$35#$D7#$9D#$90#$F0#$72#$ F2#$0B#$DD#$4C+
#$12#$5F#$1D#$9D#$64#$D2#$43#$66#$B8#$32#$7F#$A2#$ FC#$F8#$07#$6A+
#$4C#$50#$5B#$40#$C4#$E6#$D3#$38#$78#$1D#$29#$00#$ E4#$76#$97#$71+
#$1E#$84#$8E#$3C#$06#$75#$5C#$14#$86#$70#$D4#$98#$ D7#$C2#$B4#$30;
e: mpz_t = (mp_alloc: 32; mp_size: 32; mp_d: @priv_key[0]);
//modulo (n)
modulo: array[0..127] of Char =
#$39#$20#$AE#$94#$60#$C6#$D4#$13#$64#$D2#$93#$AA#$ A3#$C6#$A4#$C3+
#$67#$AE#$69#$D0#$B3#$C3#$67#$B5#$AB#$81#$88#$10#$ D2#$F1#$7A#$70+
#$E3#$5D#$20#$AE#$5B#$B6#$7B#$D8#$04#$56#$C8#$67#$ C2#$29#$01#$07+
#$19#$93#$30#$43#$FA#$ED#$79#$46#$3E#$2F#$C0#$B1#$ 4E#$43#$D9#$1F+
#$FD#$9B#$F6#$83#$0A#$89#$C0#$72#$7D#$5D#$C5#$95#$ 34#$32#$16#$8F+
#$C1#$A5#$B1#$7B#$C8#$91#$CE#$A1#$3D#$65#$B3#$88#$ 62#$44#$93#$55+
#$63#$57#$87#$9B#$AF#$6D#$55#$89#$E2#$B1#$B8#$55#$ FC#$09#$2E#$3D+
#$F4#$69#$58#$12#$CF#$1A#$8A#$06#$44#$65#$01#$5C#$ DE#$D6#$B4#$75;
m: mpz_t = (mp_alloc: 32; mp_size: 32; mp_d: @modulo[0]);
//pub. exponent (e)
pub_key = $1d;

gmpdll = 'libgmp-3.dll';

procedure mpz_init(var Dest: mpz_t); cdecl;
external gmpdll name '__gmpz_init';
procedure mpz_clear(var Dest: mpz_t); cdecl;
external gmpdll name '__gmpz_clear';
procedure mpz_powm(var Dest: mpz_t; const Base, Exponent, Modulus: mpz_t); cdecl;
external gmpdll name '__gmpz_powm';
procedure mpz_powm_ui(var Dest: mpz_t; var Base: mpz_t; Exponent: Cardinal;
const Modulus: mpz_t); cdecl;
external gmpdll name '__gmpz_powm_ui';

procedure mpz_import(var rop: mpz_t; count, order, size, endian,
nails: Cardinal; const op); cdecl;
external gmpdll name '__gmpz_import';
procedure mpz_export(var rop, count; order, size, endian,
nails: Cardinal; const op: mpz_t); cdecl;
external gmpdll name '__gmpz_export';

var
r,d: mpz_t;

procedure RSA_BlockDec(var data);
var
count: Cardinal;
begin
mpz_import(d,bufLen,1,1,0,0,data);
mpz_powm_ui(r,d,$1d,m);
count:=32;
mpz_export(data,count,1,4,1,0,r);
end;

procedure RSA_BlockEnc(var data);
var
count: Cardinal;
begin
mpz_import(d,bufLen,1,1,0,0,data);
mpz_powm(r,d,e,m);
count:=32;
mpz_export(data,count,1,4,1,0,r);
end;

procedure DecodeRSA(var data; const size: Integer);
var
i: Integer;
dt: TRSABlocks absolute data;
begin
for i:=0 to size div bufLen - 1 do RSA_BlockDec(dt);
end;

procedure EncodeRSA(var data; const size: Integer);
var
i: Integer;
dt: TRSABlocks absolute data;
begin
for i:=0 to size div bufLen - 1 do RSA_BlockEnc(dt[i]);
end;

function UnPack(const data; const size: Integer): string;
var
i,k: Integer;
dt: TRSABlocks absolute data;
begin
Result:='';
for i:=0 to size div bufLen - 1 do begin
k:=Length(Result);
SetLength(Result,k+dt[i][3]);
Move(dt[i][(128-dt[i][3])and $7C],Result[k+1],dt[i][3]);
end;
end;

function Pack(const data: string): string;
var
i,k: Integer;
begin
{ TODO : create Pack function }
end;

function Decompress(const data: string): string;
begin
Result:=ZDecompressStr(data);
end;

function DecryptStr413(const data: string): string;
begin
Result:=data;
DecodeRSA(Result[1],Length(Result));
Result:=Copy(UnPack(Result[1],Length(Result)),5,Length(Result));
Result:=Decompress(Result);
end;

initialization
mpz_init(d);
mpz_init(r);

finalization
mpz_clear(d);
mpz_clear(r);

end.


[I]Добавлено через 3 минуты
и кстати для его написания мне публично доступной инфы из инета вполне хватилоа хотя не, вру, ключи я извлекал из l2encdec запустив на отладку через ollydbg

Hint
04.10.2011, 15:44
Зато теперь есть общедоступная реализация на php и delphi :)

В интернете подробной информации нет. Про RSA понятно по l2encdec (вывод процесса распаковки). Сжатие - логично, потому что размер файла меньше (плюс DStuff написал про использование zlib у себя в USAGE). То, что сначала сжимают, а потом шифруют через RSA - тоже понятно (иначе бы ничего не сжималось :) Заголовок и CRC видно в hex-редакторе (размер остального кратен 128, что намекает на RSA и размер ключа и блоков).
Так что единственное темное пятно - ключи, а вот их нигде нет :)

Кстати, твой вариант не всегда будет работать, потому что в последнем блоке, который неполный, данные могут быть смещены (не знаю почему и зачем). Из-за этого пришлось добавить костыль:
if ($size != 0x7c)
{
$p = strlen($s) - $size;
while ($p > 2 && $s[$p - 1] != "\0") --$p;
$s = substr($s, $p, $size);
}
else
{
$s = substr($s, -$size);
}

Добавлено через 3 минуты
Честно говоря, копался в этом больше из-за любопытства, а не по необходимости. Началось с разбора нового 'itemname-*.dat' (l2disasm уже не помогает, поэтому пришлось писать свою программу), а потом захотелось разобраться и с l2encdec.

xkor
04.10.2011, 23:16
Кстати, твой вариант не всегда будет работать, потому что в последнем блоке, который неполный, данные могут быть смещены (не знаю почему и зачем). Из-за этого пришлось добавить костыль:я уже плохо помню (писал тот код года два назад), но по моему функция UnPack эту проблему решает

Pinko
05.10.2011, 00:02
ребята давайте жить дружно !!!!!!!!!!!
сделайте прогу
мне надо для декодирование dat
спасибо !

goodvin1709
05.10.2011, 09:48
Pinko, тебе же дали модули сунь их в Delphi 7 и делай декодирование Но по исходкинку вроде 413 кодировки?

Pinko
05.10.2011, 10:43
goodvin1709,
НЕ УМЕЮ !!!

TAMBIK
05.10.2011, 17:42
ребята давайте жить дружно !!!!!!!!!!!
сделайте прогу
мне надо для декодирование dat
спасибо !

НАДО !!!!!!!!!!!!!!!!!

Be3geBJIa3
28.10.2011, 11:12
Пишу редактор файлов (dat,ini,int,thm) для новых хроник пока еще нет правильных ddf структур файлов, приходится разбирать в ручную. Может у кого есть чем поделиться и объединить усилия, так как много времени уходит на это.
Разбираю ItemName-ru.dat, и так понимаю аналогов функций в ddf нету.

Hint
28.10.2011, 14:17
Пишу редактор файлов (dat,ini,int,thm) для новых хроник пока еще нет правильных ddf структур файлов, приходится разбирать в ручную. Может у кого есть чем поделиться и объединить усилия, так как много времени уходит на это.
Разбираю ItemName-ru.dat, и так понимаю аналогов функций в ddf нету.

Да, l2disasm устарел. Можно обойтись ENBBY, но это ужасные костыли (и не работает, если число элементов в массиве увеличивается). Нужные новые сложные элементы, похожие на MAT, MTX. Да и вообще, таблица (текстовый файл с табуляциями) уже не подходит, нужен вывод в более сложном формате (например, xml). Лично я, когда начала работать с GoD, написал свой l2disasm и не жалею.

Ущербный DDF для ItemName, который на некоторых файлах работать не будет, если в них встречаются длинные многомерные массивы (не работал на PTS клиенте L2.ru из-за того, что в "кривом" описании одного предмета был массив из 50+ элементов):
FS = "\t";
HEADER = 1;
RECCNT = OFF;
MTXCNT_OUT = 1;
MATCNT_OUT = 1;
ORD_IGNORE = 0;
{
UINT id;
UNICODE name;
UNICODE add_name;
ASCF description;
INT popup;
UINT supercnt0;
UINT count_1;ENBBY = [(supercnt0:-1,1)];ENBBY = [(supercnt0:-2,2)];ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UINT item_list_1[count_1];ENBBY = [(supercnt0:-1,1)];ENBBY = [(supercnt0:-2,2)];ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UINT count_2;ENBBY = [(supercnt0:-2,2)];ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UCHAR item_list_2[count_2];ENBBY = [(supercnt0:-2,2)];ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UCHAR item_list_2[count_2];ENBBY = [(supercnt0:-2,2)];ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UCHAR item_list_2[count_2];ENBBY = [(supercnt0:-2,2)];ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UCHAR item_list_2[count_2];ENBBY = [(supercnt0:-2,2)];ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UINT count_3;ENBBY = [(supercnt0:-1,3)];ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UINT item_list_3[count_3];ENBBY = [(supercnt0:-1,3)];ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UINT count_4;ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UINT item_list_4[count_4];ENBBY = [(supercnt0:-4,4)];ENBBY = [(supercnt0:-8,8)];
UINT count_5;ENBBY = [(supercnt0:-1,5)];ENBBY = [(supercnt0:-2,6)];ENBBY = [(supercnt0:-8,8)];
UINT item_list_5[count_5];ENBBY = [(supercnt0:-1,5)];ENBBY = [(supercnt0:-2,6)];ENBBY = [(supercnt0:-8,8)];
UINT count_6;ENBBY = [(supercnt0:-2,6)];ENBBY = [(supercnt0:-8,8)];
UINT item_list_6[count_6];ENBBY = [(supercnt0:-2,6)];ENBBY = [(supercnt0:-8,8)];
UINT count_7;ENBBY = [(supercnt0:-1,7)];ENBBY = [(supercnt0:-8,8)];
UINT item_list_7[count_7];ENBBY = [(supercnt0:-1,7)];ENBBY = [(supercnt0:-8,8)];
UINT count_8;ENBBY = [(supercnt0:-8,8)];
UINT item_list_8[count_8];ENBBY = [(supercnt0:-8,8)];
UINT count_9;ENBBY = [(supercnt0:-1,9)];ENBBY = [(supercnt0:-6,10)];ENBBY = [(supercnt0:-4,12)];
UINT item_list_9[count_9];ENBBY = [(supercnt0:-1,9)];ENBBY = [(supercnt0:-6,10)];ENBBY = [(supercnt0:-4,12)];
UINT count_10;ENBBY = [(supercnt0:-6,10)];ENBBY = [(supercnt0:-4,12)];
UINT item_list_10[count_10];ENBBY = [(supercnt0:-6,10)];ENBBY = [(supercnt0:-4,12)];
UINT cnt0;
UCHAR cnt0_empty;ENBBY = [(cnt0,0)];
ASCF set_bonus_desc[cnt0];
UINT supercnt1;
UINT extra_count;ENBBY = [(supercnt1:-1,1)];
UINT extra_item_list[extra_count];ENBBY = [(supercnt1:-1,1)];
UINT cnt1;
ASCF set_extra_desc;
FILLER unk1{6};
UCHAR unk2;ENBBY = [(cnt0:-1,1)];ENBBY = [(supercnt0:-1,1)];ENBBY = [(supercnt0:-2,2)];ENBBY = [(supercnt0:-4,4)];
UCHAR unk3;ENBBY = [(cnt1:-1,1)];ENBBY = [(supercnt1:-1,1)];ENBBY = [(supercnt1:-2,2)];ENBBY = [(supercnt1:-4,4)];
UINT special_enchant_amount;
ASCF special_enchant_desc;
UINT unk4;
}

Be3geBJIa3
28.10.2011, 17:17
ItemName-ru.dat победил, для него не хватает масива UINT(i)
потратил 3 дня.
у меня это выглядит вот так, для скриптового парсера.

FS = "\t";
HEADER = 1;
RECCNT = OFF;
MTXCNT_OUT = 1;
MATCNT_OUT = 1;
ORD_IGNORE = 0;
{
UINT id;
UNICODE name;
UNICODE add_name;
ASCF description;
INT popup;

UINT supercnt1;
UINT item_cnt1(supercnt1);
UINT itemid_1(item_cnt1);
UINT cnt1;
ASCF set_bonus_desc(cnt1);

UINT supercnt2;
UINT item_cnt2(supercnt2);
UINT itemid_2(item_cnt2);
UINT cnt2;
ASCF set_extra_desc(cnt2);

UINT supercnt3;
UINT item_cnt3(supercnt3);
UINT itemid_3(item_cnt3);
UINT cnt3;
ASCF set_extra_desc3(cnt3);

UINT special_enchant_amount;
ASCF special_enchant_desc;
UINT unk2;
}

Для создания l2infob.dat из этого необходимо только
UINT id;
UNICODE name;
UNICODE add_name;
ASCF description;

Разобрал actionname-ru.dat, потратил 15 минут. Ничего сложного нет.
----------------------------
Пока это только простой парсер, помогите написать или посоветуйте как это все хранить в памяти и потом уже записывать в txt, проблема в том, что заранее не известно количество элементов масива и постоянно меняется и при простой записи в txt все сдвинуто в кучу.

METAJIJI
23.07.2012, 20:21
#https://code.google.com/p/la2-encdec/source/browse/trunk/
# L2 file decoder (18:33 14.07.2012).
# @author METAJIJI Kadyshev Denis
#

use warnings;
use Time::HiRes qw(gettimeofday);
use strict;
use Encode; # use Encode qw(from_to);
#use MIME::Base64; # encode_base64 () и decode_base64 () для кодирования и декодирования соответственно.
use Compress::Zlib qw(compress uncompress);
use Math::BigInt;
use Math::BigInt only => 'GMP';
use Data::Dumper;

my $original = 0; # временно, в дальнейшем тут надо как-то сделать "прием этого значения"

my $t0 = gettimeofday; #начали отсчет
print qq[\n========================= -------- Start -------- =========================\n];

# RSA keys:
my $priv_key = q[0x30B4C2D798D47086145C75063C8E841E719776E400291D78 38D3E6C4405B504C6A07F8FCA27F32B86643D2649D1D5F124C DD0BF272F0909DD7352FE10A77B34D831043D9AE541F8263C6 FE3D1C14C2F04E43A7253A6DDA9A8C1562CBD493C1B631A195 7618AD5DFE5CA28553F746E2FC6F2DB816C7DB223EC91E9550 81C1DE65];
$priv_key = Math::BigInt->new($priv_key);
my ($mod, $exp);
if ($original == 1) { # 1 - File is Original | 0 - File is not Original
my $s = q[0x97df398472ddf737ef0a0cd17e8d172f0fef1661a38a8ae1 d6e829bc1c6e4c3cfc19292dda9ef90175e46e7394a18850b6 417d03be6eea274d3ed1dde5b5d7bde72cc0a0b71d03608655 633881793a02c9a67d9ef2b45eb7c08d4be329083ce450e68f 7867b6749314d40511d09bc5744551baa86a89dc38123dc166 8fd72d83];
$mod = Math::BigInt->new($s);
$exp = Math::BigInt->new('0x35');
} else {
my $s= q[0x75b4d6de5c016544068a1acf125869f43d2e09fc55b8b1e2 89556daf9b8757635593446288b3653da1ce91c87bb1a5c18f 16323495c55d7d72c0890a83f69bfd1fd9434eb1c02f3e4679 edfa43309319070129c267c85604d87bb65bae205de3707af1 d2108881abb567c3b3d069ae67c3a4c6a3aa93d26413d4c660 94ae2039];
$mod = Math::BigInt->new($s);
$exp = Math::BigInt->new('0x1d');
}

# Вычисление времени выполения скрипта
sub work_time {
my ($t0) = @_;

my $t1 = gettimeofday; # Узнаем время в текущей точке
my $elapsed = sprintf("%1.7f",$t1 - $t0); # Округляем до 3 знаков после запятой
print qq[\n===================== --- Done in: $elapsed sec. --- =====================\n];
}

# Запись в файл
sub save_file {
#&save_file($cfg->{'file_out'}, @a);
my ($file, $act, $data) = @_;

if (!$data) {
print qq[No Data, Error!\n];
exit 1;
};

open(FILE, ">$file-$act");
binmode FILE;
print(FILE $data);
close(FILE);
print qq[\t\tFile: ${file}-${act}\n\t\t\t successfully writed!\n];
&work_time($t0);
}

sub la2_decode {
my ($file) = @_;
&save_file($file, 'dec', &l2decode_413($file));
}

sub la2_encode {
my ($file) = @_;
&save_file($file, 'enc', &l2encode_413($file));
}


sub l2decode_413 {
my ($filename) = @_;
# use IO::Uncompress::AnyInflate qw(anyinflate $AnyInflateError);

open(FILE, "<$filename") || die qq[Error: Can not read '$filename'];
binmode FILE;

my $filesize = (stat(FILE))[7];
return if ($filesize < 28 + 128);

my $blocks = int(($filesize - 28)/128);
return if ($blocks < 1);

my $file;
read(FILE, $file, $filesize);
close(FILE);
return if (!$file);

my $head = substr($file, 0, 28); #print Dumper($head);
Encode::from_to($head, 'UTF-16LE', 'UTF-8');
return if ($head ne 'Lineage2Ver413');

my $size = $blocks * (128 * 2);
$file = unpack('H*', substr($file, 28, $size)); # переводим файл в hex, т.к. perl не умеет работать с бинарными данными. в нашем случае substr.

my $data_gz;
for (my $i = 0; $i < $blocks; ++$i) {
my $block = substr($file, $i * (128 * 2), 128 * 2);
my $res = '0x' . $block;# my $res = '0x' . unpack('H*', $block);
#print '$enc: ['.$res."]\n";
my $hex = Math::BigInt->new($res)->bmodpow($exp,$mod)->as_hex();
#print '$dec: ['.$hex."]\n";
#my $enc = Math::BigInt->new($hex)->bmodpow($priv_key,$mod)->as_hex();
#print '$enc: ['.$enc."]\n";

return if (length($hex) != 252);
substr($hex, 0, 2) =~ s/^0x//;

my $s = pack('H*' , $hex);
$size = ord($s);
return if ($size > length($s) - 1);

if ($size ne 124) { #($size ne 0x7c) | ($size ne oct('0x7c'))
my $p = length($s) - $size;
while ($p > 2 && substr($s, $p - 1, 1) ne "\0") { --$p; }
$s = substr($s, $p, $size);
} else {
$s = substr($s, -$size);
}
$data_gz .= $s;
}

my $a = unpack('L', $data_gz);
return if (!defined($a));
$size = int($a);

$data_gz = substr($data_gz, 4);

my $result = uncompress($data_gz);
#print $result;
return if (length($result) != $size);

return $result;
}

sub l2encode_413 {
my ($filename) = @_;
print qq[filename: $filename\n];
open(FILE, "<$filename") || die qq[Error: Can not read '$filename'];
binmode FILE;

my $filesize = (stat(FILE))[7];
return if ($filesize < 128); # тут мы пока еще в binmode, поэтому 128
print qq[filesize: $filesize\n];

read(FILE, my $file, $filesize);
return if (!$file);
close(FILE);

my $data_gz = compress($file,6); # Сжимаем файл
my $size = pack('L', $filesize);# Конвертируем размер файла в Long вид.
$data_gz = $size . $data_gz; # пихаем в начало архива 4 байта с размером архива.

#open(FILE, ">${filename}+head4") || die qq[Error: Can not read '$filename'];
#binmode FILE;
#print FILE $data_gz;
#close(FILE);

$data_gz = unpack('H*', $data_gz); # Конвертируем в HEX вид, чтобы perl'у было легче работать с архивом :) ну не любит он бинарные данные...
$data_gz = substr($data_gz, 0, 12) . pack('H*', unpack('H*', substr($data_gz, 12, 2)) - 1) . substr($data_gz, 14);# исправляем какой-то байт в начале файла...
$size = length($data_gz);
my $block_size = 248;
my $blocks = int(($size)/$block_size); # Тут 248, т.к. нужно 250 для функции криптования, но 2 байта уйдут на "заголовок строки" в котором будет размер блока - 7c
return if ($blocks < 1);
my $last_str = $size % $block_size; # Проверяем остаток от деления, чтобы узнать сколько у нас осталось неполных байт.
print 'size: ['.($size/2)."]\n";
print qq[blocks: $blocks\n];

my $data;
for (my $i = 0; $i < $blocks; ++$i) {
my $block = substr($data_gz, $i * $block_size, $block_size);
$block = '0x7c' . $block;
my $hex = Math::BigInt->from_hex($block)->bmodpow($priv_key,$mod)->as_hex();
substr($hex, 0, 2) =~ s/^0x//;
my $zeroNUM = 256 - length($hex);
if ($zeroNUM != 0) {
my $zeros;
for (my $i = 0; $i < $zeroNUM; $i++) { $zeros .= '0'}
$hex = $zeros . $hex;
}
return if (length($hex) != 256);
#print '$enc: [0x'.$hex."]\n"; print '$dec: ['.$block."]\n";
#print 'length($hex): ['.length($hex)."]\n";

$data .= pack('H*' , $hex);
}

my $block = substr($data_gz, -$last_str, -8);
my $size_str = unpack('H2' , pack('L' , length($block)));
$block = $block . '000000ffff0300' . substr($data_gz, -8);

#print 'size_str: ['.$size_str."]\n";

my $zeros = '';
for (my $i = 18; $i < $block_size - $last_str; $i++) { $zeros .= '0'}
$block = '0x' . $size_str . $zeros . $block . '0000';

my $hex = Math::BigInt->new($block)->bmodpow($priv_key,$mod)->as_hex();
substr($hex, 0, 2) =~ s/^0x//;
$data .= pack('H*' , $hex);
#print '$enc: [0x'.$hex."]\n"; print '$dec: ['.$block."]\n";
#print 'length($hex): ['.length($hex)."]\n";


my $result = q[Lineage2Ver413];
Encode::from_to($result, 'UTF-8', 'UTF-16LE'); # Получаем заголовок файла в UTF-16LE кодировке.
$result .= $data;
$result .= pack('H*', '000000000000000000000000');# добавляем 24 ноля, чтобы "выровнять файл"
$result .= pack('H*', '00000000'); #TODO - узнать что сюда добавляется.
$result .= pack('H*', '00000000');# добавляем 8 нолей, чтобы "выровнять файл"

#print unpack('L', pack('H*', 'dda08f96'))."\n";
return $result;

}
return 1;


Попробовал написать на perl, декодирование очень даже работает, а вот кодирование что-то не очень, может кто поможет? :)

user713
07.08.2016, 02:54
Ой чувствую щас за некропостинг получу по ушам, но всёже... В конце всех фалов закриптованных 413 алгоритмом присутствуют дополнительные 20 байт... Линейка 20 нулей прекрасно кушает, НО всёже интересно откуда l2encdec берет эти байты? Заметил что при изменении 413 на 414 меняются только 4 байта в конце. Крипт/декрипт свой сделал, но вот непойму что это в конце. Какая-то контрольная сумма? Кто знает как ее просчитать?

P.S: в оригинальных файлах ладвы эти байты в конце не пустые, собственно это и смущает...

P.P.S: а еще кому знакома цифра 143114 - подскажите как бороться, у меня уже моск вывернулся в непонятном направлении :\

ScythLab
11.08.2016, 22:38
Какая-то контрольная сумма? Кто знает как ее просчитать?Очень похоже на нее. Попробуй обычный xor DWORD'ов, если не поможет, то расчет Crc32, не думаю что туда что-то сложное запихали.


P.P.S: а еще кому знакома цифра 143114 - подскажите как бороться, у меня уже моск вывернулся в непонятном направлении :\если скажешь откуда эту цифру взял, может кто-то и подскажет

user713
15.08.2016, 10:01
если скажешь откуда эту цифру взял, может кто-то и подскажет

После сжатия большого количества данных ZLIB'ом именно это количество байт (уже сжатых) сходится с данными упакованными темже l2encdec, все что выше - полностью разное. И вдобавок zlib не хочет распаковывать информацию которую сам же и упаковал, которая больше этого числа :D Я конечно понимаю что это скорей всего мой косяк, но перепробовав десятки разных версий zlib'a и встретив эту цифру абсолютно везде, я всетаки сделал выводы что ошибка "типична" для данного продукта.

P.S: жму стандартным compress, в то время как l2encdec напрямую использует методы inflateInit, inflate и inflateEnd

ScythLab
22.08.2016, 10:49
После сжатия большого количества данных ZLIB'ом именно это количество байт (уже сжатых) сходится с данными упакованными темже l2encdec, все что выше - полностью разное. И вдобавок zlib не хочет распаковывать информацию которую сам же и упаковал, которая больше этого числа :D Я конечно понимаю что это скорей всего мой косяк, но перепробовав десятки разных версий zlib'a и встретив эту цифру абсолютно везде, я всетаки сделал выводы что ошибка "типична" для данного продукта.

P.S: жму стандартным compress, в то время как l2encdec напрямую использует методы inflateInit, inflate и inflateEndУверен, что это твой косяк, когда работал с ZLib'ом вообще не помню каких либо сложностей, все сжималось и разжималось, для любых файлов ла2.

user713
25.08.2016, 11:02
Спасибо) Будем исправляться :)

Stenly76
14.11.2016, 11:25
Ой чувствую щас за некропостинг получу по ушам, но всёже... В конце всех фалов закриптованных 413 алгоритмом присутствуют дополнительные 20 байт... Линейка 20 нулей прекрасно кушает, НО всёже интересно откуда l2encdec берет эти байты? Заметил что при изменении 413 на 414 меняются только 4 байта в конце. Крипт/декрипт свой сделал, но вот непойму что это в конце. Какая-то контрольная сумма? Кто знает как ее просчитать?

P.S: в оригинальных файлах ладвы эти байты в конце не пустые, собственно это и смущает...

P.P.S: а еще кому знакома цифра 143114 - подскажите как бороться, у меня уже моск вывернулся в непонятном направлении :\

и от меня некропост :)
это CRC32 взятый начиная с самого начала включая заголовок (Lineage2Ver) и по результат от Енкрипта (до выравнивания нулями)

Добавлено через 9 минут
Зато теперь есть общедоступная реализация на php и delphi :)

В интернете подробной информации нет. Про RSA понятно по l2encdec (вывод процесса распаковки). Сжатие - логично, потому что размер файла меньше (плюс DStuff написал про использование zlib у себя в USAGE). То, что сначала сжимают, а потом шифруют через RSA - тоже понятно (иначе бы ничего не сжималось :) Заголовок и CRC видно в hex-редакторе (размер остального кратен 128, что намекает на RSA и размер ключа и блоков).
Так что единственное темное пятно - ключи, а вот их нигде нет :)

Кстати, твой вариант не всегда будет работать, потому что в последнем блоке, который неполный, данные могут быть смещены (не знаю почему и зачем). Из-за этого пришлось добавить костыль:
if ($size != 0x7c)
{
$p = strlen($s) - $size;
while ($p > 2 && $s[$p - 1] != "\0") --$p;
$s = substr($s, $p, $size);
}
else
{
$s = substr($s, -$size);
}

Добавлено через 3 минуты
Честно говоря, копался в этом больше из-за любопытства, а не по необходимости. Началось с разбора нового 'itemname-*.dat' (l2disasm уже не помогает, поэтому пришлось писать свою программу), а потом захотелось разобраться и с l2encdec.

Похоже догадался: идет выравнивание до кратности в 4ре байта.

user713
29.12.2016, 08:31
это CRC32 взятый начиная с самого начала включая заголовок (Lineage2Ver) и по результат от Енкрипта (до выравнивания нулями)

Спасибо, то что нужно ;)

Похоже догадался: идет выравнивание до кратности в 4ре байта.

Именно так, тоже просчитал это, немного поэкспериментировав, и убрал все костыли из кода)

P.P.S: а еще кому знакома цифра 143114 - подскажите как бороться, у меня уже моск вывернулся в непонятном направлении :\

Уверен, что это твой косяк, когда работал с ZLib'ом вообще не помню каких либо сложностей, все сжималось и разжималось, для любых файлов ла2.

Как показали эксперименты - косяк не мой) Взял оригинальные файлы Ла2 - и все разжалось, сжалось байт в байт) А если брать файлы уже обработанные через l2encdec - тут начинаются расхождения (l2encdec жмёт чуть сильней и заголовок и хвост потока отличаются от оригинального zlib) Клиент ладвы кушает оба варианта без проблем. Моя тулза понимает файлы l2encdec, l2encdec понимает файлы от моей тулзы, но при взаимной обработки блоки могут отличаться (чаще это первый и последний блоки). Но главное что всё работает :)