zanarkand

package module
v0.0.0-...-5929559 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 20, 2021 License: MIT Imports: 16 Imported by: 1

README

Zanarkand

Build Status Code Quality GitHub Issues GitHub Pull Requests GitHub License Discord

Zanarkand is a library to read FFXIV network traffic from PCAP, AF_Packet, or PCAP files. It can additionally handle TCP reassembly and provides an interface for IPC frame decoding.

For Windows users, elevated security privileges may be required, as well as a local firewall exemption.

To use the library, you need to instantiate a Sniffer and then loop NextFrame in it once it starts. For each Frame, you can then iterate Messages in it. Helper methods are available to filter Segment and Opcodes from Frames and Messages respectively. The Sniffer can be stopped and restarted at any time.

Example

import (
	"flag"
	"fmt"
	"log"

	"github.com/ayyaruq/zanarkand"
)

func main() {
	// Open flags for debugging if wanted (-assembly_debug_log)
	flag.Parse()

	// Setup the Sniffer
	sniffer, err := zanarkand.NewSniffer("pcap", "en0")
	if err != nil {
		log.Fatal(err)
	}

	// Create a channel to receive Messages on
	subscriber := zanarkand.NewGameEventSubscriber()

	// Don't block the Sniffer, but capture errors
	go func() {
		err := subscriber.Subscribe(sniffer)
		if err != nil {
			log.Fatal(err)
		}
	}()

// Capture the first 10 Messages sent from the server
// This ignores Messages sent by the client to the server
	for i := 0; i < 10; i++ {
		message := <-subscriber.IngressEvents
		fmt.Println(message.String())
	}

	// Stop the sniffer
	subscriber.Close(sniffer)
}

Developing

To start, install Go 1.13 or later. For ease of error handling, the Go 1.13 error wrapping features are used and so this is the minimum supported version.

To add to your project, simple go mod init if you don't already have a go.mod file, and then go get -u github.com/ayyaruq/zanarkand.

Contributing

Once you have a Go environment setup, install dependencies with make deps.

Zanarkand follows the normal go fmt for style. All methods and types should be at least somewhat documented, beyond that develop as you will as there's no specific expectations. Changes are best submited as pull-requests in GitHub.

Regarding versioning, at this point it's probably overkill, as opcodes and types are externalised and so there's no real need to have explicit versions on Zanarkand itself.

TODO

Documentation

Overview

Package zanarkand provides TCP packet reassembly for FFXIV network streams and some simple interfaces to access them. The main use is for capturing FFXIV IPC Messages for parsing or analysis of game events. In FFXIV, communication between the client and server is done via RPC. Each IPC message is identified by a Segment ID to identify it's payload type, and some Segment-specific fields such as opcodes to identify IPC types.. One or more Messages are then wrapped into a Frame, optionally compressed with ZLIB, and transmitted over TCP.

Index

Constants

View Source
const (
	SessionInit = 1
	SessionRecv = 2

	GameEvent = 3

	ServerPing = 7
	ServerPong = 8

	EncryptInit = 9
	EncryptRecv = 10
)

Segment types separate messages into their relevant field maps. Session/Encryption types are not implemented due to them largely only containing the player ID, or information that should be kept hidden due to privacy concerns. While this may inconvenience debugging, this project will not facilitate capturing player login details.

Variables

This section is empty.

Functions

This section is empty.

Types

type ErrDecodingFailure

type ErrDecodingFailure struct {
	Err error
}

ErrDecodingFailure indicates an error during decoding a specific message type.

func (ErrDecodingFailure) Error

func (e ErrDecodingFailure) Error() string

func (*ErrDecodingFailure) Unwrap

func (e *ErrDecodingFailure) Unwrap() error

type ErrNotEnoughData

type ErrNotEnoughData struct {
	Expected int
	Received int
	Err      error
}

ErrNotEnoughData occurs when the Length field is longer than the payload.

