Emily
July 18th, 2011, 17:32
The purpose of this thread is not to make you a genius at identifying packets. It is simply a basic introduction to finding packets based on previous packet structure, guessing, or knowledge.
To start...
There are three different ways to identifying packets (in my opinion).
Knowing previous packet structure
Guess & Check Method
Memorizing the packet, or understanding its usage in the client
If you can already do the third way to identifying packets, then this thread isn't for you.
Starting off in the Client:
You will first need to identify the 'PacketParser' as it is called commonly now, or the class that handles incoming packets.
Searching:
"T1 - "
Should help you to identify this class.
Now, finding a packet:
Packets are easy to find, but maybe challenging to refract and implement in your server.
A packet will look like this:
if (Class318.aClass354_2585 == Class26.currentIncommingPacket) {
int i_48_ = Class274.serverOutBuffer.readInt_V1();
int i_49_ = Class274.serverOutBuffer.readUByte(true);
int i_50_ = Class274.serverOutBuffer.readUByteC();
Class186_Sub44_Sub6.anIntArray6246[i_49_] = i_48_;
Class186_Sub41_Sub12.anIntArray6066[i_49_] = i_50_;
OutputStream_Sub1.anIntArray48[i_49_] = 1;
int i_51_ = Class186_Sub41_Sub12.anIntArray6064[i_49_] - 1;
for (int i_52_ = 0; i_52_ < i_51_; i_52_++) {
if (Class186_Sub36.anIntArray4713[i_52_] <= i_48_)
OutputStream_Sub1.anIntArray48[i_49_] = i_52_ + 2;
}
Class162.anIntArray1398[Class186_Sub41_Sub36.method2377(31, Class248.anInt2086++)] = i_49_;
Class26.currentIncommingPacket = null;
return true;
}
Looking at first glance you may be over taken by it, but actually it is very simple to figure out.
Lets first look at individual parts and see what we can come up with:
Class274.serverOutBuffer.readInt_V1();
Although the naming of this maybe different in your client, the method still holds
This snippet is a readInt1, meaning server sided it would be:
buffer.writeInt1(value);
This:
Class274.serverOutBuffer.readUByte(true);
Is a read unsigned byte, Click the spoiler below to learn about bytes, integers, ect
buffer.writeByte(value);
Computers store information/memory using: bytes, shorts integers, long integers, and double integers.
These are broken up as follows:
Byte - 8 bits
Short - 16 bits
Long - 64 bits
Double - 64 bits
Bytes are used to store values between 0 and 255 (also known as (2^8)-1)
Shorts are used to store values between 0 and 65535 (also known as (2^16)-1)
Longs are used to store values between 0 and 9,223,372,036,854,775,807
Doubles are used to store values between 0 and 18,446,744,073,709,551,615 (also known as (2^64)-1)
If you aren't see the pattern, to find the max value that each type can hold, you take 2 to the power of the bits it can hold, and subtract 1.
Signed types can hold both negative and positive values, while Unsigned can only hold positive and 0 values.
int i_50_ = Class274.serverOutBuffer.readUByteC();
Is a read unsigned byte C
Which translates to:
buffer.writeByteC(value);
Now we know that this Packet contains:
writeInt1
writeByte
writeByteC
At this present time, I would say you couldn't say which packet that is off the top of your head. (Some can, but as stated above, if you can. This thread isn't really for you).
Let's compare it to another sources packets.
Dementhium:
public static void sendSkillLevel(Player player, int skill) {
MessageBuilder bldr = new MessageBuilder(85);
bldr.writeInt((int) player.getSkills().getXp(skill));
bldr.writeByte(player.getSkills().getLevel(skill)) ;
bldr.writeByteS(skill);
player.getConnection().write(bldr.toMessage());
}
Looks pretty close, other then the byteS.
Emberscape:
public void sendSkillLevel(int skill) {
player.getSession().write(new StaticPacketBuilder().setId(38)
.addByteA(player.getSkills().getLevel(skill))
.addInt1((int) player.getSkills().getXp(skill))
.addByteC(skill)
.toPacket());
}
Looks pretty close, other then the ByteA. So let's assume it is something possibly dealing with sendSkills?
Let's refractor the client a bit.
int i_48_ = Class274.serverOutBuffer.readInt_V1();
Becomes..
int xp = Class274.packet.readInt1()
int i_49_ = Class274.serverOutBuffer.readUByte(true);
Becomes..
int skill = Class274.packet.readUByte();
int i_50_ = Class274.serverOutBuffer.readUByteC();
Becomes..
int level = Class274.packet.readUByteC();
So now our method looks something like this:
if (Class318.aClass354_2585 == Class26.currentIncommingPacket) {
int xp = Class274.packet.readInt1();
int skill = Class274.packet.readUByte();
int level = Class274.packet.readUByteC();
Class186_Sub44_Sub6.anIntArray6246[skill] = xp;
Class186_Sub41_Sub12.anIntArray6066[skill] = level;
OutputStream_Sub1.anIntArray48[skill] = 1;
int i_51_ = Class186_Sub41_Sub12.anIntArray6064[skill] - 1;
for (int i_52_ = 0; i_52_ < i_51_; i_52_++) {
if (Class186_Sub36.anIntArray4713[i_52_] <= xp)
OutputStream_Sub1.anIntArray48[skill] = i_52_ + 2;
}
Class162.anIntArray1398[Class186_Sub41_Sub36.method2377(31, Class248.anInt2086++)] = skill;
Class26.currentIncommingPacket = null;
return true;
}
Let's do two final things, lets make i_52_ = value, and i_51_ = skillsCount
So now, our finish method:
if (Class318.aClass354_2585 == Class26.currentIncommingPacket) {
int xp = Class274.packet.readInt1();
int skill = Class274.packet.readUByte();
int level = Class274.packet.readUByteC();
Class186_Sub44_Sub6.anIntArray6246[skill] = xp;
Class186_Sub41_Sub12.anIntArray6066[skill] = level;
OutputStream_Sub1.anIntArray48[skill] = 1;
int skillsCount = Class186_Sub41_Sub12.anIntArray6064[skill] - 1;
for (int value = 0; value < skillsCount; value++) {
if (Class186_Sub36.anIntArray4713[value] <= xp)
OutputStream_Sub1.anIntArray48[skill] = value + 2;
}
Class162.anIntArray1398[Class186_Sub41_Sub36.method2377(31, Class248.anInt2086++)] = skill;
Class26.currentIncommingPacket = null;
return true;
}
Ok, big deal? Doesn't affect anything client sided, lets write the server sided portion. I'm going to write it using 647's files, since that is what is convenient on my eclipse at the current time. (Also the method is from 647 client).
The header:
public static void sendSkilllLevel(Player player, double xp, int skill, int level) {
Why the double? Well looking at extend information we know that bytes & shorts can't hold xp since it goes up to 200million.
Next, we need to find the packetId, you can either search this portion of the code:
aClass354_2585
Or, use eclipse and click open declaration.
Regardless you will come upon this:
static IncomingPacket aClass354_2585 = new IncomingPacket(101, 6);
101 = PacketId
6 = PacketSize
So continuing our method:
public static void sendSkillLevel(Player player, double xp, int skill, int lvl) {
PacketBuilder pb = new PacketBuilder(101);
Next, we have a writeInt1 , which relates to xp.
So lets write it as:
pb.writeInt1((int) xp);
We cast (int) to xp, since it is a double.
Next, we have a write unsigned byte for skill
So lets write it as:
pb.writeByte(skill);
Finally, we have a write unsigned byte C for level.
So lets write it as:
pb.writeByteC(level);
Then finish with writing out our channel;
pb.write(player.getChannel());
Finished packet will look similar to this:
public static void sendSkillLevel(Player player, double xp, int skill, int level) {
PacketBuilder pb = new PacketBuilder(101);
pb.writeInt1((int) xp);
pb.writeByte(skill);
pb.writeByteC(level);
pb.write(player.getChannel());
}
Not so hard?
To start, you need to find the file that contains the mask, I've checked in the newer revisions and searching:
== '~')
Will provide the class with the masks, you can also search for hexadecimal numbers that are products of 2^X as Sinisoul stated in a response post.
Ex: 0x1, 0x2, 0x4
Ok, so lets say you found your class, now lets get to the masks.
Similar to identifying packets, it is easier to find specific masks if you know either their packet structure or methods that are attached. For this example we will use animation mask.
Conveniently some masks do not change, in this case animation mask has not changed from 614 so it is relative easy to identify.
The maskId is:
So this is our method: (Excuse the refracted method, as I don't have the old naming of this available)
[code]
if ((i & 0x80) != 0) {
int[] is = new int[4];
for (int index = 0; index < 4; index++) {
is[index] = packet.readUnsignedShort();
if (is[index] == 65535)
is[index] = -1;
}
int i_11_ = packet.readSignedByteA_();
Class257.applyAnimationUpdate(is, i_11_, player);
}
We can ignore:
if (is[index] == 65535)
is[index] = -1;
Since that is telling us if the value is equal to 65535 for the Short then it will return -1. This is due to the fact that shorts have a maximum holding value of 0 - 65535.
So by reading this we know we have a:
buffer.writeShort(value);
and:
buffer.writeByteA(value);
Pay attention to the loop as well, as that will be carried over when we write the server sided method.
Now lets move to the actual server related shizniz.
We need to locate our Player updating file, in the case of 647 it is PlayerUpdateManager.java
We need to find our playerUpdate block which contains masks, so just earch for "0x" and you will find it.
if(flags.get(UpdateFlag.ANIMATION) || multi) {
mask |= 0x80;
}
If there is an Animation update needed, we return the mask 0x80.
Then we apply the method:
if(flags.get(UpdateFlag.ANIMATION) || multi) {
applyAnimationUpdate(block, otherPlayer);
}
Now writing the actual method:
Your first line will look something similar to:
private static void applyAnimationUpdate(PacketBuilder packet, Player otherPlayer) {
Now, remember this section from the client?
for (int index = 0; index < 4; index++) {
We are going to write it exactly the same.
So your first section of the method will look like this.
private static void applyAnimationUpdate(PacketBuilder packet, Player otherPlayer) {
for(int index = 0; index < 4; index++) {
packet.writeShort(otherPlayer.getLastAnimation().g etId());
}
Now, we still have that writeByteA, which we include after the closing bracket of the for loop. So now your finished method is:
private static void applyAnimationUpdate(PacketBuilder packet, Player otherPlayer) {
for(int index = 0; index < 4; index++) {
packet.writeShort(otherPlayer.getLastAnimation().g etId());
}
packet.writeByteA(otherPlayer.getLastAnimation().g etDelay());
}
Not to tricky
To start...
There are three different ways to identifying packets (in my opinion).
Knowing previous packet structure
Guess & Check Method
Memorizing the packet, or understanding its usage in the client
If you can already do the third way to identifying packets, then this thread isn't for you.
Starting off in the Client:
You will first need to identify the 'PacketParser' as it is called commonly now, or the class that handles incoming packets.
Searching:
"T1 - "
Should help you to identify this class.
Now, finding a packet:
Packets are easy to find, but maybe challenging to refract and implement in your server.
A packet will look like this:
if (Class318.aClass354_2585 == Class26.currentIncommingPacket) {
int i_48_ = Class274.serverOutBuffer.readInt_V1();
int i_49_ = Class274.serverOutBuffer.readUByte(true);
int i_50_ = Class274.serverOutBuffer.readUByteC();
Class186_Sub44_Sub6.anIntArray6246[i_49_] = i_48_;
Class186_Sub41_Sub12.anIntArray6066[i_49_] = i_50_;
OutputStream_Sub1.anIntArray48[i_49_] = 1;
int i_51_ = Class186_Sub41_Sub12.anIntArray6064[i_49_] - 1;
for (int i_52_ = 0; i_52_ < i_51_; i_52_++) {
if (Class186_Sub36.anIntArray4713[i_52_] <= i_48_)
OutputStream_Sub1.anIntArray48[i_49_] = i_52_ + 2;
}
Class162.anIntArray1398[Class186_Sub41_Sub36.method2377(31, Class248.anInt2086++)] = i_49_;
Class26.currentIncommingPacket = null;
return true;
}
Looking at first glance you may be over taken by it, but actually it is very simple to figure out.
Lets first look at individual parts and see what we can come up with:
Class274.serverOutBuffer.readInt_V1();
Although the naming of this maybe different in your client, the method still holds
This snippet is a readInt1, meaning server sided it would be:
buffer.writeInt1(value);
This:
Class274.serverOutBuffer.readUByte(true);
Is a read unsigned byte, Click the spoiler below to learn about bytes, integers, ect
buffer.writeByte(value);
Computers store information/memory using: bytes, shorts integers, long integers, and double integers.
These are broken up as follows:
Byte - 8 bits
Short - 16 bits
Long - 64 bits
Double - 64 bits
Bytes are used to store values between 0 and 255 (also known as (2^8)-1)
Shorts are used to store values between 0 and 65535 (also known as (2^16)-1)
Longs are used to store values between 0 and 9,223,372,036,854,775,807
Doubles are used to store values between 0 and 18,446,744,073,709,551,615 (also known as (2^64)-1)
If you aren't see the pattern, to find the max value that each type can hold, you take 2 to the power of the bits it can hold, and subtract 1.
Signed types can hold both negative and positive values, while Unsigned can only hold positive and 0 values.
int i_50_ = Class274.serverOutBuffer.readUByteC();
Is a read unsigned byte C
Which translates to:
buffer.writeByteC(value);
Now we know that this Packet contains:
writeInt1
writeByte
writeByteC
At this present time, I would say you couldn't say which packet that is off the top of your head. (Some can, but as stated above, if you can. This thread isn't really for you).
Let's compare it to another sources packets.
Dementhium:
public static void sendSkillLevel(Player player, int skill) {
MessageBuilder bldr = new MessageBuilder(85);
bldr.writeInt((int) player.getSkills().getXp(skill));
bldr.writeByte(player.getSkills().getLevel(skill)) ;
bldr.writeByteS(skill);
player.getConnection().write(bldr.toMessage());
}
Looks pretty close, other then the byteS.
Emberscape:
public void sendSkillLevel(int skill) {
player.getSession().write(new StaticPacketBuilder().setId(38)
.addByteA(player.getSkills().getLevel(skill))
.addInt1((int) player.getSkills().getXp(skill))
.addByteC(skill)
.toPacket());
}
Looks pretty close, other then the ByteA. So let's assume it is something possibly dealing with sendSkills?
Let's refractor the client a bit.
int i_48_ = Class274.serverOutBuffer.readInt_V1();
Becomes..
int xp = Class274.packet.readInt1()
int i_49_ = Class274.serverOutBuffer.readUByte(true);
Becomes..
int skill = Class274.packet.readUByte();
int i_50_ = Class274.serverOutBuffer.readUByteC();
Becomes..
int level = Class274.packet.readUByteC();
So now our method looks something like this:
if (Class318.aClass354_2585 == Class26.currentIncommingPacket) {
int xp = Class274.packet.readInt1();
int skill = Class274.packet.readUByte();
int level = Class274.packet.readUByteC();
Class186_Sub44_Sub6.anIntArray6246[skill] = xp;
Class186_Sub41_Sub12.anIntArray6066[skill] = level;
OutputStream_Sub1.anIntArray48[skill] = 1;
int i_51_ = Class186_Sub41_Sub12.anIntArray6064[skill] - 1;
for (int i_52_ = 0; i_52_ < i_51_; i_52_++) {
if (Class186_Sub36.anIntArray4713[i_52_] <= xp)
OutputStream_Sub1.anIntArray48[skill] = i_52_ + 2;
}
Class162.anIntArray1398[Class186_Sub41_Sub36.method2377(31, Class248.anInt2086++)] = skill;
Class26.currentIncommingPacket = null;
return true;
}
Let's do two final things, lets make i_52_ = value, and i_51_ = skillsCount
So now, our finish method:
if (Class318.aClass354_2585 == Class26.currentIncommingPacket) {
int xp = Class274.packet.readInt1();
int skill = Class274.packet.readUByte();
int level = Class274.packet.readUByteC();
Class186_Sub44_Sub6.anIntArray6246[skill] = xp;
Class186_Sub41_Sub12.anIntArray6066[skill] = level;
OutputStream_Sub1.anIntArray48[skill] = 1;
int skillsCount = Class186_Sub41_Sub12.anIntArray6064[skill] - 1;
for (int value = 0; value < skillsCount; value++) {
if (Class186_Sub36.anIntArray4713[value] <= xp)
OutputStream_Sub1.anIntArray48[skill] = value + 2;
}
Class162.anIntArray1398[Class186_Sub41_Sub36.method2377(31, Class248.anInt2086++)] = skill;
Class26.currentIncommingPacket = null;
return true;
}
Ok, big deal? Doesn't affect anything client sided, lets write the server sided portion. I'm going to write it using 647's files, since that is what is convenient on my eclipse at the current time. (Also the method is from 647 client).
The header:
public static void sendSkilllLevel(Player player, double xp, int skill, int level) {
Why the double? Well looking at extend information we know that bytes & shorts can't hold xp since it goes up to 200million.
Next, we need to find the packetId, you can either search this portion of the code:
aClass354_2585
Or, use eclipse and click open declaration.
Regardless you will come upon this:
static IncomingPacket aClass354_2585 = new IncomingPacket(101, 6);
101 = PacketId
6 = PacketSize
So continuing our method:
public static void sendSkillLevel(Player player, double xp, int skill, int lvl) {
PacketBuilder pb = new PacketBuilder(101);
Next, we have a writeInt1 , which relates to xp.
So lets write it as:
pb.writeInt1((int) xp);
We cast (int) to xp, since it is a double.
Next, we have a write unsigned byte for skill
So lets write it as:
pb.writeByte(skill);
Finally, we have a write unsigned byte C for level.
So lets write it as:
pb.writeByteC(level);
Then finish with writing out our channel;
pb.write(player.getChannel());
Finished packet will look similar to this:
public static void sendSkillLevel(Player player, double xp, int skill, int level) {
PacketBuilder pb = new PacketBuilder(101);
pb.writeInt1((int) xp);
pb.writeByte(skill);
pb.writeByteC(level);
pb.write(player.getChannel());
}
Not so hard?
To start, you need to find the file that contains the mask, I've checked in the newer revisions and searching:
== '~')
Will provide the class with the masks, you can also search for hexadecimal numbers that are products of 2^X as Sinisoul stated in a response post.
Ex: 0x1, 0x2, 0x4
Ok, so lets say you found your class, now lets get to the masks.
Similar to identifying packets, it is easier to find specific masks if you know either their packet structure or methods that are attached. For this example we will use animation mask.
Conveniently some masks do not change, in this case animation mask has not changed from 614 so it is relative easy to identify.
The maskId is:
So this is our method: (Excuse the refracted method, as I don't have the old naming of this available)
[code]
if ((i & 0x80) != 0) {
int[] is = new int[4];
for (int index = 0; index < 4; index++) {
is[index] = packet.readUnsignedShort();
if (is[index] == 65535)
is[index] = -1;
}
int i_11_ = packet.readSignedByteA_();
Class257.applyAnimationUpdate(is, i_11_, player);
}
We can ignore:
if (is[index] == 65535)
is[index] = -1;
Since that is telling us if the value is equal to 65535 for the Short then it will return -1. This is due to the fact that shorts have a maximum holding value of 0 - 65535.
So by reading this we know we have a:
buffer.writeShort(value);
and:
buffer.writeByteA(value);
Pay attention to the loop as well, as that will be carried over when we write the server sided method.
Now lets move to the actual server related shizniz.
We need to locate our Player updating file, in the case of 647 it is PlayerUpdateManager.java
We need to find our playerUpdate block which contains masks, so just earch for "0x" and you will find it.
if(flags.get(UpdateFlag.ANIMATION) || multi) {
mask |= 0x80;
}
If there is an Animation update needed, we return the mask 0x80.
Then we apply the method:
if(flags.get(UpdateFlag.ANIMATION) || multi) {
applyAnimationUpdate(block, otherPlayer);
}
Now writing the actual method:
Your first line will look something similar to:
private static void applyAnimationUpdate(PacketBuilder packet, Player otherPlayer) {
Now, remember this section from the client?
for (int index = 0; index < 4; index++) {
We are going to write it exactly the same.
So your first section of the method will look like this.
private static void applyAnimationUpdate(PacketBuilder packet, Player otherPlayer) {
for(int index = 0; index < 4; index++) {
packet.writeShort(otherPlayer.getLastAnimation().g etId());
}
Now, we still have that writeByteA, which we include after the closing bracket of the for loop. So now your finished method is:
private static void applyAnimationUpdate(PacketBuilder packet, Player otherPlayer) {
for(int index = 0; index < 4; index++) {
packet.writeShort(otherPlayer.getLastAnimation().g etId());
}
packet.writeByteA(otherPlayer.getLastAnimation().g etDelay());
}
Not to tricky