PDA

View Full Version : Basic Introduction to Identifying Packets



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

EvolvedBlitz
July 18th, 2011, 17:33
Wow I cant imagine on how long this took nice job :D

mumups
July 18th, 2011, 17:35
I like

Metrocoptic
July 18th, 2011, 17:43
edit: sry I didnt read the intro, I get it :D
good job emily

SkrilleX
July 18th, 2011, 17:43
Damn Nj emily

Stacx
July 18th, 2011, 17:59
omfg dont look at the values being written look at the methods that the values are passed to :fp:fp:fp:fp:

some numbers and operations hardly change where they root it saves shitload of time lul

Emily
July 18th, 2011, 18:04
omfg dont look at the values being written look at the methods that the values are passed to :fp:fp:fp:fp:

Send skills is an out liar to your method, since array values are set equal the the values being written.

While some/most methods do have methods they write to, which contain more information.

Like:


/*
* Set Camera Angle, Packet Id: 29
*/
if (Class364.aClass354_3104 == Class26.currentIncommingPacket) {
int angleX = Class274.packet.readUnsignedLEShort();
int angleY = Class274.packet.readUnsignedShortA();
Class82.method588(true);
Class186_Sub41_Sub23.setCameraAngle(angleY, angleX, 0);
Class26.currentIncommingPacket = null;
return true;
}




static final void setCameraAngle(int angleY, int angleX, int height) {
angleY <<= 3;
height <<= 3;
angleX <<= 3;
if (Class6_Sub3.anInt5509 == 2) {
Class336_Sub2.anInt5336 = angleY;
Class229.anInt1917 = height;
Class186_Sub18.anInt4560 = angleX;
}
Class95.aFloat688 = (float) angleX;
Class169.aFloat1451 = (float) angleY;
Class101.method713();
Class348_Sub1.aBoolean3447 = true;
}

Stacx
July 18th, 2011, 18:06
Send skills is an out liar to your method, since array values are set equal the the values being written.

While some/most methods do have methods they write to, which contain more information.

Like:


/*
* Set Camera Angle, Packet Id: 29
*/
if (Class364.aClass354_3104 == Class26.currentIncommingPacket) {
int angleX = Class274.packet.readUnsignedLEShort();
int angleY = Class274.packet.readUnsignedShortA();
Class82.method588(true);
Class186_Sub41_Sub23.setCameraAngle(angleY, angleX, 0);
Class26.currentIncommingPacket = null;
return true;
}




static final void setCameraAngle(int angleY, int angleX, int height) {
angleY <<= 3;
height <<= 3;
angleX <<= 3;
if (Class6_Sub3.anInt5509 == 2) {
Class336_Sub2.anInt5336 = angleY;
Class229.anInt1917 = height;
Class186_Sub18.anInt4560 = angleX;
}
Class95.aFloat688 = (float) angleX;
Class169.aFloat1451 = (float) angleY;
Class101.method713();
Class348_Sub1.aBoolean3447 = true;
}



Class186_Sub41_Sub36.method2377(31, Class248.anInt2086++)

Emily
July 18th, 2011, 18:09
Class186_Sub41_Sub36.method2377(31, Class248.anInt2086++)

That is setting an array index to the skill value.

But I do see what you are saying with what it draws back to.

647:


static int method2377(int i, int i_22_) {
return i & i_22_;
}


656:


static int method2187(int i, int j)
{
return i & j;
}


Which you are basically saying is easier to identify what a packet does. Correct?

Stacx
July 18th, 2011, 18:12
That is setting an array index to the skill value.

But I do see what you are saying with what it draws back to.

647:


static int method2377(int i, int i_22_) {
return i & i_22_;
}


656:


static int method2187(int i, int j)
{
return i & j;
}


Which you are basically saying is easier to identify what a packet does. Correct?

not neccesarily it's purpose but actually to find the packet, you can compare to an older client(for example 508 has every packet identified)
(also, +2 and - 1 from the end of the line can be useful)
but the method of comparing the values, operators and the possible count of specifed values in a packet can be done after you've located the actual packet, for example if there's multiple packets equal to the method it roots to.

SiniSoul
July 18th, 2011, 18:32
Outgoing packets have a class that represents their size and number, since that wasn't in here. Emily you didn't explain ISAAC because the best way to find where packets are parsed is the 'T1' thing but its also good to fully rewrite the RSBuffer_Sub2 (Packet Buffer) to get where the ISAAC values are popped because everything that comes in is encrypted and everything that goes out is encrypted.

Good article <3



final void putPacketId(int i, int j)
{
super.buffer[super.seek_req++] = (byte)(encryption.pop((byte)125) + i);
anInt10169++;
}


Clients can now handle (incoming) more than 255 packets as you can see.



final int getPacketId(int i)
{
anInt10166++;
int j = 0xff & super.buffer[super.seek_req++] - encryption.pop((byte)126);
if(j < 128)
return j;
else
return (super.buffer[super.seek_req++] - encryption.pop((byte)126) & 0xff) + (-128 + j << 8);
}




final boolean getLength2(int junk)
{
anInt10167++;
int j = super.buffer[super.seek_req] - (encryption.poll(100) & 0xff);
if(j < 128)
return false;
return true;
}


SOOOOOOOOOOOOOOOO, this may be the cause for var type int? :3

tedhead2
July 18th, 2011, 18:38
omg emily u learned me soo muchh ty!@@#!12

own4g3
July 18th, 2011, 18:50
Thanks for this!!
identifying masks now, plox!

SiniSoul
July 18th, 2011, 18:53
Thanks for this!!
identifying masks now, plox!

Search for hexidecimal numbers that are products of 2^x



0x1
0x2
0x4
0x8
0x10
0x20

RuneSoftware
July 18th, 2011, 18:54
Not a bad tutorial, a long is 64bit though.

SiniSoul
July 18th, 2011, 18:56
Not a bad tutorial, a long is 64bit though.

?????

RuneSoftware
July 18th, 2011, 18:57
?????

Only the registered members can see the link.

SiniSoul
July 18th, 2011, 18:57
Only the registered members can see the link.

I know but I see no indication within the article that she said that long was not 64 bit.

RuneSoftware
July 18th, 2011, 18:58
I know but I see no indication within the article that she said that long was not 64 bit.

Extended information.

SiniSoul
July 18th, 2011, 18:59
Extended information.

Totally irrelevant, it was automatically assumed you knew that.

bl00dshooter
July 18th, 2011, 19:01
Extended information.

Jonah Falcon, a 40 year-old american, is the man with the world's largest penis, measured at approximately 13.5 inches (34cm) when hard.


...extended information?

SiniSoul
July 18th, 2011, 19:01
Jonah Falcon, a 40 year-old american, is the man with the world's largest penis, measured at approximately 13.5 inches (34cm) when hard.


...extended information?

Back on topic please.

RuneSoftware
July 18th, 2011, 19:03
Totally irrelevant, it was automatically assumed you knew that.

It's still misleading to members who don't know.

Emily
July 18th, 2011, 19:06
Only the registered members can see the link.

Sorry was going off of this: Only the registered members can see the link.

Will fix when I get home.

dragonkk
July 19th, 2011, 08:17
People will fail even with this tuto. They don't know what bits are L0L. Remember last time i explained? Not sure if it was you or caleum. took awhile

Stacx
July 19th, 2011, 09:14
People will fail even with this tuto. They don't know what bits are L0L. Remember last time i explained? Not sure if it was you or caleum. took awhile

binary digits herp derp