func (ErrNotEnoughData) Error

func (e ErrNotEnoughData) Error() string

func (*ErrNotEnoughData) Unwrap

func (e *ErrNotEnoughData) Unwrap() error

type ErrUnknownInput

type ErrUnknownInput struct {
	Err error
}

ErrUnknownInput indicates the provided mode for a sniffer is not a known type.

func (*ErrUnknownInput) Unwrap

func (e *ErrUnknownInput) Unwrap() error

type FlowDirection

type FlowDirection int

FlowDirection indicates the flow being inbound or outbound.

const (
	FrameIngress FlowDirection = 1
	FrameEgress  FlowDirection = 2
)

FrameIngress is an inbound Frame. FrameEgress is an outbound Frame.

type Frame

type Frame struct {
	Magic      uint64    `json:"-"`              // [0:8] - mainly to verify magic bytes
	Timestamp  time.Time `json:"-"`              // [16:24] - timestamp in milliseconds since epoch
	Length     uint32    `json:"size"`           // [24:28]
	Connection uint16    `json:"connectionType"` // [28:30] - 0 lobby, 1 zone, 2 chat
	Count      uint16    `json:"count"`          // [30:32]

	Compressed bool `json:"compressed"` // [33] UINT8 bool tho

	Body []byte `json:"-"`
	// contains filtered or unexported fields
}

Frame is an FFXIV bundled message encapsulation layer. Currently, bytes 4:7, 8:15, 32, and 34:39 are unknown.

func (*Frame) Decode

func (f *Frame) Decode(p []byte)

Decode a frame from byte data

func (*Frame) Direction

func (f *Frame) Direction() FlowDirection

Direction outputs if the Frame is inbound or outbound.

func (*Frame) MarshalJSON

func (f *Frame) MarshalJSON() ([]byte, error)

MarshalJSON provides an override for timestamp handling for encoding/JSON

func (*Frame) Meta

func (f *Frame) Meta() *FrameMeta

Meta returns the frame metadata, a gopacket.Flow this allows the user to determine if a Frame is inbound or outbound.

func (*Frame) String

func (f *Frame) String() string

String provides a string representation of a frame header.

type FrameMeta

type FrameMeta struct {
	Flow gopacket.Flow
}

FrameMeta represents metadata from the IP and TCP layers on the Frame.

type GameEventMessage

type GameEventMessage struct {
	GenericHeader

	Opcode uint16 `json:"opcode"` // [18:20] - message context identifier, the "opcode"

	ServerID  uint16    `json:"serverID"` // [22:24]
	Timestamp time.Time `json:"-"`        // [24:28]

	Body []byte `json:"-"`
	// contains filtered or unexported fields
}

GameEventMessage is a pre-type casted GameEventHeader and body.

func (*GameEventMessage) Decode

func (m *GameEventMessage) Decode(r *bufio.Reader) error

Decode turns a byte payload into a real GameEventMessage.

func (GameEventMessage) IsMessage

func (GameEventMessage) IsMessage()

IsMessage confirms a GameEventMessage is a Message.

func (*GameEventMessage) MarshalJSON

func (m *GameEventMessage) MarshalJSON() ([]byte, error)

MarshalJSON provides an override for timestamp handling for encoding/JSON

func (*GameEventMessage) String

func (m *GameEventMessage) String() string

String prints a Segment and IPC Message specific headers.

type GameEventSubscriber

type GameEventSubscriber struct {
	IngressEvents chan *GameEventMessage
	EgressEvents  chan *GameEventMessage
}

GameEventSubscriber is a Subscriber for GameEvent segments.

func NewGameEventSubscriber

func NewGameEventSubscriber() *GameEventSubscriber

NewGameEventSubscriber returns a Subscriber handle with channels for inbound and outbound GameEventMessages.

func (*GameEventSubscriber) Close

func (g *GameEventSubscriber) Close(s *Sniffer)

