Вдохновившись темой
https://forum.zone-game.info/showthread.php?t=40993 захотелось мне написать на коленке что-то вроде простенького редактора dat файлов, полноценного редактора конечно не получилось, но все же возможно найдутся люди кто подхватит идею
Итак, первым делом пришлось вытащить ключ из l2encdec, тут пришла на помощь IDA, открываю файл l2encdec.exe, декомпил в С код, долгий анализ и ничего не вышло, нету его там, но где то же он есть )) Открываю gg-bps.dll и вот она заветная строка
PHP код:
v3 = "75b4d6de5c016544068a1acf125869f43d2e09fc55b8b1e289556daf9b8757635593446288b3653da1ce91c87bb1a5c18f16323495c55"
"d7d72c0890a83f69bfd1fd9434eb1c02f3e4679edfa43309319070129c267c85604d87bb65bae205de3707af1d2108881abb567c3b3d0"
"69ae67c3a4c6a3aa93d26413d4c66094ae20390000001d";
Этих данных нам с головой хватит для дешифрации (Примечание, способ работает после прогона папки system утилитой patcher.exe)
Первые 256 байт это так называемый modulus, оставшиеся 8 байт это publicExponent (то что нужно для инициализации RSAPublicKeySpec)
Далее конечно же написание кода с использованием полученных ключей, что примечательно структура даток напоминает структуру пакетов, а именно ByteBuffer и все вытекающее, чтобы увидеть быстрый результат пришлось взять L2GameDataName.dat т.к. он состоит из списка простых строк
Свернуть ↑
PHP код:
import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
/**
BSmith
08.08.2016
*/
public class Test
{
public static void main(String[] args)
{
Cipher cipher;
try
{
cipher = Cipher.getInstance("RSA/ECB/nopadding");
RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(new BigInteger("75B4D6DE5C016544068A1ACF125869F43D2E09FC55B8B1E289556DAF9B8757635593446288B3653DA1CE91C87BB1A5C18F16323495C55D7D72C0890A83F69BFD1FD9434EB1C02F3E4679EDFA43309319070129C267C85604D87BB65BAE205DE3707AF1D2108881ABB567C3B3D069AE67C3A4C6A3AA93D26413D4C66094AE2039", 16), new BigInteger("1d", 16));
RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(rsaPublicKeySpec);
cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey);
}
catch(Exception e)
{
e.printStackTrace();
return;
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(128);
try(FileInputStream fileInputStream = new FileInputStream(new File("L2GameDataName.dat")))
{
fileInputStream.skip(28);
byte[] bytes = new byte[128];
int length = fileInputStream.available() - 20;
while(length > 0)
{
length -= fileInputStream.read(bytes);
byte[] doFinal = cipher.doFinal(bytes);
int size = doFinal[0x03];
size += doFinal[0x02] << 8 & 0x0000ff00;
size += doFinal[0x01] << 16 & 0x00ff0000;
size += doFinal[0x00] << 24 & 0xff000000;
int pad = -size & 0x01 + -size & 0x02;
byteArrayOutputStream.write(doFinal, 128 - size - pad, size);
}
bytes = byteArrayOutputStream.toByteArray();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes, 4, bytes.length - 4);
InflaterInputStream inflaterInputStream = new InflaterInputStream(byteArrayInputStream, new Inflater());
byteArrayOutputStream = new ByteArrayOutputStream(128);
bytes = new byte[128];
while((length = inflaterInputStream.read(bytes)) > 0)
{
byteArrayOutputStream.write(bytes, 0, length);
}
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArrayOutputStream.toByteArray());
int count = Integer.reverseBytes(byteBuffer.getInt());
for(int i = 0; i < count; i++)
{
System.out.println(readUtfString(byteBuffer));
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
public static String readUtfString(ByteBuffer buffer)
{
int size = Integer.reverseBytes(buffer.getInt());
byte[] bytes = new byte[size];
for(int i = 0; i < size; i += 2)
{
bytes[i + 1] = buffer.get();
bytes[i] = buffer.get();
}
try
{
return new String(bytes, "Unicode");
}
catch(Exception e)
{
return "Null";
}
}
}
Свернуть ↑Развернуть ↓
И конечно же результат, точнее вырезки из лога
Свернуть ↑
Упаковка: Улучш. Бронзовый Браслет (90 дн.)
Упаковка: Улучш. Стальной Браслет (1 день)
Упаковка: Улучш. Стальной Браслет (15 дн.)
Упаковка: Улучш. Стальной Браслет (30 дн.)
Упаковка: Улучш. Стальной Браслет (90 дн.)
Упаковка: Улучш. Мифриловый Браслет (1 день)
Упаковка: Улучш. Мифриловый Браслет (15 дн.)
LineageNpcsTex3.castle_enchanter.castle_enchanter_ a_t01
LineageNpcsTex3.castle_healer.castle_healer_a_t00
LineageNpcsTex3.castle_healer.castle_healer_a_t01
LineageNpcsTex3.castle_summoner.castle_summoner_a_ t00
LineageNpcsTex3.castle_summoner.castle_summoner_a_ t01
LineageNpcsTex3.castle_summoner.castle_summoner_a_ t02
LineageNpcsTex3.castle_summoner.castle_summoner_a_ t03
LineageEffect.u_aga_pegasus_deco
icon.reward_02
Map_Arena
Свернуть ↑Развернуть ↓
P.s. Подводя итоги можно с уверенностью сказать что основа для декодирования даток готова, далее необходимо собственно сделать поддержку структур нужных нам файлов а именно это
PHP код:
int count = Integer.reverseBytes(byteBuffer.getInt());
for(int i = 0; i < count; i++)
{
System.out.println(readUtfString(byteBuffer));
}