class PacketFu::TCPPacket

TCPPacket is used to construct TCP packets. They contain an EthHeader, an IPHeader, and a TCPHeader.

Example

tcp_pkt = PacketFu::TCPPacket.new
tcp_pkt.tcp_flags.syn=1
tcp_pkt.tcp_dst=80
tcp_pkt.tcp_win=5840
tcp_pkt.tcp_options="mss:1460,sack.ok,ts:#{rand(0xffffffff)};0,nop,ws:7"

tcp_pkt.ip_saddr=[rand(0xff),rand(0xff),rand(0xff),rand(0xff)].join('.')
tcp_pkt.ip_daddr=[rand(0xff),rand(0xff),rand(0xff),rand(0xff)].join('.')

tcp_pkt.recalc
tcp_pkt.to_f('/tmp/tcp.pcap')

tcp6_pkt = PacketFu::TCPPacket.new(:on_ipv6 => true)
tcp6_pkt.tcp_flags.syn=1
tcp6_pkt.tcp_dst=80
tcp6_pkt.tcp_win=5840
tcp6_pkt.tcp_options="mss:1460,sack.ok,ts:#{rand(0xffffffff)};0,nop,ws:7"
tcp6_pkt.ipv6_saddr="4::1"
tcp6_pkt.ipv6_daddr="12:3::4567"
tcp6_pkt.recalc
tcp6_pkt.to_f('/tmp/udp.pcap')

Parameters

:eth
  A pre-generated EthHeader object.
:ip
  A pre-generated IPHeader object.
:flavor
  TODO: Sets the "flavor" of the TCP packet. This will include TCP options and the initial window
  size, per stack. There is a lot of variety here, and it's one of the most useful methods to
  remotely fingerprint devices. :flavor will span both ip and tcp for consistency.
 :type
  TODO: Set up particular types of packets (syn, psh_ack, rst, etc). This can change the initial flavor.
:config
 A hash of return address details, often the output of Utils.whoami?

Attributes

eth_header[RW]
ip_header[RW]
ipv6_header[RW]
tcp_header[RW]

Public Class Methods

can_parse?(str) click to toggle source
# File lib/packetfu/protos/tcp.rb, line 61
def self.can_parse?(str)
  return false unless str.size >= 54
  return false unless EthPacket.can_parse? str
  if IPPacket.can_parse? str
    return true if str[23,1] == "\x06"
  elsif IPv6Packet.can_parse? str
    return true if str[20,1] == "\x06"
  end
  return false
end
new(args={}) click to toggle source
Calls superclass method PacketFu::Packet::new
# File lib/packetfu/protos/tcp.rb, line 84
def initialize(args={})
  if args[:on_ipv6] or args[:ipv6]
    @eth_header = EthHeader.new(args.merge(:eth_proto => 0x86dd)).read(args[:eth])
    @ipv6_header = IPv6Header.new(args).read(args[:ipv6])
    @tcp_header = TCPHeader.new(args).read(args[:tcp])

    @ipv6_header.body = @tcp_header
    @eth_header.body = @ipv6_header
    @headers = [@eth_header, @ipv6_header, @tcp_header]

    @ipv6_header.ipv6_next = 0x06
  else
    @eth_header = EthHeader.new(args.merge(:eth_proto => 0x0800)).read(args[:eth])
    @ip_header = IPHeader.new(args).read(args[:ip])
    @tcp_header = TCPHeader.new(args).read(args[:tcp])

    @ip_header.body = @tcp_header
    @eth_header.body = @ip_header
    @headers = [@eth_header, @ip_header, @tcp_header]

    @ip_header.ip_proto = 0x06
  end
  @tcp_header.flavor = args[:flavor].to_s.downcase

  super
  if args[:flavor]
    tcp_calc_flavor(@tcp_header.flavor)
  else
    tcp_calc_sum
  end
end

Public Instance Methods

peek_format() click to toggle source

TCP packets are denoted by a “T ”, followed by size, source and dest information, packet flags, sequence number, and IPID.

# File lib/packetfu/protos/tcp.rb, line 215
def peek_format
  if ipv6?
    peek_data = ["6T "]
    peek_data << "%-5d" % self.to_s.size
    peek_data << "%-31s" % "#{self.ipv6_saddr}:#{self.tcp_src}"
    peek_data << "->"
    peek_data << "%31s" % "#{self.ipv6_daddr}:#{self.tcp_dst}"
  else
    peek_data = ["T  "]
    peek_data << "%-5d" % self.to_s.size
    peek_data << "%-21s" % "#{self.ip_saddr}:#{self.tcp_src}"
    peek_data << "->"
    peek_data << "%21s" % "#{self.ip_daddr}:#{self.tcp_dst}"
  end
  flags = ' ['
  flags << self.tcp_flags_dotmap
  flags << '] '
  peek_data << flags
  peek_data << "S:"
  peek_data << "%08x" % self.tcp_seq
  unless ipv6?
    peek_data << "|I:"
    peek_data << "%04x" % self.ip_id
  end
  peek_data.join
