class WillowRun::Sniffer

Sniffing contains the logic to do with sniffing 802.11 frames on an interface using the airport command's sniff verb.

Example

# Typical use for the Parser class
sniffer = WillowRun::Sniffer.new
# sniff for 30 seconds
sniffer.sniff(:time => 30)

Attributes

file[RW]

file contains the path to the tcpdump capture file version 2.4, 802.11 with radiotap header

interface[RW]

interface is the interface to capture on

pcap[RW]

pcap contains the parsed tcpdump data

Public Class Methods

new(opts=false) click to toggle source

You can either create a parser object with or without data when it is iniatilized.

# File lib/willow_run/sniffer.rb, line 25
def initialize(opts=false)
  if opts
    if opts[:file]
      unless File.exists?(opts[:file])
        raise WillowRunError.new("#{opts[:file]} doesn't seem to exist!")
      else
        @file = opts[:file]
      end
      return true
    end
    if opts[:sniff]
      if opts[:channel] and opts[:interface]
        sniff(opts[:channel],opts[:interface])
      elsif opts[:interface]
        sniff("",opts[:interface])
      elsif opts[:channel]
        sniff(opts[:channel])
      else
        sniff
      end
    end
  end
  true
end

Public Instance Methods

default_interface() click to toggle source

Determine the default routeable interface, taken from packetfu's utils.rb

# File lib/willow_run/sniffer.rb, line 94
def default_interface
  ip = default_ip
  Socket.getifaddrs.each do |ifaddr|
    next unless ifaddr.addr.ip?
    return ifaddr.name if ifaddr.addr.ip_address == ip
  end
  # Fall back to libpcap as last resort
  return Pcap.lookupdev
end
default_ip() click to toggle source

Determine the default ip address, taken from packetfu's utils.rb

# File lib/willow_run/sniffer.rb, line 81
def default_ip
  begin
    orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true  # turn off reverse DNS resolution temporarily
    UDPSocket.open do |s|
      s.connect rand_routable_daddr.to_s, rand_port
      s.addr.last
    end
  ensure
    Socket.do_not_reverse_lookup = orig
  end
end
sniff(opts=false) click to toggle source

sniff() alllows the capturing packets, as long as the process as the correct privelleges. Optionally, a set of options can be passed in as a hash to set custom parameters for the sniffing. A default of 5 seconds has been given to the sniff method, whereafter the sniffing will stop / the process PID be killed.

# File lib/willow_run/sniffer.rb, line 56
def sniff(opts=false)
  if Process.uid == 0
    if opts
      interface = opts[:interface] if opts[:interface]
      channel = opts[:channel] if opts[:channel]
      timeout = opts[:timeout] if opts[:timeout]
    end
    interface = default_interface unless interface
    channel = "" unless channel
    timeout = 5 unless timeout
    stdin, stdout, stderr, wait_thr = Open3.popen3("#{AIRPORT} #{interface} sniff #{channel}")
    sleep(timeout.to_i)
    if Process.kill("KILL", wait_thr.pid)
      @file = determine_cap_file
      @pcap = Parser.new.parse(self)
    else
      raise WillowRunError.new("unable to kill pid #{wait_thr.pid}")
    end
  else
    raise WillowRunError.new("sniffing must be done with root!")
  end
  self
end

Private Instance Methods

determine_cap_file() click to toggle source

Since the airport command on Apple's part has no easy way to access the file name/path for the pcap that is generated during the sniffing process. By default, and without any options, the pcap file will be generated in /tmp as *.cap file with a random name associated with it.

# File lib/willow_run/sniffer.rb, line 123
def determine_cap_file
  # We use the most recently modified file, best we can really
  # do in this case.
  Dir.glob("/tmp/*.cap").max_by {|f| File.mtime(f)}
end
rand_port() click to toggle source

A helper for getting a random port number, taken from packetfu's utils.rb

# File lib/willow_run/sniffer.rb, line 114
def rand_port
  rand(0xffff-1024)+1024
end
rand_routable_daddr() click to toggle source

Since 177/8 is IANA reserved (for now), this network should be handled by your default gateway and default interface. Taken from packetfu's utils.rb logic.

# File lib/willow_run/sniffer.rb, line 109
def rand_routable_daddr
  IPAddr.new((rand(16777216) + 2969567232), Socket::AF_INET)
end