Packet Analyzer

From WCell

Jump to: navigation, search

WCell's PacketAnalyzer allows anyone to parse a WoW-packet-stream into a human-readable text format.
In order to analyze packets, they need to be defined. The WCell team is currently overviewing a collection of the definition of all existing packets, written in XML for simple re-usability (see below for details).

Contents

[edit] How to start?

  • Get the Packet Analyzer by checking out WCell
    • Its located in the Utilities-folder
    • -> The PacketAnalyzer-class is defined in the WCell.PacketAnalysis-project.
  • You can find all currently defined Packets in the Run/RealmServer/Content/Packets/-folder
  • Log a packet-stream into a re-usable format and use the Packet Analyzer to produce a human-readable output file from it:
    • -> Currently KSniffer and sniffitzt format are supported - but any other format can easily be implemented
    • Load all xml-files in the given directory and all its sub-directories as Packet Definitions to the Analyzer, by calling:
      PacketAnalyzer.LoadDefinitions(string dir);
    • Call the following method to let the PacketAnalyzer create a human-readable log (outputFile) of the a KSniffer-formatted log-file (inputFile):
      LogConverter.ConvertLog(string inputFile, string outputFile);

[edit] How to use the PA Console

First start by running the WCell.Tools Project.
Type ? pa or just pa (short for Packet Analyzer) to display all possible PA-Operations.
Then type :pa to select the PA command. This way you don't have to always type the PA prefix.
Keep in mind that the command-names are not case-sensitive, so you can type pa or PA and it will lead to the same result.

You can find a detailed overview over all PA-Commands in the Docs-folder.

[edit] Example

1. Select the folder C:/logs in which we only have log-files that use the default KSniffer format

  • sd C:/logs (select the given folder and all files within)
  • sp KSniffer or: sp 0 (Select Parser for KSniffer format)
  • df otherfile.xml (deselect otherfile.xml which is not KSniffer format)

2. Select the file to extract the logs to

  • so PAOutput.txt (select output file: PAOutput.txt within the directory selected above)

3. Add filters to only display Spells, Quests or Pet packets, but exclude CMSG_PET_CAST_SPELL

  • af io quest, pet, spell ([AddFilter IncludeOr] add filter to include all packets whose names contain *either* of the given arguments (quest, pet or spell))
  • af eo CMSG_PET_CAST_SPELL ([AddFilter ExcludeOr] add filter to exclude all packets whose names contain the given argument)

4. Finally Save settings and Parse

Parse will always automatically save your settings so that when you restart the settings will still be the same.

  • parse

To save your settings without parsing, just use:

  • save

You can now find the output in the selected Output-file (default: <WCell-dir>/../Logs/PAToolOutput.txt)

[edit] Defining Packets as a sequence of Segments

You can define Packets programmatically, or -a lot easier- using XML. The structure is as follows:

  • Every Packet-Definition file starts with the XML-header:
<?xml version="1.0" encoding="utf-8"?>
  • Next comes the XML-root, called Definitions.
    • If the Version-attribute does not match with the Analyzer's version, it will show you the changelog and allow you to easily perform all necessary conversions
    • Also you should specify the location of the XSD Schema so your XML-editor can aid you when defining Packet-definitions
<Definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xsi:schemaLocation=". ../../../../Utilities/WCell.PacketAnalyzer/Xml/PacketAnalyzerSchema.xsd"
             Version="4">
		...
</Definitions>
  • Definitions can contain AuthPacket or RealmPacket-elements, which again consist of:
    • The optional Sender-attribute: Decides who is supposed to send this packet. The Sender is only necessary for packets that can be sent from Client AND Server, to define distinct structures for either case:
      • Any (default)
      • Server
      • Client
    • The OpCode-attribute
    • Zero or more PacketSegments. There are different kinds of PacketSegments:

[edit] Simple

A simple PacketSegment defines one simple entity with:

  • Name
  • Type: See Types
  • Optional SegmentType: The fully specified name of an Enum that the content of this segment should be parsed and rendered as. The WCell.Constants-namespace can be ommitted

[edit] Complex

A Complex PacketSegment is a sequence of one or more segments, defined in the Segments element.

[edit] List

A List is a repeated Complex PacketSegment, holding the Segments-element.

[edit] Default

By default, it first reads the length and then <length> times the defined sequence of Segments from the packet. The type of the length -just like Simple Segments- has to be defined in the Type-field (Byte, UInt etc).

