• Unfortunately, we have experienced significant hard drive damage that requires urgent maintenance and rebuilding. The forum will be a state of read only until we install our new drives and rebuild all the configurations needed. Please follow our Facebook page for updates, we will be back up shortly! (The forum could go offline at any given time due to the nature of the failed drives whilst awaiting the upgrades.) When you see an Incapsula error, you know we are in the process of migration.

Maple Story encription

Skilled Illusionist
Joined
Apr 26, 2015
Messages
302
Reaction score
77
Hi guys.
I'm developing a clientless bot in Java for v62/83. However i have some problem's the the encription.
The problem is everythime i encode with the iv it become a different packet which i'm not able to decript

Encode:

Code:
@Override	protected void encode(ChannelHandlerContext ctx, Object message, ByteBuf buffer) throws Exception {
		final byte[] input = (byte[]) message;
		System.out.println("To decode: " + HexTool.toString(input));
		final byte[] unencrypted = new byte[input.length];
		System.arraycopy(input, 0, unencrypted, 0, input.length);
		final byte[] ret = new byte[unencrypted.length + 4];
		final byte[] header = send_crypto.getPacketHeader(unencrypted.length);
		MapleCustomEncryption.encryptData(unencrypted);
		send_crypto.crypt(unencrypted);
		System.arraycopy(header, 0, ret, 0, 4);
		System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
		buffer.writeBytes(ret);
		System.out.println("After encode:  " + HexTool.toString(ret));
		
	


	}

Decode:

