com.vethrfolnir.game.network.mu.crypt.MuDecoder.java Source code

Java tutorial

Introduction

Here is the source code for com.vethrfolnir.game.network.mu.crypt.MuDecoder.java

Source

/**
 * Copyright (C) 2013-2014 Project-Vethrfolnir
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.vethrfolnir.game.network.mu.crypt;

import static com.vethrfolnir.game.network.mu.crypt.MuCryptUtils.*;
import io.netty.buffer.*;

import java.nio.ByteOrder;

import com.vethrfolnir.logging.MuLogger;
import com.vethrfolnir.tools.PrintData;

/**
 * @author Vlad
 *
 */
public final class MuDecoder {

    private static final MuLogger log = MuLogger.getLogger(MuDecoder.class);

    // Internal use
    private static ByteBufAllocator alloc = //UnpooledByteBufAllocator.DEFAULT;
            PooledByteBufAllocator.DEFAULT;

    // GMO KEYS
    private static short[] Xor32Keys = new short[] { 0xAB, 0x11, 0xCD, 0xFE, 0x18, 0x23, 0xC5, 0xA3, 0xCA, 0x33,
            0xC1, 0xCC, 0x66, 0x67, 0x21, 0xF3, 0x32, 0x12, 0x15, 0x35, 0x29, 0xFF, 0xFE, 0x1D, 0x44, 0xEF, 0xCD,
            0x41, 0x26, 0x3C, 0x4E, 0x4D };

    public static ByteBuf DecodeXor32(ByteBuf buff) {

        if (buff.readerIndex() != 0) {
            log.warn("Buffer must be at index 0!");
            buff.readerIndex(0);
        }

        int header = GetHeaderSize(buff);
        int decodedSize = GetDecodedSize(buff);

        buff.readerIndex(header);
        DecXor32(buff, header, decodedSize - header);
        buff.readerIndex(0);
        return buff;
    }

    /**
     * C3 C4
     * @param buff
     * @return 
     */
    public static ByteBuf DecodePacket(ByteBuf buff) {
        if (buff.writerIndex() <= 2) {
            log.fatal("Ambiguous buffer! " + ByteBufUtil.hexDump(buff));
            return null;
        }
        if (buff.readerIndex() != 0) {
            log.warn("Buffer must be at index 0!");
            buff.readerIndex(0);
        }

        int header = GetHeaderSize(buff);
        int packetSize = GetPacketSize(buff);
        int decodedSize = GetDecodedSize(buff);

        int contentSize = packetSize - header;

        //System.out.println("Header[0x"+PrintData.fillHex(buff.getUnsignedByte(0), 2)+"] size: "+GetHeaderSize(buff)+" Packet Size: "+GetPacketSize(buff) +" Content Size: "+contentSize +" Decoded: "+ GetDecodedSize(buff));

        ByteBuf out = alloc.heapBuffer(decodedSize, decodedSize);

        int originalHead = buff.getUnsignedByte(0);

        buff.readerIndex(header);
        int size = DecodeBlock(buff, out, header, contentSize);
        //buff.clear();

        size += header - 1;

        out.writerIndex(0);
        out.writeByte(originalHead);

        switch (originalHead) {
        case 0xc3:
            out.writeByte(size);
            break;
        case 0xC4:
            out.writeByte(size >> 8);
            out.writeByte(size & 0xFF);
            break;
        }

        out.writerIndex(size);
        out.readerIndex(header);

        DecXor32(out, header, size);

        out.readerIndex(0);
        return out;
    }