[edit] External Length

Sometimes, the length of a list is defined in another segment, prior to the List. In that case the List specifies the segment that holds the length in the LengthSegment-attribute. If the LengthSegment is defined, the List does not have a Type and cannot be compared by a Switch(refer to the LengthSegment instead).

[edit] StaticList

A StaticList is a List with a fixed length which is defined in the Length-attribute (unlike the normal List which reads the length from the packet).

[edit] Switch

A Switch does not have a type, instead it contains a set of Cases, which will be matched against the value of the Segment, defined in the CompareWith - attribute.
When comparing Switches with List-segments, the actual comparison-value is the length of the list (since some structures vary, depending on the length of a list).

[edit] Case

Each Case consists of:

  • An attribute that describes the kind of comparison and the value to match against
  • A set of PacketSegments that represents the structure to be read when the Switch's CompareWith-segment's content's content matches this Case.

Every Case will be compared against the value that is read from the CompareWith-segment. For each matching case, the Case' structure is read from the Packet once.
The different kind of comparisons are:

  • Equals: Case matches, if the Switch's CompareWith-segment's content equals this Case' value
  • NotEqual: Case matches, if the Switch's CompareWith-segment's content is not equal to this Case' value
  • GreaterThan: Case matches, if the Switch's CompareWith-segment's content is greater than this Case' value
  • LessThan: Case matches, if the Switch's CompareWith-segment's content is less than this Case' value
  • GreaterOrEqual: Case matches, if the Switch's CompareWith-segment's content is greater than or equal to this Case' value
  • LessOrEqual: Case matches, if the Switch's CompareWith-segment's content is less than or equal to this Case' value
  • And: Case matches, if (value & CompareValue) != CompareValue
  • AndNot: Case matches, if (value & CompareValue) == 0
  • Either: Case matches if value is equal to either of a set of arguments that are seperated by comma
  • Neither: Case matches if value is not equal to any of a set of arguments that are seperated by comma

The compare-value may contain simple mathmatical expressions, where operators and operands always have to be seperated by at least one space, operator-priorities are ignored, evaluation is always from left to right and brackets are not supported.

Allowed Operators:

  • | (binary Or)
  • & (binary And)
  • ^(binary XOr)
  • + (plus)
  • - (minus)
  • * (multiply)
  • / (divide)

For example:

  • And="SomeFlag | SomeFlag2 | SomeFlag3" -> Only matches if all 3 flags are set in the given value
  • GreaterThan="1 + 3 * 5" -> Only matches if the given value is > 1 + 3 * 5 = 4*5 = 20 (since operator priorities are ignored)
  • Either="Channel, Whisper" -> Only matches if the given value is either Channel or Whisper

[edit] Update Packets

Update Packets (SMSG_UPDATE_OBJECT and SMSG_UPDATE_OBJECT_COMPRESSED) are automatically parsed (using the UpdatePacket analyzer).
An example can be found below.

[edit] Types

Simple types that a simple packet segment can consist of:

		Byte,
		UShort,
		Short,
		UInt,
		Int,
		ULong,
		Long,
		Float,
		Guid,
		PackedGuid,
		CString,
		/// <summary>
		/// String that is preceeded with its length in one byte
		/// </summary>
		PascalStringByte,
		/// <summary>
		/// String that is preceeded with its length in a UShort
		/// </summary>
		PascalStringUShort,
		/// <summary>
		/// String that is preceeded with its length as a UInt
		/// </summary>
		PascalStringUInt

[edit] XSD Schema

The PacketAnalyzer's XML-structure is defined as XSD Schema. Most XML editors are able to read these schema files and use it to give you immediate feedback and even offer you intellisense (automatic lookup and completion of symbol names).
Visual Studio ships with a pretty decent XML editor.

[edit] Examples

[edit] CMSG_LIST_INVENTORY

The client sends this packet to inspect a vendors inventory. It only contains a single argument: The vendor to be inspected.

<?xml version="1.0" encoding="utf-8"?>
<Definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xsi:schemaLocation=". ../../../../Utilities/WCell.PacketAnalyzer/Xml/PacketAnalyzerSchema.xsd"
             Version="4">
  <RealmPacket OpCode="CMSG_LIST_INVENTORY">
    <Simple>
      <Name>Vendor EntityId</Name>
      <Type>Guid</Type>
    </Simple>
  </RealmPacket>
</Definitions>