Code:
@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> message) throws Exception {
		DecoderState decoderState = ctx.channel().attr(DECODER_STATE_KEY).get();
		
		if (decoderState == null) {
			decoderState = new DecoderState();
			ctx.channel().attr(DECODER_STATE_KEY).set(decoderState);
		}
		if (in.readableBytes() >= 4 && decoderState.packetlength == -1) {
			int packetHeader = in.readInt();
			if (!PacketEncoder.recvCypher.checkPacket(packetHeader)) {
				System.out.println("Invalid packet");
				//return;
			}
			decoderState.packetlength = MapleAESOFB.getPacketLength(packetHeader);
		} else if (in.readableBytes() < 4 && decoderState.packetlength == -1) {
			
			return;
		}
		if (in.readableBytes() >= decoderState.packetlength) {
			byte decryptedPacket[] = new byte[decoderState.packetlength];
			in.readBytes(decryptedPacket);
			System.out.println("Starting decode:  " + HexTool.toString(decryptedPacket));
			
			decoderState.packetlength = -1;
			PacketEncoder.recvCypher.crypt(decryptedPacket);
			MapleCustomEncryption.decryptData(decryptedPacket);
			System.out.println("Decoded: " + HexTool.toString(decryptedPacket));
			message.add(decryptedPacket);
			
		}


and the Cipher definition:

Code:
private final static short MAPLE_VERSION = 62;


	public static final byte key[] = { 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
			(byte) 0xB4, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x52,
			0x00, 0x00, 0x00 };


	public static byte ivRecv[] = { 70, 114, 122, 82 };


	public static byte ivSend[] = { 82, 48, 120, 115 };


	public static MapleAESOFB send_crypto;
	public static MapleAESOFB recvCypher;


	static {
		ivRecv[3] = (byte) (Math.random() * 255);
		ivSend[3] = (byte) (Math.random() * 255);
		send_crypto = new MapleAESOFB(key, ivSend, (short) (0xFFFF - MAPLE_VERSION));
		recvCypher = new MapleAESOFB(key, ivRecv, MAPLE_VERSION);
		


	}

If i comment the line send_crypto.crypt(unencrypted); and the line PacketEncoder.recvCypher.crypt(decryptedPacket); (the iv cripto) it works


any idea why?
 
Custom Title Activated
Loyal Member
Joined
Jan 18, 2010
Messages
3,109
Reaction score
1,139
Well, in newer versions, Shanda was removed (MapleCustomEncryption), but you're doing v83. If this is a server-side end, the "crypt" is doing an AES crypt and then updating your IV shift. If you remove that, your IV doesn't even change or update? You sure it "works", or it just looks correct?

Also, to add on because of the above post, he's correct; the first packet (the handshake) is unencrypted. It is also the packet that initializes and syncs the server->client IVs. If you never sync the IVs in the handshake and keep them equal to that of the client, packets sent/received will be messed up.
 
Upvote 0
Skilled Illusionist
Joined
Apr 26, 2015
Messages
302
Reaction score
77
I have a mocked server just replying with a encripted packet, i just want to test the point-to-point encription first and then start with the interation.
If i remove the iv in both mocked server and my client it works.
Something with the iv is messing up with the packet and then the server cant decript it.
However without the encription works...



Btw, i'm not even trying to connect to real server, just a mocked version for now.
 
Upvote 0
Junior Spellweaver
Joined
Jun 29, 2009
Messages
139
Reaction score
12
Right... Your IV's aren't being set correctly as your assuming that the client provides the IV's to the server... which it doesn't. Your IV's need to be set based on what the server tells you. I've made a similar bot before and I tested it on a proper maplestory server, otherwise you might find yours only works on your fake server.
 
Upvote 0
Skilled Illusionist
Joined
Apr 26, 2015
Messages
302
Reaction score
77
I have a mocked server just replying with a encripted packet, i just want to test the point-to-point encription first and then start with the interation.
If i remove the iv in both mocked server and my client it works.
Something with the iv is messing up with the packet and then the server cant decript it.
However without the encription works...



Btw, i'm not even trying to connect to real server, just a mocked version for now.
I just wanted to test the encryption and decryption of data. Unfortunately, everytime I encrypt a packet it becomes a different array of bytes because the IV changes. If I comment the IV crypt part in both client/server I can encrypt on client and decrypt the data on my mocked server.
Thank you for the help guys, I will post the full source later!



Here is a example:
Before encode: F1 00 00 00 00 00 00 00 ->The packet i want to encode
After encode: B9 7C B1 7C 7E 79 4D B0 94 AF B7 45 -> The encoded packet header + data. The encoded packet changes everytime i call the method recvCypher.crypt in encoder...
10:23:57.301 [nioEventLoopGroup-4-1] INFO com.maplebr.client.net.server.MockedServerHandler - Server - Connected
Before decode: 7E 79 4D B0 94 AF B7 45 -> This is the same packet as defined in 'after encode'
After decode: CE A5 75 54 20 1D 7F C1 -> We decode and the same packet is different everytime i try to decode.



I think the problem is the packet header.
Anyone knows the correct getHeader routine for the client?



Edit:
This is a small unit test for the IV and produces the following output:

IV: 46 72 7A 16IV: 52 30 78 05
Value: 01 0A 0A
Crypt: 3E C1 DA
Decrypt: 16 A8 DB --> This should be 01 0A 0A since we are decrypting. However it prints something else...
IV: 4C CA 74 9F
IV: B9 23 C1 59


Code:
package com.maple.br;


import org.junit.BeforeClass;
import org.junit.Test;


import com.maplebr.client.tool.HexTool;
import com.maplebr.client.tool.MapleAESOFB;


public class IVTest {


	private final static short MAPLE_VERSION = 62;
	
	public static final byte key[] = { 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, (byte) 0xB4, 0x00, 0x00,
			0x00, 0x1B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00 };


	public static byte ivRecv[] = { 70, 114, 122, 82 };
	public static byte ivSend[] = { 82, 48, 120, 115 };


	public static MapleAESOFB send_crypto;
	public static MapleAESOFB recvCypher;
	
	


	@BeforeClass
	public static void init() {
		ivRecv[3] = (byte) (Math.random() * 255);
		ivSend[3] = (byte) (Math.random() * 255);
		
		send_crypto = new MapleAESOFB(key, ivSend.clone(), MAPLE_VERSION);
		recvCypher = new MapleAESOFB(key, ivRecv.clone(), MAPLE_VERSION);
		
	}
	
	
	
 [USER=196303]TesT[/USER]
	public void testIv(){
		
		System.out.println("IV: " + HexTool.toString(recvCypher.getIv()));
		System.out.println("IV: " + HexTool.toString(send_crypto.getIv()));
		
		byte test [] = {0x1, 0xA, 0xA};
		
		System.out.println("Value: " + HexTool.toString(test));
		byte encripted [] = test.clone();
		send_crypto.crypt(encripted);
		System.out.println("Crypt: " + HexTool.toString(encripted));
		
		byte unc [] = recvCypher.crypt(encripted);
		System.out.println("Decrypt: " + HexTool.toString(unc));//Should print again the AA, right? However it does not..
		
		System.out.println("IV: " + HexTool.toString(recvCypher.getIv()));
		System.out.println("IV: " + HexTool.toString(send_crypto.getIv()));
		
		
	}


}
 
Upvote 0
Elite Diviner
Joined
Mar 24, 2015
Messages
426
Reaction score
416
You are using different IVs for encryption and decryption so there is no way it would decrypt correctly.
 
Upvote 0
Skilled Illusionist
Joined
Apr 26, 2015
Messages
302
Reaction score
77
Guys, i have fixed the problem with the IV and now i got a question. The algorithm to calculate the packet header on client side v62 is public? If so, could someone link ?
I can see my decripted packet on the server now, however it fails when i try to validate the packet header:
//MapleServerHandler
if (!client.getReceiveCrypto().checkPacket(packetHeader)) {
session.close();
return false;
}

Thank you!
 
Upvote 0
Custom Title Activated
Loyal Member
Joined
Jan 18, 2010
Messages
3,109
Reaction score
1,139
Guys, i have fixed the problem with the IV and now i got a question. The algorithm to calculate the packet header on client side v62 is public? If so, could someone link ?
I can see my decripted packet on the server now, however it fails when i try to validate the packet header:
//MapleServerHandler
if (!client.getReceiveCrypto().checkPacket(packetHeader)) {
session.close();
return false;
}

Thank you!

This is how Nexon validates their packets on the server-side:
PHP:
short uRawSeq = in.readShort();
        short uDataLen = in.readShort();
        short uSeqBase = (short) (((SystemConstants.Client << 8) & 0xFF00));
        if (((((((uRawSeq >> 8) ^ (pSocket.m_uSeqRcv >> 16)) & 0xFF) != ((uSeqBase >> 8) & 0xFF))))) {
            System.err.println("Incorrect packet header sequencing");
            ctx.disconnect();
            return;
        }

Core client code:
PHP:
if ( puSeqKey )
    v15 = uSeqBase ^ *((_WORD *)puSeqKey + 1);
  else
    v15 = uSeqBase;
  *(_WORD *)v13 = v15;
  v16 = v13 + 2;
  if ( bEnc )
    v17 = v6->m_uOffset ^ v15;
  else
    v17 = v6->m_uOffset;
  *(_WORD *)v16 = v17;
 
Upvote 0
Skilled Illusionist
Joined
Apr 26, 2015
Messages
302
Reaction score
77
This is how Nexon validates their packets on the server-side:
PHP:
short uRawSeq = in.readShort();
        short uDataLen = in.readShort();
        short uSeqBase = (short) (((SystemConstants.Client << 8) & 0xFF00));
        if (((((((uRawSeq >> 8) ^ (pSocket.m_uSeqRcv >> 16)) & 0xFF) != ((uSeqBase >> 8) & 0xFF))))) {
            System.err.println("Incorrect packet header sequencing");
            ctx.disconnect();
            return;
        }

Core client code:
PHP:
if ( puSeqKey )
    v15 = uSeqBase ^ *((_WORD *)puSeqKey + 1);
  else
    v15 = uSeqBase;
  *(_WORD *)v13 = v15;
  v16 = v13 + 2;
  if ( bEnc )
    v17 = v6->m_uOffset ^ v15;
  else
    v17 = v6->m_uOffset;
  *(_WORD *)v16 = v17;
Thanks man!
Anyone already converted this to c# or Java?

I wrote the following test and it works.
public class IVHeaderCheckTest {


public byte iv [] = { 70, 114, 122, 82 };

public MapleAESOFB aes = new MapleAESOFB(Client.MAPLE_KEY, iv, Client.MAPLE_VERSION);


@Before
public void init(){
iv[3] = (byte) (Math.random() * 255);
}

@Test
public void testHeader() {
byte [] header = aes.getPacketHeader(16);
int value = new BigInteger(header).intValue();
System.out.println(value);
boolean result = aes.checkPacket(value);
System.out.println(result);//prints true
Assert.assertTrue("Could not validate the header", result);
}
}

This code on server-side validates as follow:
if (!client.getReceiveCrypto().checkPacket(packetHeader)) { session.close();
return false;
}

Which mean's if i generate on client-side using the same value as the receive crypto, it will
work, right? :).
 
