wave

package module
v0.0.0-...-4781da5 Latest Latest
Warning

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

Go to latest
Published: Sep 27, 2020 License: MIT Imports: 6 Imported by: 1

README

Wave

Wave provides configurable MITM proxy to read and edit TCP packets from games.

PkgGoDev

Table of Content

Features

  • Hook packets by their ID
  • Edit packets on-fly
  • Prevent unnecessary packets from being sent
  • Manually create and send custom packet

Getting started

You can find full example on examples/proxy

Import the wave package from GitHub in your code:

import "github.com/Dmitriy-Vas/wave"

Create remote and local addresses, they must implement net.Addr interface.

localAddr, _ := net.ResolveTCPAddr("tcp", ":7999")
remoteAddr, _ := net.ResolveTCPAddr("tcp", "74.91.123.86:7000")

Create buffer, must implement wave.PacketBuffer interface. You can use default buffer, but I'd recommend create your own wrapper.

buf := &buffer.DefaultBuffer{
    PacketReader: &buffer.DefaultReader{Order: binary.LittleEndian},
    PacketWriter: &buffer.DefaultWriter{Order: binary.LittleEndian},
}

Create wave.Config and fill with your parameters

config := wave.Config{
    // First bytes of packet, means packet payload size
    PacketLengthSize:   wave.Size32Bits,
    // Packet type usually goes right after payload size
    PacketTypeSize:     wave.Size8Bits,
    // Sometimes PacketLengthSize may include self bytes, in this example it will be additional 32bits (4 bytes)
    LengthIncludesSelf: false,

    // Remote address of server, wave will connect to this address
    RemoteAddress:      remoteAddr,
    // Local address, wave will listen for new connection on this address
    LocalAddress:       localAddr,
    // If you want to establish connection to the remove server right after receiving local connection
    ConnectImmediately: true,

    // Implementation of wave.PacketBuffer
    Buffer:             buf,

    // In current example packet is encrypted using RC4
    // I want to decrypt on start (to read) and encrypt on end (to send)
    OutgoingProcess: func(buffer buffer.PacketBuffer, start bool) {
        raw, _ := hex.DecodeString("c79332b197f92ba85ed281a023")
        cipher, _ := rc4.NewCipher(raw)
        cipher.XORKeyStream(buffer.Bytes()[5:], buffer.Bytes()[5:])
    },
    IncomingProcess: func(buffer buffer.PacketBuffer, start bool) {
        raw, _ := hex.DecodeString("6a39570cc9de4ec71d64821894")
        cipher, _ := rc4.NewCipher(raw)
        cipher.XORKeyStream(buffer.Bytes()[5:], buffer.Bytes()[5:])
    },
}

Create new wave.Proxy instance

proxy := wave.New(config)

If you want to edit packets, you must create packet structure and register to wave.Proxy. Check lib/packets for examples.

// 1 is packet ID
// true means this packet is outgoing (from client to server)
// last parameter is a pointer to the empty packet, must implement wave.Packet interface
proxy.AddPacket(1, true, (*outgoing.NewAccountPacket)(nil))

To actually edit packet, register your hook and do magic

proxy.HookPacket(int64(lib.IncReceiveHour), false, func(conn *wave.Conn, packet wave.Packet) {
    receiveHourPacket := packet.(*incoming.ReceiveHourPacket)
    log.Printf("Changing hour from %d to 12", receiveHourPacket.Hour)
    receiveHourPacket.Hour = 12
})
Game list
  • DarkStory Online
    • Traffic is not encrypted.
    • Boolean takes 1 extra bytes (2 in total) and uses int16.
    • Byte takes 1 extra byte (2 in total) and uses int16.
    • Length goes in first 8 bytes and uses int64.
    • After length goes packet ID and uses int64.
    • Most part of packets were added and ready for use.
Contributing

!!!Need help with typos!!!
If you want to contribute, fork this project, commit changes and create pull request. Please describe your changes and what they are doing in pull request.

Documentation

Index

Constants

View Source
const (
	// Size64Bits specifies size for long number (int64 and uint64)
	Size64Bits = 8
	// Size32Bits specifies size for int number (int32 and uint32)
	Size32Bits = 4
	// Size16Bits specifies size for short number (int16 and uint16)
	Size16Bits = 2
	// Size8Bits specifies size for byte and bool (int8 and uint8)
	Size8Bits = 1
)

Variables

This section is empty.

Functions

func InitializeStruct

func InitializeStruct(t reflect.Type) interface{}

InitializeStruct Creates new instance from type.

func ReadNumber

func ReadNumber(buffer buffer.PacketBuffer, size uint64) (result int64)

ReadNumber reads any number with any type, based on specified size (in bytes). For example, if size=32bits, int (4 bytes) will be read from the buffer.

func WriteNumber