The Output could be something like this:

Packet #302 (CMSG_CAST_SPELL), Length: 4 bytes
        Spell Id: ClassSkillBlizzardRank1 (Id: 10)

[edit] CMSG_MESSAGECHAT

A chat message, sent by the client (thus the CMSG_ prefix), uses a Switch to define a varying Packet-structure. The three main parts of a chat-message are:

  • Type (defined through the WCell.Core.ChatMsgType-Enum)
  • The Language being used (defined through the WCell.Core.ChatLanguage-Enum)
  • The Switch segment matches the value of the packet in the CompareWith-segment (in this case: Type) against its set of conditions and then decides the further structure of the packet:
    • Most kinds of messages only contain the message-string
    • Whisper- and Channel-messages expect the receiver's name in addition to the message-string
<?xml version="1.0" encoding="utf-8"?>
<Definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xsi:schemaLocation=". ../../../../Utilities/WCell.PacketAnalyzer/Xml/PacketAnalyzerSchema.xsd"
             Version="4">
    <RealmPacket OpCode="CMSG_MESSAGECHAT">
      <Simple>
        <Name>Type</Name>
        <Type>UInt</Type>
        <SegmentType>WCell.Constants.ChatMsgType</SegmentType>
      </Simple>
      <Simple>
        <Name>Language</Name>
        <Type>UInt</Type>
        <SegmentType>WCell.Constants.ChatLanguage</SegmentType>
      </Simple>
      <Switch CompareWith="Type">
        <Case Equals="Say">
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
        <Case Equals="Yell">
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
        <Case Equals="Emote">
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
        <Case Equals="Party">
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
        <Case Equals="Raid">
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
        <Case Equals="Guild">
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
        <Case Equals="RaidLeader">
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
        <Case Equals="RaidWarn">
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
        <Case Equals="Whisper">
          <Simple>
            <Name>Receiver</Name>
            <Type>CString</Type>
          </Simple>
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
        <Case Equals="WhisperInform">
          <Simple>
            <Name>Receiver</Name>
            <Type>CString</Type>
          </Simple>
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
        <Case Equals="Channel">
          <Simple>
            <Name>Receiver</Name>
            <Type>CString</Type>
          </Simple>
          <Simple>
            <Name>Message</Name>
            <Type>CString</Type>
          </Simple>
        </Case>
      </Switch>
    </RealmPacket>
</Definitions>


The Output could be something like this:

Packet #149 (CMSG_MESSAGECHAT), Length: 21 bytes
        Type: Party
        Language: Common
        Message: Hey all!
Packet #149 (CMSG_MESSAGECHAT), Length: 25 bytes
        Type: Whisper
        Language: Common
        Target: Domi
        Message: <3 WCell


[edit] SMSG_QUESTGIVER_QUEST_COMPLETE

An opcode sent to the client when he has completed a quest, containing a few simple parts and a List. It contains:

  • Quest Id (uint)
  • Constant (uint) = 3
  • Reward Xp (uint)
  • Reward Money (uint)
  • Bonus honor (uint)
  • Number of forced reward items (uint) and for every forced reward item, there are two values. This structure is represented as a List
    <List>
      <Name>Reward Items Count</Name>
      <Type>UInt</Type>

This part defines the name of the part containing the list length and the type of that part.

      <Simple>
        <Name>Reward Item Id</Name>
        <Type>UInt</Type>
      </Simple>
      <Simple>
        <Name>Reward Item Amount</Name>
        <Type>UInt</Type>
      </Simple>
     </List>

This part defines structure of the list and its values.

  • Complete SMSG_QUESTGIVER_QUEST_LIST definition:
  <RealmPacket OpCode="SMSG_QUESTGIVER_QUEST_COMPLETE">
    <Simple>
      <Name>Quest Id</Name>
      <Type>UInt</Type>
    </Simple>
    <Simple>
      <Name>Always three, if it differs report it please</Name>
      <Type>UInt</Type>
    </Simple>
    <Simple>
      <Name>Reward XP</Name>
      <Type>UInt</Type>
    </Simple>
    <Simple>
      <Name>Reward money</Name>
      <Type>UInt</Type>
    </Simple>
    <Simple>
      <Name>Bonus honor</Name>
      <Type>UInt</Type>
    </Simple>
    <List>
      <Name>Reward Items Count</Name>
      <Type>UInt</Type>
      <Simple>
        <Name>Reward Item Id</Name>
        <Type>UInt</Type>
      </Simple>
      <Simple>
        <Name>Reward Item Amount</Name>
        <Type>UInt</Type>
      </Simple>
     </List>
  </RealmPacket>