Last edited:
Upvote 0
Custom Title Activated
Loyal Member
Joined
Jan 18, 2010
Messages
3,109
Reaction score
1,139
@br1337 the code I provided was Java, I use it on my emulator. The second code was directly from the client. I translated it into Java which is what is reading in shorts.

Also, did you try other headers to confirm? Sometimes one header works and others won't. Also, from the looks of it, you're using the default IV. If you update it and try another header, does it still work? ;P
 
Upvote 0
Elite Diviner
Joined
Mar 24, 2015
Messages
426
Reaction score
416
Hi @Eric.
Thanks for your help, i managed to get in game.

You can find the source here:

https://github.com/br1337/maplev62-clb

However it does not work for MapleLegend and MapleRoyals which was my goal.

From what it looks like your packets in "CommonPacketOperations" are missing some stuff. I don't remember exactly but I think for Royals you need to send HWID/MAC in your charSelected. And Legends uses pic for the login.
 
Upvote 0
Skilled Illusionist
Joined
Apr 26, 2015
Messages
302
Reaction score
77
How didn't it work? For mine I got the player to appear in the world but couldn't get them to move around. Did you get somewhere near to that?
You have to send the movement packet to do that.
This source will just spawn the player in game and move to start position.

Anyways, i'm migrating this source to GMS.

I'm having some issues after char selection. :(
 
Upvote 0
Skilled Illusionist
Joined
Apr 26, 2015
Messages
302
Reaction score
77
From what it looks like your packets in "CommonPacketOperations" are missing some stuff. I don't remember exactly but I think for Royals you need to send HWID/MAC in your charSelected. And Legends uses pic for the login.
They have some sort of protection in Maple Royals, we cant even check if the server port is open.
 
Upvote 0
Back
Top