end
read(str=nil, args={}) click to toggle source
Calls superclass method PacketFu::Packet#read
# File lib/packetfu/protos/tcp.rb, line 72
def read(str=nil, args={})
  super
  # Strip off any extra data, if we are asked to do so.
  if args[:strip]
    tcp_body_len = self.ip_len - self.ip_hlen - (self.tcp_hlen * 4)
    @tcp_header.body.read(@tcp_header.body.to_s[0,tcp_body_len])
    tcp_calc_sum
    @ip_header.ip_recalc
  end
  self
end
tcp_calc_flavor(str) click to toggle source

Sets the correct flavor for TCP Packets. Recognized flavors are:

windows, linux, freebsd
# File lib/packetfu/protos/tcp.rb, line 118
def tcp_calc_flavor(str)
  ts_val = Time.now.to_i + rand(0x4fffffff)
  ts_sec = rand(0xffffff)
  case @tcp_header.flavor = str.to_s.downcase
  when "windows" # WinXP's default syn
    @tcp_header.tcp_win = 0x4000
    @tcp_header.tcp_options="MSS:1460,NOP,NOP,SACKOK"
    @tcp_header.tcp_src = rand(5000 - 1026) + 1026
    @ip_header.ip_ttl = 64
  when "linux" # Ubuntu Linux 2.6.24-19-generic default syn
    @tcp_header.tcp_win = 5840
    @tcp_header.tcp_options="MSS:1460,SACKOK,TS:#{ts_val};0,NOP,WS:7"
    @tcp_header.tcp_src = rand(61_000 - 32_000) + 32_000
    @ip_header.ip_ttl = 64
  when "freebsd" # Freebsd
    @tcp_header.tcp_win = 0xffff
    @tcp_header.tcp_options="MSS:1460,NOP,WS:3,NOP,NOP,TS:#{ts_val};#{ts_sec},SACKOK,EOL,EOL"
    @ip_header.ip_ttl = 64
  else
    @tcp_header.tcp_options="MSS:1460,NOP,NOP,SACKOK"
  end
  tcp_calc_sum
end
tcp_calc_sum() click to toggle source

tcp_calc_sum() computes the TCP checksum, and is called upon intialization. It usually should be called just prior to dropping packets to a file or on the wire.

# File lib/packetfu/protos/tcp.rb, line 148
def tcp_calc_sum
  if @ipv6_header
    checksum = ipv6_calc_sum_on_addr
    tcp_len = ipv6_len
  else
    checksum = ip_calc_sum_on_addr
    tcp_len = ip_len.to_i - ((ip_hl.to_i) * 4)
  end

  checksum += 0x06 # TCP Protocol.
  checksum += tcp_len
  checksum += tcp_src
  checksum += tcp_dst
  checksum += (tcp_seq.to_i >> 16)
  checksum += (tcp_seq.to_i & 0xffff)
  checksum += (tcp_ack.to_i >> 16)
  checksum += (tcp_ack.to_i & 0xffff)
  checksum += ((tcp_hlen << 12) + 
               (tcp_reserved << 9) + 
               (tcp_ecn.to_i << 6) + 
               tcp_flags.to_i
              )
  checksum += tcp_win
  checksum += tcp_urg

  chk_tcp_opts = (tcp_opts.to_s.size % 2 == 0 ? tcp_opts.to_s : tcp_opts.to_s + "\x00") 
  chk_tcp_opts.unpack("n*").each {|x| checksum = checksum + x }
  if (tcp_len - (tcp_hlen * 4)) >= 0
    real_tcp_payload = payload[0, (tcp_len - (tcp_hlen * 4))] # Can't forget those pesky FCSes!
  else
    real_tcp_payload = payload # Something's amiss here so don't bother figuring out where the real payload is.
  end
  chk_payload = (real_tcp_payload.size % 2 == 0 ? real_tcp_payload : real_tcp_payload + "\x00") # Null pad if it's odd.
  chk_payload.unpack("n*").each {|x| checksum = checksum+x }
  checksum = checksum % 0xffff
  checksum = 0xffff - checksum
  checksum == 0 ? 0xffff : checksum
  @tcp_header.tcp_sum = checksum
end
tcp_recalc(arg=:all) click to toggle source

Recalculates various fields of the TCP packet.

Parameters

:all
  Recomputes all calculated fields.
:tcp_sum
  Recomputes the TCP checksum.
:tcp_hlen
  Recomputes the TCP header length. Useful after options are added.
# File lib/packetfu/protos/tcp.rb, line 198
def tcp_recalc(arg=:all)
  case arg
  when :tcp_sum
    tcp_calc_sum
  when :tcp_hlen
    @tcp_header.tcp_recalc :tcp_hlen
  when :all
    @tcp_header.tcp_recalc :all
    tcp_calc_sum
  else
    raise ArgumentError, "No such field `#{arg}'"
  end
end