v193+ Encryption

Page 1 of 3 123 LastLast
Results 1 to 15 of 39
  1. #1
    Member PacketBakery is offline
    MemberRank
    Sep 2016 Join Date
    60Posts

    v193+ Encryption

    This is going to be my last release as I already quit maplestory development but I still see people struggling with this. I released a v188 idb that wasnt virtualized and gave all the information necessary but people still couldn't figure it out so here's leech-able code (for the most part).

    Credits go to myself and @Eric as we worked together to figure this out.

    There are 2 parts to this.
    Part 1

    Multiple Encryption Types: Neckson introduced a secondary encryption type (Value 2) in their MakeBufferList which replaces AES (Value 1). They only use this for their (Server -> Client) packet data as (Client -> Server) still uses AES. It should also be noted they use this on every server except their login server.

    The code to decrypt / encrypt this is below.

    Code:
    private static void EncInit(int uSeqSnd, byte[] aSrc, boolean bDecrypt) {
            for (int i = 0; i < aSrc.length; i++) {
                if (bDecrypt) {
                    aSrc[i] -= (byte) uSeqSnd;
                } else {
                    aSrc[i] += (byte) uSeqSnd;
                }
            }
        }
    Bonus MapleShark Code:

    Code:
    public void EncInit(byte[] aBuffer, bool bLoopback) {
        uint uIV = BitConverter.ToUInt32(aIV, 0);
        for (int i = 0; i < aBuffer.Length; i++) {
            if (!bLoopback) {
                aBuffer[i] -= (byte)(uIV);
            } else {
                aBuffer[i] += (byte)(uIV);
            }
        }
    }
    Part 2
    Opcode Encryption is something they introduced for their (Client -> Server) packets. The packet data itself still uses AES as its encryption but the opcodes are shuffled afterwards using DES-EDE2. Note that they only do this on every server except their login server. You will know if they do this or not by receiving a packet that is a length of 32,776 after MigrateIn. As of v194, the header to this packet is "40". Note that the block size is 4 because every opcode is 4 bytes encrypted however with 3DES, each encryption is 8 bytes as they combined 2 opcodes into a string for an 8 byte encryption.

    The dissection of this packet is like this:

    Code:
    [XX XX XX XX] - Block Size: The amount of bytes to read for each encrypted opcode.
    [XX XX XX XX] - Buffer Size: The amount of bytes to read for the buffer
    [XX x Above Length] - Buffer: This includes all the encrypted opcodes plus padding if amount of opcodes does not reach full buffer length.
    Here is the Java class to support 3DES.

    Code:
    /**
     *
     * @author PacketBakery
     */
    public class TripleDESCipher {
     
        public byte[] aKey = new byte[24];
        public Key pKey;
     
        public TripleDESCipher(byte[] aKey) {
            System.arraycopy(aKey, 0, this.aKey, 0, aKey.length);
            this.pKey = new SecretKeySpec(aKey, "DESede");
        }
     
        public byte[] Encrypt(byte[] aData) throws Exception {
            Cipher pCipher = Cipher.getInstance("DESede");
            pCipher.init(Cipher.ENCRYPT_MODE, this.pKey);
            return pCipher.doFinal(aData);
        }
     
        public byte[] Decrypt(byte[] aData) throws Exception {
            Cipher pCipher = Cipher.getInstance("DESede");
            pCipher.init(Cipher.DECRYPT_MODE, this.pKey);
            return pCipher.doFinal(aData);
        }
    }
    Bonus MapleShark Class:

    Code:
    public class TripleDESCipher {
        public static String Decrypt(byte[] aData, byte[] aKey) {
            TripleDESCryptoServiceProvider pCipher = new TripleDESCryptoServiceProvider();
            pCipher.BlockSize = 64;
            pCipher.Mode = CipherMode.ECB;
            pCipher.Padding = PaddingMode.None;
            pCipher.FeedbackSize = 64;
            pCipher.GenerateIV();
            MethodInfo mi = pCipher.GetType().GetMethod("_NewEncryptor", BindingFlags.NonPublic | BindingFlags.Instance);
            object[] aObj = { aKey, pCipher.Mode, pCipher.IV, pCipher.FeedbackSize, 1};
            ICryptoTransform pTransform = mi.Invoke(pCipher, aObj) as ICryptoTransform;
            byte[] aResult = pTransform.TransformFinalBlock(aData, 0, aData.Length);
            pCipher.Clear();
            return UTF8Encoding.UTF8.GetString(aResult);
        }
    }
    Now there's a few things to note. First is the key.
    The key consists of 2 parts;
    Part 1: Character ID converted into ASCII
    Part 2: Machine ID

    The key length is a total of 16 bytes, it can not be less and it can not be more. Note that since we are using TripleDES which uses a 24 byte key, this means the first 8 bytes of your key need to be appended as the last 8 bytes as well.

    So we need to initialize our key:

    Code:
    byte[] aKey = new byte[24];
    Next we need to convert our characterid into ascii and append it into our key array.

    Code:
    String sCharacterID = String.valueOf(this.dwCharacterID);
    for (int i = 0; i < sCharacterID.length(); i++) {
        aKey[i] = (byte) sCharacterID.charAt(i);
    }
    After this, part 1 is completed. Next is onto part 2.
    We need to get the length of our current key so we dont go over 16 bytes and then we need to append our machineid into our key.

    Code:
    int nLength = sCharacterID.length();
    for (int i = nLength; i < 16; i++) {
        aKey[i] = pMachineID.aMachineID[i - nLength];
    }
    Now our key is 16 bytes but since 3DES requires a 24 byte key, we append the 1st 8 bytes as our last 8 bytes as well.

    Code:
    System.arraycopy(aKey, 0, aKey, 16, 8);
    Now our key is complete and we can create our buffer with our encrypted opcodes.
    The way I do this is using a map to maintain the real opcode and encrypted opcode for when I decrypt it later in my OnPacket functions.

    This map has to be local to the User (or their Socket) as each user does not get the same opcodes.

    Code:
    public Map<Integer, Integer> mEncryptedOpcode = new LinkedHashMap<>();
    Next we have to populate the map with the real & encrypted opcodes.

    Code:
    List<Integer> aUsed = new ArrayList<>();
    String sOpcode = "";
    for (int i = ClientPacket.BeginUser.Get(); i < ClientPacket.Count.Get(); i++) {
        int nNum = Utilities.GetRandom(ClientPacket.BeginUser.Get(), 9999);
        while (aUsed.contains(nNum)) {
            nNum = Utilities.GetRandom(ClientPacket.BeginUser.Get(), 9999);
        }
        String sNum = String.format("%04d", nNum);
        if (!aUsed.contains(nNum)) {
            this.mEncryptedOpcode.put(nNum, i);
            aUsed.add(nNum);
            sOpcode += sNum;
        }
    }
    aUsed.clear();
    After we populated our map, we have to create the buffer and send it to our user.
    Note: The buffer does not have to be a size of 32,776 however I add random bytes to pad it since that's also what neckson does. The required length only needs to be that of the amount of opcodes.

    Code:
    TripleDESCipher pCipher = new TripleDESCipher(aKey);
    try {
        byte[] aBuffer = new byte[Short.MAX_VALUE + 1];
        byte[] aEncrypt = pCipher.Encrypt(sOpcode.getBytes());
        System.arraycopy(aEncrypt, 0, aBuffer, 0, aEncrypt.length);
        for (int i = aEncrypt.length; i < aBuffer.length; i++) {
            aBuffer[i] = (byte) Math.random();
        }
        SendPacket(ClientSocketPacket.OnOpcodeEncryption(4, aBuffer), false);
    } catch (Exception e) {
        e.printStackTrace();
    }
    As explained above, our packet will look like this:

    Code:
    public static OutPacket OnOpcodeEncryption(int nBlockSize, byte[] aBuffer) {
        OutPacket oPacket = new OutPacket(LoopbackPacket.OpcodeEncryption, false);
        oPacket.EncodeInt(nBlockSize);
        oPacket.EncodeInt(aBuffer.length);
        oPacket.EncodeBuffer(aBuffer);
        return oPacket;
    }
    Now the last part is decrypting it to the real opcodes when the client sends the server your encrypted header that you created. Wherever you read in the packet and decode your opcode, you will have to modify it to read from your encrypted opcode map:

    Code:
    int nType = iPacket.DecodeShort();
    if(pSocket.mEncryptedOpcode.containsKey(nType)) {
        nType = pSocket.mEncryptedOpcode.get(nType);
    }
    ...and that's all you have to do. Below is the mapleshark bonus to decrypt and sniff properly but it'll also be one code block.

    Code:
    if (nOpcode == OpcodeEncryption) {
        int nSize = BitConverter.ToInt32(packetBuffer, 4);
        byte[] aData = new byte[nSize];
        Buffer.BlockCopy(packetBuffer, 8, aData, 0, nSize);
        String sOpcode = TripleDESCipher.Decrypt(aData, pMain.aKey);
        try {
            int x = 0;
            for (int i = ClientPacket.BeginUser; i < ClientPacket.Count; i++) {
                pMain.mEncryptedOpcode.Add(Int32.Parse(sOpcode.Substring(x, 4)), i);
                x += 4;
            }
        } catch (Exception e) {
            MessageBox.Show(e.Message);
        }
    } else if (nPort != LoginServer.nPort && bOutbound) {
        if (pMain.mEncryptedOpcode.ContainsKey(nOpcode)) {
            nOpcode = (ushort) pMain.mEncryptedOpcode[nOpcode];
        }
    }
    Now you should be able to handle the new encryptions both for your server and sniffing.
    Again, credit to myself and @Eric as he was a big help when we were working on this together.
    This could of been released long ago by a few people who were aware of it and the only reason I'm releasing it now is because the 1st person who figured it out decided to keep it private and not offer much help to people who asked (or claimed they knew it and were full of shit but who knows). But thats it, I'm gone from this scene and wish everyone who still develops a maplestory server best of luck.
    Last edited by Eric; 24-04-18 at 03:40 AM. Reason: Fixing formats


  2. #2
    Member Arnah is offline
    MemberRank
    Mar 2016 Join Date
    87Posts

    Re: v193+ Encryption

    Cool stuff ill never use but cool
    Last edited by Arnah; 24-04-18 at 05:29 AM.

  3. #3
    Apprentice lai is offline
    MemberRank
    Jan 2018 Join Date
    14Posts

    Re: v193+ Encryption

    Cool stuff ill never use but cool

  4. #4
    Yuki Zygon is offline
    MemberRank
    Aug 2008 Join Date
    IllinoisLocation
    1,208Posts

    Re: v193+ Encryption

    You can't escape. No one ever does...

  5. #5
    Account Upgraded | Title Enabled! Generic230 is offline
    MemberRank
    Jul 2009 Join Date
    506Posts

    Re: v193+ Encryption

    very cool
    Cool stuff ill never use but cool

  6. #6
    Novice Zdasl is offline
    MemberRank
    Oct 2008 Join Date
    2Posts

    Re: v193+ Encryption

    How to get MachineID?

  7. #7
    Enthusiast swordiemen is offline
    MemberRank
    May 2007 Join Date
    31Posts

    Re: v193+ Encryption

    Very cool stuff, I'll probably use it!

  8. #8
    Moderator Eric is offline
    ModeratorRank
    Jan 2010 Join Date
    DEV CityLocation
    3,188Posts

    Re: v193+ Encryption

    Quote Originally Posted by Zdasl View Post
    How to get MachineID?
    The same way you get the user's CharacterID, they're both within the MigrateIn packet (which is where you'll send the initialization packet after you've validated the user and before you send SetField).

  9. #9
    Member icelemon1314 is offline
    MemberRank
    May 2010 Join Date
    78Posts

    Re: v193+ Encryption

    Very cool stuff.

    All functions of the new version of idb cannot be decompiled. Isn't all functions handled by vm?

  10. #10
    ・ARC:+200 Yuuroido is offline
    MemberRank
    Jul 2010 Join Date
    Saitama, JPLocation
    333Posts

    Re: v193+ Encryption

    Thank you for the part 2, it worked on JMS too.

  11. #11
    Infraction Baɴɴed holthelper is offline
    MemberRank
    Apr 2008 Join Date
    1,765Posts

    Re: v193+ Encryption

    Cool stuff ill never use but cool

  12. #12
    Interesting... SharpAceX is offline
    MemberRank
    Oct 2008 Join Date
    2,011Posts

    Re: v193+ Encryption

    Cool stuff ill never use but cool

  13. #13
    Moderator Eric is offline
    ModeratorRank
    Jan 2010 Join Date
    DEV CityLocation
    3,188Posts

    Re: v193+ Encryption

    Quote Originally Posted by icelemon1314 View Post
    Very cool stuff.

    All functions of the new version of idb cannot be decompiled. Isn't all functions handled by vm?
    If you're referring to opcode crypto in new IDBs, all of the functions are VM'd, yes. Hence why the only way you'd be able to debug and implement this is with the unvirtualized v188 client/idb that he released previously. They're both the same though, the crypto hasn't changed since; only difference is that as of v193 you can't get in-game properly without it

  14. #14
    ・ARC:+200 Yuuroido is offline
    MemberRank
    Jul 2010 Join Date
    Saitama, JPLocation
    333Posts

    Re: v193+ Encryption

    It really doesn't matter but... isn't it since v186 (Override/Beyond) that they've started to use the part 1 crypto?
    (and IIRC, the part 2 crypto has been used since v183 (Kerning Tower))

  15. #15
    I'm overrated. Fraysa is offline
    MemberRank
    Apr 2008 Join Date
    4,891Posts

    Re: v193+ Encryption

    Cool use ill never stuff but cool



Page 1 of 3 123 LastLast

Advertisement