func WriteNumber(buffer buffer.PacketBuffer, size uint64, value int64)

WriteNumber writes any number with any type, based on specified size (in bytes). For example, if size=32bits, int (4 bytes) will be written to the buffer.

Types

type Config

type Config struct {
	PacketLengthSize   uint64   // How much bytes will be used to read packet length.
	PacketTypeSize     uint64   // How much bytes will be used to read packet type.
	LengthIncludesSelf bool     // Packet length includes payload size with PacketLengthSize.
	RemoteAddress      net.Addr // Target address, will be used to establish connection.
	LocalAddress       net.Addr // Local address, will be used to listen for new connection.
	ConnectImmediately bool     // If true, will connect to the remote host right after receiving new connection from listener.
	// Pointer to the implementation of PacketBuffer.
	// example: &buffer.DefaultBuffer{
	//		PacketReader: &buffer.DefaultReader{Order: binary.LittleEndian},
	//		PacketWriter: &buffer.DefaultWriter{Order: binary.LittleEndian},
	//	}
	Buffer buffer.PacketBuffer
	// This functions fires twice, when packet fully received (start=true) and before sending to the destination (start=false)
	// Useful to cipher/decipher packets
	OutgoingProcess Process
	IncomingProcess Process
}

type Conn

type Conn struct {
	ID                    uint32   // Connection's unique ID
	LocalConn, RemoteConn net.Conn // Bidirectional streams (client <-> proxy <-> server)
	Done                  chan error
	// contains filtered or unexported fields
}

Conn represents current connection.

func (*Conn) Buffer

func (c *Conn) Buffer() buffer.PacketBuffer

Buffer returns connection's PacketBuffer. Do not use this buffer in multiple goroutines, otherwise you will get data race. If you want to pass PacketBuffer to goroutine, use Clone on buffer and use replica only.

example: buffer := conn.Buffer()

go func(buffer PacketBuffer) {
	Do something with replica here
}(buffer.Clone())

func (*Conn) Close

func (c *Conn) Close(err error)

Close closes the connection, error is optionally.

func (*Conn) Reconnect

func (c *Conn) Reconnect(RemoteAddress net.Addr)

Reconnect connects to the remote address, closes (if exists) current connection.

func (*Conn) SendPacket

func (c *Conn) SendPacket(buffer buffer.PacketBuffer, packet Packet, outgoing bool) error

Writes packet data to the buffer and sends to the destination. Useful for manually sending packets. Outgoing=true, if you want to write to the server. Outgoing=false, if you want to write to the client.

type Packet

type Packet interface {
	GetID() int64
	SetID(id int64)
	SetSend(value bool)
	GetSend() bool
	Read(b buffer.PacketBuffer)
	Write(b buffer.PacketBuffer)
}

type PacketHook

type PacketHook func(conn *Conn, packet Packet)

PacketHook is a shortcut for packet hook function. Conn is a connection, which are received packet. Packet is an interface Packet, can be asserted to the actual packet type.

type Process

type Process func(buffer buffer.PacketBuffer, start bool)

Process represents function to process packet data. Firstly executes on fully received packet (start=true). Next time executes right before sending to the destination (start=false).

type Proxy

type Proxy struct {
	Connections                              *sync.Map // map[uint32]*Conn. Concurrency safe. Map with all current connections
	OutgoingPacketHooks, IncomingPacketHooks *sync.Map // map[id][]PacketHook Concurrency safe. Map with all registered packet hooks
	OutgoingPacketMap, IncomingPacketMap     *sync.Map // map[id]reflect.Type. Concurrency safe.
	Config                                   Config    // Proxy configuration
	// contains filtered or unexported fields
}

func New

func New(config Config) *Proxy

New creates new proxy instance with specified config.

func (*Proxy) AddPacket

func (p *Proxy) AddPacket(ID int64, outgoing bool, packet Packet)

AddPacket adds Packet to the list of available to hook packets. Pass empty Packet, example: (*SomePacket)(nil). Outgoing=true, if your packet is going from client to server. Outgoing=false, if your packet is going from server to client.

func (*Proxy) Close

func (p *Proxy) Close()

Close closes every connection and stops the listener.

func (*Proxy) HookPacket

func (p *Proxy) HookPacket(ID int64, outgoing bool, hook PacketHook)

HookPacket adds function, which will be fired for every packet with specified ID. Outgoing=true, if your packet is going from client to server. Outgoing=false, if your packet is going from server to client.

func (*Proxy) Index

func (p *Proxy) Index() uint32

Index returns current amount of served connections.

func (*Proxy) NewConn

func (p *Proxy) NewConn(id uint32, conn net.Conn) *Conn

func (*Proxy) Start

func (p *Proxy) Start() (err error)

Start starts listening on LocalAddress and accept new connections.

Jump to

Keyboard shortcuts

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