[edit] Update Packets

This is how parsed Update Packets look like:

[3:03:10 AM] Update Packet:
	UpdateBlock: High: 1073741824 (0x40000000) - Low: 693277786 (FieldCount: 16)
		Type: Create

		Movement:
			UpdateFlags: HasLowGuid, HasHighGuid
			Flag0x8: 693277786
			Flag0x10: 1073741824

		Fields:
			Guid: High: 1073741824 (0x40000000) - Low: 693277786
			Type: Object, Item
			Scale X: 1
			Owner: High: Player (0x00000000) - Low: 10483211
			Contained: High: Player (0x00000000) - Low: 10483211
			Stack Count: 1
			Flags: 4294934529
			Enchantment 7: 2708
			Enchantment 10: 2708
			Enchantment 13: 2760
			Enchantment 16: 2889
			Property Seed: 1328734847
			Durability: 41

	UpdateBlock: High: 1073741824 (0x40000000) - Low: 693277747 (FieldCount: 10)
		Type: Create

		Movement:
			UpdateFlags: HasLowGuid, HasHighGuid
			Flag0x8: 693277747
			Flag0x10: 1073741824

		Fields:
			Guid: High: 1073741824 (0x40000000) - Low: 693277747
			Type: Object, Item
			Scale X: 1
			Owner: High: Player (0x00000000) - Low: 10483211
			Contained: High: Player (0x00000000) - Low: 10483211
			Stack Count: 1
			Flags: 4294934529
			Property Seed: 2214194756


[3:03:10 AM] Update Packet:
	UpdateBlock: High: Player (0x00000000) - Low: 10483211 (FieldCount: 5)
		Type: Values

		Fields:
			Aura: GoldenGryphon (Id: 32235)
			Mountdisplayid: 17697

[edit] Examples for Input format

  • sniffitzt:
<?xml version="1.0" ?>
<sniffitztlog>
  <header accountName="SomeAccount" clientBuild="8278" clientLang="enUS" realmName="Alterac Mountains" realmServer="us.logon.worldofwarcraft.com:3724" snifferVersion="Beta 6"></header>
  <packet date="1212943426" direction="S2C" opcode="103">01000000090000008F502203000000000100000000000DE1CD0300000000030000000001220100001F0000000900000019C40A04000000000100000000016F0100000900000003000000C8AB940300000000010000000001250A000046000000030000004A115303000000000100000000019B0D00003A000000080000008FDE9B030000000001000000000123100000460000000900000084DB690300000000010000000001070F00004600000007000000DC97F70300000000010000000001C50500003300000005000000F199060400000000010000000001550000001900000009000000</packet>
</sniffitztlog>
  • KSniffer:
{SERVER} Packet: (0x0132) SMSG_SPELL_GO PacketSize = 39 TimeStamp = 
|------------------------------------------------|----------------|
|00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F |0123456789ABCDEF|
|------------------------------------------------|----------------|
|0F 49 23 F9 01 0F 49 23 F9 01 25 51 00 00 00 01 |.I#...I#..%Q....|
|AE B2 F9 1C 01 43 63 32 01 00 00 00 00 00 00 80 |.....Cc2........|
|00 00 0F 43 63 32 01                            |...Cc2.         |
-------------------------------------------------------------------
  • Varied KSniffer:
{SERVER} Packet: (0x033D) SMSG_BROADCAST_MSG PacketSize = 54 TimeStamp = 17854453
01 00 00 00 57 65 6C 63 6F 6D 65 20 74 6F 20 74 68 65 20 45 75 72 6F 70 65 61 6E 20 57 6F 72 6C 64 20 6F 66 20 57 61 72 63 72 61 66 74 20 72 65 61 6C 6D 73 21 00 


[edit] How to make use of the Packet Analyzer?

  • The Dev:Packet Analyzer-Article covers how you can use the Packet Analyzer in your programs
  • WCell's Unit Tests utilize the Packet Analyzer to easily define expected server-client communication
  • When running in Debug Mode, WCell dumps all Packets into single temporary files per Account (by default - can be deactivated) that are overridden whenever an Account logs in again. Those files can be found in the Dump/ folder within the binary folder (usually: Run/RealmServer/Debug).
Personal tools