Close will stop a sniffer, drain the channels, then close the channels.

func (*GameEventSubscriber) Subscribe

func (g *GameEventSubscriber) Subscribe(s *Sniffer) error

Subscribe starts the GameEventSubscriber.

type GenericHeader

type GenericHeader struct {
	Length      uint32 `json:"size"`          // [0:4]
	SourceActor uint32 `json:"sourceActorID"` // [4:8]
	TargetActor uint32 `json:"targetActorID"` // [8:12]
	Segment     uint16 `json:"segmentType"`   // [12:14]
	// contains filtered or unexported fields
}

GenericHeader provides the metadata for an FFXIV IPC. 0:16 provide the generic header, other types have additional header fields. Data pulled from Sapphire's `Network/CommonNetwork.h`

func (*GenericHeader) Decode

func (m *GenericHeader) Decode(r *bufio.Reader) error

Decode a GenericHeader from a byte array.

func (*GenericHeader) String

func (m *GenericHeader) String() string

String is a stringer for the GenericHeader of a Message.

type GenericMessage

type GenericMessage interface {
	Decode(*bufio.Reader) error
	IsMessage()
	MarshalJSON() ([]byte, error)
	String() string
}

GenericMessage is an interface for other Message types to make the Framer generic.

type KeepaliveMessage

type KeepaliveMessage struct {
	GenericHeader
	ID        uint32    `json:"ID"` // [16:20]
	Timestamp time.Time `json:"-"`  // [20:24]
}

KeepaliveMessage is a representation of ping/pong requests.

func (*KeepaliveMessage) Decode

func (m *KeepaliveMessage) Decode(r *bufio.Reader) error

Decode turns a byte payload into a real KeepaliveMessage.

func (KeepaliveMessage) IsMessage

func (KeepaliveMessage) IsMessage()

IsMessage confirms a KeepaliveMessage is a Message.

func (*KeepaliveMessage) MarshalJSON

func (m *KeepaliveMessage) MarshalJSON() ([]byte, error)

MarshalJSON provides an override for timestamp handling for encoding/JSON

func (*KeepaliveMessage) String

func (m *KeepaliveMessage) String() string

String prints the Segment header and Keepalive Message.

type KeepaliveSubscriber

type KeepaliveSubscriber struct {
	Events chan *KeepaliveMessage
}

KeepaliveSubscriber is a Subscriber for Keepalive segments.

func NewKeepaliveSubscriber

func NewKeepaliveSubscriber() *KeepaliveSubscriber

NewKeepaliveSubscriber returns a Subscriber handle. As the traffic is minimal, this subscriber uses a single Event channel.

func (*KeepaliveSubscriber) Close

func (k *KeepaliveSubscriber) Close(s *Sniffer)

Close will stop a sniffer, drain the channel, then close the channel.

func (*KeepaliveSubscriber) Subscribe

func (k *KeepaliveSubscriber) Subscribe(s *Sniffer) error

Subscribe starts the KeepaliveSubscriber.

type Sniffer

type Sniffer struct {
	// Sniffer State
	Active bool
	Status string

	Source *gopacket.PacketSource
	// contains filtered or unexported fields
}

Sniffer is a representation of a packet source, filter, and destination.

func NewSniffer

func NewSniffer(mode, src string) (*Sniffer, error)

NewSniffer creates a Sniffer instance.

func (*Sniffer) NextFrame

func (s *Sniffer) NextFrame() (*Frame, error)

NextFrame returns the next decoded Frame read by the Sniffer.

func (*Sniffer) Start

func (s *Sniffer) Start() error

Start an initialised Sniffer.

func (*Sniffer) Stop

func (s *Sniffer) Stop()

Stop a running Sniffer.

type Subscriber

type Subscriber interface {
	Subscribe(*Sniffer)
	Close()
}

Subscriber describes the interface for individual Frame segment subscribers.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL