package crypt; import java.nio.ByteOrder; import java.util.Arrays; import com.jds.jn.crypt.ProtocolCrypter; import com.jds.jn.network.packets.DecryptedPacket; import com.jds.jn.network.packets.PacketType; import com.jds.jn.parser.datatree.VisualValuePart; import com.jds.jn.session.Session; import com.jds.nio.buffer.NioBuffer; import crypt.helpers.Obfuscator; /** * Author: VISTALL * Company: J Develop Station * Date: 16.09.2009 * Time: 10:20:30 */ public class L2GameCrypter implements ProtocolCrypter { private boolean _isEnables; private byte[] _inKey; private byte[] _outKey; private Obfuscator _obs = new Obfuscator(); private final Object _clientLock = new Object(); private final Object _serverLock = new Object(); @Override public byte[] decrypt(byte[] raw, PacketType dir, Session session) { if(!_isEnables && dir == PacketType.CLIENT) return raw; if(!_isEnables && dir == PacketType.SERVER) { DecryptedPacket packet = new DecryptedPacket(null, PacketType.SERVER, raw, System.currentTimeMillis(), session.getProtocol(), false); if(packet.isKey()) searchKey(packet); return raw; } switch(dir) { case CLIENT: synchronized(_clientLock) { decode(raw, _outKey); NioBuffer buff = NioBuffer.wrap(raw); buff.order(ByteOrder.LITTLE_ENDIAN); int val = buff.getUnsigned(); if(val != 0xD0) { val = _obs.decodeSingleOpcode(val); buff.put(0, (byte) val); } else { buff.put(0, (byte) val); int second = _obs.decodeDoubleOpcode(buff.getUnsignedShort(1)); buff.putShort(1, (short) second); } return buff.array(); } case SERVER: synchronized(_serverLock) { decode(raw, _inKey); if(raw[0] == 0x0B) searchSeed(session, raw); } break; } return raw; } private synchronized void searchKey(DecryptedPacket packet) { byte[] key = new byte[16]; for(int i = 0; i < 8; i++) { VisualValuePart part = (VisualValuePart) packet.getRootNode().getPartByName("key" + i); // key key[i] = part.getValueAsByte(); } //xor key key[8] = (byte) 0xc8; key[9] = (byte) 0x27; key[10] = (byte) 0x93; key[11] = (byte) 0x01; key[12] = (byte) 0xa1; key[13] = (byte) 0x6c; key[14] = (byte) 0x31; key[15] = (byte) 0x97; int seed = packet.getInt("seed"); if(seed != 0) _obs.init_tables(seed); _inKey = Arrays.copyOf(key, key.length); _outKey = Arrays.copyOf(key, key.length); _isEnables = true; } public void searchSeed(Session session, byte[] raw) { DecryptedPacket packet = new DecryptedPacket(null, PacketType.SERVER, raw, System.currentTimeMillis(), session.getProtocol(), false); if(packet.getPacketInfo() != null && !packet.hasError()) { if(packet.getRootNode().getPartByName("seed") != null) { _obs.disable(); int seed = packet.getInt("seed"); if(seed != 0) _obs.init_tables(seed); } } } public void decode(byte[] raw, byte[] key) { int size = raw.length; int temp = 0; for(int i = 0; i < size; i++) { int temp2 = raw[i] & 0xFF; raw[i] = (byte) (temp2 ^ key[i & 15] ^ temp); temp = temp2; } int old = key[8] & 0xff; old |= key[9] << 8 & 0xff00; old |= key[10] << 0x10 & 0xff0000; old |= key[11] << 0x18 & 0xff000000; old += size; key[8] = (byte) (old & 0xff); key[9] = (byte) (old >> 0x08 & 0xff); key[10] = (byte) (old >> 0x10 & 0xff); key[11] = (byte) (old >> 0x18 & 0xff); } @Override public byte[] encrypt(byte[] raw, PacketType dir, Session session) { return null; } }