    private static int DecodeBlock(ByteBuf buff, ByteBuf outBuff, int offset, int size) {
        // decripted size
        int index = 0;

        if ((size % 11) != 0) {
            log.warn("Cannot decrypt packet, it's already decrypted!: Size " + size + " = " + ((size % 11)));
            log.warn(PrintData.printData(buff.nioBuffer()));
            return -1;
        }

        ByteBuf encrypted = alloc.heapBuffer(11, 11).order(ByteOrder.LITTLE_ENDIAN);
        short[] uByteArray = new short[encrypted.capacity()];

        ByteBuf decrypted = alloc.heapBuffer(8, 8).order(ByteOrder.LITTLE_ENDIAN);
        ByteBuf converter = alloc.heapBuffer(4).order(ByteOrder.LITTLE_ENDIAN);

        for (int i = 0; i < size; i += 11) {
            buff.readBytes(encrypted);

            //System.out.println("ENC: "+PrintData.printData(encrypted.nioBuffer()));
            int Result = BlockDecode(decrypted, getAsUByteArray(encrypted, uByteArray), converter,
                    MuKeyFactory.getClientToServerPacketDecKeys());
            if (Result != -1) {
                //Buffer.BlockCopy(Decrypted, 0, m_DecryptResult, (OffSet - 1) + DecSize, Result);

                outBuff.writerIndex((offset - 1) + index);
                outBuff.writeBytes(decrypted);

                //outBuff.writeBytes(decrypted);

                decrypted.clear();
                encrypted.clear();
                converter.clear();
                //System.arraycopy(Decrypted, 0, m_DecryptResult, (OffSet - 1) + DecSize, Result);

                index += Result;
            }
        }

        return index;
    }

    /**
     * @param decrypted
     * @param encrypted
     * @param decServerKeys
     * @return
     */
    private static int BlockDecode(ByteBuf decrypted, short[] InBuf, ByteBuf converter, long[] Keys) {

        long[] Ring = new long[4];
        short[] Shift = new short[4];

        ShiftBytes(Shift, 0x00, InBuf, 0x00, 0x10);
        ShiftBytes(Shift, 0x16, InBuf, 0x10, 0x02);

        writeByteArray(converter, Shift);
        flushArray(Shift, 0, 4);

        ShiftBytes(Shift, 0x00, InBuf, 0x12, 0x10);
        ShiftBytes(Shift, 0x16, InBuf, 0x22, 0x02);

        writeByteArray(converter, Shift);
        flushArray(Shift, 0, 4);

        ShiftBytes(Shift, 0x00, InBuf, 0x24, 0x10);
        ShiftBytes(Shift, 0x16, InBuf, 0x34, 0x02);

        writeByteArray(converter, Shift);
        flushArray(Shift, 0, 4);

        ShiftBytes(Shift, 0x00, InBuf, 0x36, 0x10);
        ShiftBytes(Shift, 0x16, InBuf, 0x46, 0x02);

        writeByteArray(converter, Shift);
        flushArray(Shift, 0, 4);

        // for (int i = 0; i < Ring.length; i++) {
        // System.err.print(Integer.toHexString((int) Ring[i])+" ");;
        // }

        // System.err.println();

        for (int i = 0; i < Ring.length; i++) {
            Ring[i] = converter.readInt();
        }
        converter.clear();

        Ring[2] = Ring[2] ^ Keys[10] ^ (Ring[3] & 0xFFFF);
        Ring[1] = Ring[1] ^ Keys[9] ^ (Ring[2] & 0xFFFF);
        Ring[0] = Ring[0] ^ Keys[8] ^ (Ring[1] & 0xFFFF);

        //       System.err.println("Finished Ring: ");
        //       for (int i = 0; i < Ring.length; i++) {
        //       System.err.print(Integer.toHexString((int) Ring[i])+" ");;
        //       }
        //       System.err.println();
        int[] CryptBuf = new int[4];

        // Had ushort cast here.
        CryptBuf[0] = (int) (Keys[8] ^ ((Ring[0] * Keys[4]) % Keys[0]));

        CryptBuf[1] = (int) (Keys[9] ^ ((Ring[1] * Keys[5]) % Keys[1]) ^ (Ring[0] & 0xFFFF));
        CryptBuf[2] = (int) (Keys[10] ^ ((Ring[2] * Keys[6]) % Keys[2]) ^ (Ring[1] & 0xFFFF));
        CryptBuf[3] = (int) (Keys[11] ^ ((Ring[3] * Keys[7]) % Keys[3]) ^ (Ring[2] & 0xFFFF));

        //      System.err.println("Pre done: " + PrintData.printData(CryptBuf));

        short[] Finale = new short[2];
        ShiftBytes(Finale, 0x00, InBuf, 0x48, 0x10);
        Finale[0] ^= Finale[1];
        Finale[0] ^= 0x3D;

        converter.clear();
        for (int i = 0; i < CryptBuf.length; i++) {
            converter.writeShort(CryptBuf[i]);
        }

        decrypted.writeBytes(converter, Finale[0]);
        converter.clear();

        //System.out.println(PrintData.printData(decrypted.nioBuffer()));

        // System.err.println("Finale: "+ Finale[0]);

        short Check = 0xF8;
        for (int i = 0; i < Finale[0]; ++i)
            Check = (short) (Check ^ decrypted.getUnsignedByte(i));

        if (Finale[1] == Check)
            return Finale[0];

        //System.err.println("Finale["+Finale[0]+"] And done: "+PrintData.printData(decrypted.nioBuffer()));

        return Finale[0];
    }

    private static long ShiftBytes(short[] OutBuf, long Arg_4, short[] InBuf, long Arg_C, long Arg_10) {
        long Size = ((((Arg_10 + Arg_C) - 1) / 8) + (1 - (Arg_C / 8)));

        short[] Tmp = new short[20];
        System.arraycopy(InBuf, (int) (Arg_C / 8), Tmp, 0, (int) Size);

        long Var_4 = (Arg_10 + Arg_C) & 0x7;

        if (Var_4 != 0) // Pay attention, too many casts, look for negatives
            Tmp[(int) (Size - 1)] = (short) (Tmp[(int) (Size - 1)] & 0xFF << (int) (8 - Var_4));

        Arg_C &= 0x7;

        ShiftRight(Tmp, (int) Size, (int) Arg_C);
        ShiftLeft(Tmp, (int) Size + 1, (int) (Arg_4 & 0x7));

        if ((Arg_4 & 0x7) > Arg_C)
            ++Size;
        if (Size != 0)
            for (int i = 0; i < Size; ++i) {
                OutBuf[(int) (i + (Arg_4 / 8))] = (short) ((byte) (OutBuf[(int) (i + (Arg_4 / 8))] | Tmp[i])
                        & 0xFF);
            }

        return Arg_10 + Arg_4;
    }

    private static void ShiftLeft(short[] Data, int Size, int Shift) {
        if (Shift == 0)
            return;
        for (int i = 1; i < Size; i++)
            Data[Size - i] = (byte) ((Data[Size - i] >> Shift) | ((Data[Size - i - 1]) << (8 - Shift)));

        Data[0] = (short) ((byte) (Data[0] >> Shift) & 0xFF);
    }

    private static void ShiftRight(short[] Data, int Size, int Shift) {

        if (Shift == 0)
            return;
        for (int i = 1; i < Size; i++)
            Data[i - 1] = (byte) ((Data[i - 1] << Shift) | (Data[i] >> (8 - Shift)));

        Data[Size - 1] = (short) ((byte) (Data[Size - 1] << Shift) & 0xFF);
    }

    private static void DecXor32(ByteBuf buff, int SizeOfHeader, int Len) {

        for (int i = Len - 1; i > 0; i--) {
            int Buff = buff.getUnsignedByte(buff.readerIndex() + i);
            Buff ^= (Xor32Keys[(i + SizeOfHeader) & 31] ^ buff.getUnsignedByte((buff.readerIndex() + i) - 1));
            buff.setByte(buff.readerIndex() + i, Buff);
        }
    }

    public static void main(String[] args) {
        //byte[] data = PacketUtils.hex2Bytes("C1 04 F3 0D d3");
        //byte[] data = PacketUtils.hex2Bytes("C1 0F F3 7B 85 58 A8 B9 E9 41 BF 09 40 1D 74");
        byte[] data = PacketUtils.hex2Bytes(
                "C3 5A 9E 4D 18 56 28 FB 20 E5 2D A7 92 5A 01 33 CB 50 BA F0 10 69 76 43 16 D1 65 36 64 13 F1 45 CC 1A 2F B1 B7 4E 24 49 1F F2 2F 54 A7 92 F8 04 7E 8A A7 A7 73 EF 00 C9 FC E2 CF 35 E4 58 21 A0 5C 89 16 23 B2 8C 5C 4C 62 23 B2 8B 90 27 12 61 07 5D 00 4D C3 F3 5A 31 99 A7");

        ByteBuf buff = Unpooled.buffer().order(ByteOrder.LITTLE_ENDIAN);

        ByteBuf out = null;
        for (int i = 0; i < 1; i++) {
            buff.writeBytes(data);

            long t1 = System.currentTimeMillis();
            //DecodeXor32(buff);
            out = DecodePacket(buff);
            long t2 = System.currentTimeMillis();

            System.out.println("Process time: " + (t2 - t1) + " milis");

        }
        System.out.println(PrintData.printData(out.nioBuffer()));

    }
}