Updating PacketFu to 1.0.2
git-svn-id: file:///home/svn/framework3/trunk@12689 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
28d5febfad
commit
1efb6a1ff2
107
lib/packetfu.rb
107
lib/packetfu.rb
|
@ -1,109 +1,4 @@
|
|||
|
||||
# :title: PacketFu Documentation
|
||||
# :include: ../README
|
||||
# :include: ../INSTALL
|
||||
# :include: ../LICENSE
|
||||
|
||||
$: << File.expand_path(File.dirname(__FILE__))
|
||||
require "packetfu/structfu"
|
||||
require "ipaddr"
|
||||
require 'rubygems' if RUBY_VERSION =~ /^1\.[0-8]/
|
||||
|
||||
module PacketFu
|
||||
|
||||
# Sets the expected byte order for a pcap file. See PacketFu::Read.set_byte_order
|
||||
@byte_order = :little
|
||||
|
||||
# Checks if pcaprub is loaded correctly.
|
||||
@@pcaprub_loaded = false
|
||||
|
||||
# PacketFu works best with Pcaprub version 0.8-dev (at least)
|
||||
# The current (Aug 01, 2010) pcaprub gem is 0.9, so should be fine.
|
||||
def self.pcaprub_platform_require
|
||||
begin
|
||||
require 'pcaprub'
|
||||
rescue LoadError
|
||||
return false
|
||||
end
|
||||
@@pcaprub_loaded = true
|
||||
end
|
||||
|
||||
pcaprub_platform_require
|
||||
if @@pcaprub_loaded
|
||||
if Pcap.version !~ /[0-9]\.[7-9][0-9]?(-dev)?/ # Regex for 0.7-dev and beyond.
|
||||
@@pcaprub_loaded = false # Don't bother with broken versions
|
||||
raise LoadError, "PcapRub not at a minimum version of 0.8-dev"
|
||||
end
|
||||
require "packetfu/capture"
|
||||
require "packetfu/inject"
|
||||
end
|
||||
|
||||
end
|
||||
require 'packetfu/packetfu'
|
||||
|
||||
require "packetfu/pcap"
|
||||
require "packetfu/packet"
|
||||
require "packetfu/invalid"
|
||||
require "packetfu/eth"
|
||||
require "packetfu/ip"
|
||||
require "packetfu/arp"
|
||||
require "packetfu/icmp"
|
||||
require "packetfu/udp"
|
||||
require "packetfu/hsrp" # Depends on UDP
|
||||
require "packetfu/tcp"
|
||||
require "packetfu/ipv6" # This is pretty minimal.
|
||||
require "packetfu/utils"
|
||||
require "packetfu/config"
|
||||
|
||||
module PacketFu
|
||||
|
||||
# Version 1.0.0 was released July 31, 2010
|
||||
# Version 1.0.1 is unreleased.
|
||||
VERSION = "1.0.1"
|
||||
|
||||
# Returns the current version of PacketFu. Incremented every once
|
||||
# in a while, when I remember
|
||||
def self.version
|
||||
PacketFu::VERSION
|
||||
end
|
||||
|
||||
# Returns the version in a binary format for easy comparisons.
|
||||
def self.binarize_version(str)
|
||||
if(str.respond_to?(:split) && str =~ /^[0-9]+(\.([0-9]+)(\.[0-9]+)?)?$/)
|
||||
bin_major,bin_minor,bin_teeny = str.split(/\x2e/).map {|x| x.to_i}
|
||||
bin_version = (bin_major.to_i << 16) + (bin_minor.to_i << 8) + bin_teeny.to_i
|
||||
else
|
||||
raise ArgumentError, "Compare version malformed. Should be \x22x.y.z\x22"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the version is equal to or greater than the compare version.
|
||||
# If the current version of PacketFu is "0.3.1" for example:
|
||||
#
|
||||
# PacketFu.at_least? "0" # => true
|
||||
# PacketFu.at_least? "0.2.9" # => true
|
||||
# PacketFu.at_least? "0.3" # => true
|
||||
# PacketFu.at_least? "1" # => true after 1.0's release
|
||||
# PacketFu.at_least? "1.12" # => false
|
||||
# PacketFu.at_least? "2" # => false
|
||||
def self.at_least?(str)
|
||||
this_version = binarize_version(self.version)
|
||||
ask_version = binarize_version(str)
|
||||
this_version >= ask_version
|
||||
end
|
||||
|
||||
# Returns true if the current version is older than the compare version.
|
||||
def self.older_than?(str)
|
||||
this_version = binarize_version(self.version)
|
||||
ask_version = binarize_version(str)
|
||||
this_version < ask_version
|
||||
end
|
||||
|
||||
# Returns true if the current version is newer than the compare version.
|
||||
def self.newer_than?(str)
|
||||
return false if str == self.version
|
||||
!self.older_than?(str)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
== INSTALL
|
||||
|
||||
Installation is pretty straightforward -- it's a gem now!
|
||||
|
||||
$ rvm gem install packetfu
|
||||
|
||||
Not using rvm? For shame! Get it now, it will make your life 100x better.
|
||||
|
||||
$ links http://rvm.beginrescueend.com/
|
||||
|
||||
If you are installing from a source checkout, just run (as root / rvmsudo):
|
||||
|
||||
$ rvmsudo ./setup.rb
|
||||
$ sudo ruby ./setup.rb # If not on rvm, and seriously what is wrong with you?
|
||||
|
||||
== Testing
|
||||
|
||||
The easiest way to test the installation is to run PacketFu via
|
||||
irb, using the example shell in the "examples" directory:
|
||||
|
||||
% sudo irb -r packetfu-shell.rb
|
||||
|
||||
After the banner, you should see something like:
|
||||
|
||||
>>> Use $packetfu_default.config for salient networking details.
|
||||
IP: 192.168.1.100 Mac: 00:1d:e0:54:2f:7e Gateway: 00:03:2f:32:a5:3c
|
||||
Net: 192.168.1.0 Iface: wlan0
|
||||
>>> Packet capturing/injecting enabled.
|
||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||
|
||||
If not, then Something Went Wrong. It's most likely that you have either
|
||||
an older or broken version of pcaprub (try installing the version provided
|
||||
with Metasploit), or you have a very, very old version of libpcap (version
|
||||
0.9.4 is the oldest tested version, and there's really no reason to not
|
||||
be at least on 1.0.0).
|
||||
|
||||
== Complaints
|
||||
|
||||
If things don't work out, please contact todb@planb-security.net, and I'll
|
||||
try to get you all sorted out.
|
|
@ -1,28 +0,0 @@
|
|||
== LICENSE
|
||||
|
||||
Copyright (c) 2008-2010, Tod Beardsley
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Tod Beardsley nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY TOD BEARDSLEY ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL TOD BEARDSLEY BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
= PacketFu
|
||||
|
||||
A library for reading a writing packets to an interface or to a libpcap-formatted file.
|
||||
It is maintained at http://code.google.com/p/packetfu
|
||||
|
||||
== Documentation
|
||||
|
||||
PacketFu is rdoc-compatable. In the same directory as this file, run "rdoc" by itself, and then view doc/index.html with your favored browser. Once that's done, navigate at the top, and read up on how to create a Packet or Capture from an interface with show_live or whatever.
|
||||
|
||||
== Requirements
|
||||
|
||||
PcapRub:
|
||||
|
||||
$ svn co http://www.metasploit.com/svn/framework3/trunk/external/pcaprub
|
||||
|
||||
or
|
||||
|
||||
$ rvm gem install pcaprub
|
||||
|
||||
Marshall Beddoe's PcapRub is required only for packet reading and writing from a network interfaces (which is a pretty big only). PcapRub itself relies on libpcap 0.9.8 or later for packet injection. PcapRub also requires root privilieges to access the interface directly.
|
||||
|
||||
== Examples
|
||||
|
||||
PacketFu ships with dozens and dozens of tests, built on Test::Unit. These should give good pointers on how you're expected to use it. See the /tests directory. Furthermore, PacketFu also ships with packetfu-shell.rb, which should be run via IRB (as root, if you intend to use your interfaces).
|
||||
|
||||
== Author
|
||||
|
||||
PacketFu is maintained primarily by Tod Beardsley <todb@planb-security.net>
|
|
@ -0,0 +1,93 @@
|
|||
|
||||
# :title: PacketFu Documentation
|
||||
# :include: ../README
|
||||
# :include: ../INSTALL
|
||||
# :include: ../LICENSE
|
||||
|
||||
cwd = File.expand_path(File.dirname(__FILE__))
|
||||
|
||||
$: << cwd
|
||||
|
||||
require File.join(cwd,"packetfu","structfu")
|
||||
require "ipaddr"
|
||||
require 'rubygems' if RUBY_VERSION =~ /^1\.[0-8]/
|
||||
|
||||
module PacketFu
|
||||
|
||||
# Sets the expected byte order for a pcap file. See PacketFu::Read.set_byte_order
|
||||
@byte_order = :little
|
||||
|
||||
# Checks if pcaprub is loaded correctly.
|
||||
@pcaprub_loaded = false
|
||||
|
||||
# PacketFu works best with Pcaprub version 0.8-dev (at least)
|
||||
# The current (Aug 01, 2010) pcaprub gem is 0.9, so should be fine.
|
||||
def self.pcaprub_platform_require
|
||||
begin
|
||||
require 'pcaprub'
|
||||
rescue LoadError
|
||||
return false
|
||||
end
|
||||
@pcaprub_loaded = true
|
||||
end
|
||||
|
||||
pcaprub_platform_require
|
||||
if @pcaprub_loaded
|
||||
if Pcap.version !~ /[0-9]\.[7-9][0-9]?(-dev)?/ # Regex for 0.7-dev and beyond.
|
||||
@pcaprub_loaded = false # Don't bother with broken versions
|
||||
raise LoadError, "PcapRub not at a minimum version of 0.8-dev"
|
||||
end
|
||||
require "packetfu/capture"
|
||||
require "packetfu/inject"
|
||||
end
|
||||
|
||||
def self.pcaprub_loaded?
|
||||
@pcaprub_loaded
|
||||
end
|
||||
|
||||
# Returns an array of classes defined in PacketFu
|
||||
def self.classes
|
||||
constants.map { |const| const_get(const) if const_get(const).kind_of? Class}.compact
|
||||
end
|
||||
|
||||
def self.add_packet_class(klass)
|
||||
raise "Need a class" unless klass.kind_of? Class
|
||||
if klass.name !~ /[A-Za-z0-9]Packet/
|
||||
raise "Packet classes should be named 'ProtoPacket'"
|
||||
end
|
||||
@packet_classes ||= []
|
||||
@packet_classes << klass
|
||||
@packet_classes.sort! {|x,y| x.name <=> y.name}
|
||||
end
|
||||
|
||||
def self.packet_classes
|
||||
@packet_classes || []
|
||||
end
|
||||
|
||||
def self.packet_prefixes
|
||||
return [] unless @packet_classes
|
||||
@packet_classes.map {|p| p.to_s.split("::").last.to_s.downcase.gsub(/packet$/,"")}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def require_protos(cwd)
|
||||
protos_dir = File.join(cwd, "packetfu", "protos")
|
||||
Dir.new(protos_dir).each do |fname|
|
||||
next unless fname[/\.rb$/]
|
||||
begin
|
||||
require File.join(protos_dir,fname)
|
||||
rescue
|
||||
warn "Warning: Could not load `#{fname}'. Skipping."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require File.join(cwd,"packetfu","version")
|
||||
require File.join(cwd,"packetfu","pcap")
|
||||
require File.join(cwd,"packetfu","packet")
|
||||
require_protos(cwd)
|
||||
require File.join(cwd,"packetfu","utils")
|
||||
require File.join(cwd,"packetfu","config")
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,136 +1,54 @@
|
|||
module PacketFu
|
||||
|
||||
# Packet is the parent class of EthPacket, IPPacket, UDPPacket, TCPPacket, and all
|
||||
# other packets.
|
||||
# other packets. It acts as both a singleton class, so things like
|
||||
# Packet.parse can happen, and as an abstract class to provide
|
||||
# subclasses some structure.
|
||||
class Packet
|
||||
|
||||
attr_reader :flavor # Packet Headers are responsible for their own specific flavor methods.
|
||||
attr_accessor :headers # All packets have a header collection, useful for determining protocol trees.
|
||||
attr_accessor :iface # Default inferface to send packets to
|
||||
|
||||
# Register subclasses in PacketFu.packet_class to do all kinds of neat things
|
||||
# that obviates those long if/else trees for parsing. It's pretty sweet.
|
||||
def self.inherited(subclass)
|
||||
PacketFu.add_packet_class(subclass)
|
||||
end
|
||||
|
||||
# Force strings into binary.
|
||||
def self.force_binary(str)
|
||||
str.force_encoding "binary" if str.respond_to? :force_encoding
|
||||
end
|
||||
|
||||
# parse_app makes a valiant attempt at picking out particular applications (beyond
|
||||
# the transport layer). As of right now, this only accounts for HSRP. I don't really
|
||||
# intend to get very far with this because I need a better way to parse packets anyway --
|
||||
# each packet type (including application layers) should be responsible for their own
|
||||
# parsing rules. But, let's assume that'll never happen, so continue with this folly.
|
||||
#
|
||||
# This is an optional step, since it can lead to misidentified applications, depending
|
||||
# on the strategy used to pick out app layers. For example, we really shouldn't have
|
||||
# a rule that says that HTTP must be port 80, since it can easily be on 3128 or any
|
||||
# other arbitrary port. However, we can say with certainty that HSRP must be dst port
|
||||
# 1985, because that's what the RFC dictates.
|
||||
def self.parse_app(parsed_packet,packet)
|
||||
if parsed_packet.is_udp?
|
||||
# Figure out UDP protocols (DNS, DHCP, etc)
|
||||
# All HSRP is dst 224.0.0.2:1985 with a TTL of 1, so sayeth RFC 2281.
|
||||
if(
|
||||
parsed_packet.ip_ttl == 1 and
|
||||
parsed_packet.ip_dst == 0xe0000002 and
|
||||
parsed_packet.udp_dst == 1985
|
||||
)
|
||||
return HSRPPacket.new.read(packet)
|
||||
else
|
||||
return parsed_packet
|
||||
end
|
||||
elsif parsed_packet.is_tcp?
|
||||
# Figure out TCP protocols (HTTP, SSH, etc)
|
||||
return parsed_packet
|
||||
else
|
||||
# I don't know any others.
|
||||
return parsed_packet
|
||||
end
|
||||
end
|
||||
|
||||
# Parse() creates the correct packet type based on the data, and returns the apporpiate
|
||||
# Packet subclass.
|
||||
# Packet subclass object.
|
||||
#
|
||||
# There is an assumption here that all incoming packets are either EthPacket
|
||||
# or InvalidPacket types.
|
||||
# or InvalidPacket types. This will be addressed pretty soon.
|
||||
#
|
||||
# If application-layer parsing is /not/ desired, that should be indicated explicitly
|
||||
# with an argument of :parse_app => false.
|
||||
# with an argument of :parse_app => false. Otherwise, app-layer parsing will happen.
|
||||
#
|
||||
# New packet types should get an entry here.
|
||||
def self.parse(packet,args={})
|
||||
# It is no longer neccisary to manually add packet types here.
|
||||
def self.parse(packet=nil,args={})
|
||||
parse_app = true if(args[:parse_app].nil? or args[:parse_app])
|
||||
force_binary(packet)
|
||||
if packet.size >= 14 # Min size for Ethernet. No check for max size, yet.
|
||||
case packet[12,2] # Check the Eth protocol field.
|
||||
when "\x08\x00" # It's IP.
|
||||
if 1.respond_to? :ord
|
||||
ipv = packet[14,1][0].ord >> 4
|
||||
else
|
||||
ipv = packet[14,1][0] >> 4
|
||||
end
|
||||
case ipv # Check the IP version field.
|
||||
when 4; # It's IPv4.
|
||||
case packet[23,1] # Check the IP protocol field.
|
||||
when "\x06"; p = TCPPacket.new # Returns a TCPPacket.
|
||||
when "\x11"; p = UDPPacket.new # Returns a UDPPacket.
|
||||
when "\x01"; p = ICMPPacket.new # Returns an ICMPPacket.
|
||||
else; p = IPPacket.new # Returns an IPPacket since we can't tell the transport layer.
|
||||
end
|
||||
else; p = IPPacket.new # Returns an IPPacket of this crazy IP version.
|
||||
end
|
||||
when "\x08\x06" # It's arp
|
||||
if packet.size >= 28 # Min size for complete arp
|
||||
p = ARPPacket.new
|
||||
else; p = EthPacket.new # Returns an EthPacket since we can't deal with tiny arps.
|
||||
end
|
||||
when "\x86\xdd" # It's IPv6
|
||||
if packet.size >= 54 # Min size for a complete IPv6 packet.
|
||||
p = IPv6Packet.new
|
||||
else; p = EthPacket.new # Returns an EthPacket since we can't deal with tiny Ipv6.
|
||||
end
|
||||
else; p = EthPacket.new # Returns an EthPacket since we can't tell the network layer.
|
||||
end
|
||||
if parse_app
|
||||
classes = PacketFu.packet_classes.select {|pclass| pclass.can_parse? packet}
|
||||
else
|
||||
p = InvalidPacket.new # Not the right size for Ethernet (jumbo frames are okay)
|
||||
classes = PacketFu.packet_classes.select {|pclass| pclass.can_parse? packet}.reject {|pclass| pclass.layer_symbol == :application}
|
||||
end
|
||||
p = classes.sort {|x,y| x.layer <=> y.layer}.last.new
|
||||
parsed_packet = p.read(packet,args)
|
||||
app_parsed_packet = parse_app ? parse_app(parsed_packet,packet) : nil
|
||||
return app_parsed_packet || parsed_packet
|
||||
end
|
||||
|
||||
#method_missing() delegates protocol-specific field actions to the apporpraite
|
||||
#class variable (which contains the associated packet type)
|
||||
#This register-of-protocols style switch will work for the
|
||||
#forseeable future (there aren't /that/ many packet types), and it's a handy
|
||||
#way to know at a glance what packet types are supported.
|
||||
def method_missing(sym, *args, &block)
|
||||
case sym.to_s
|
||||
when /^invalid_/
|
||||
@invalid_header.send(sym,*args)
|
||||
when /^eth_/
|
||||
@eth_header.send(sym,*args)
|
||||
when /^arp_/
|
||||
@arp_header.send(sym,*args)
|
||||
when /^ip_/
|
||||
@ip_header.send(sym,*args)
|
||||
when /^icmp_/
|
||||
@icmp_header.send(sym,*args)
|
||||
when /^udp_/
|
||||
@udp_header.send(sym,*args)
|
||||
when /^hsrp_/
|
||||
@hsrp_header.send(sym,*args)
|
||||
when /^tcp_/
|
||||
@tcp_header.send(sym,*args)
|
||||
when /^ipv6_/
|
||||
@ipv6_header.send(sym,*args)
|
||||
def handle_is_identity(ptype)
|
||||
idx = PacketFu.packet_prefixes.index(ptype.to_s.downcase)
|
||||
if idx
|
||||
self.kind_of? PacketFu.packet_classes[idx]
|
||||
else
|
||||
raise NoMethodError, "Unknown method `#{sym}' for this packet object."
|
||||
end
|
||||
end
|
||||
|
||||
def respond_to?(sym, include_private = false)
|
||||
if sym.to_s =~ /^(invalid|eth|arp|ip|icmp|udp|hsrp|tcp|ipv6)_/
|
||||
self.instance_variable_get("@#{$1}_header").respond_to? sym
|
||||
else
|
||||
super
|
||||
raise NoMethodError, "Undefined method `is_#{ptype}?' for #{self.class}."
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -216,13 +134,6 @@ module PacketFu
|
|||
# Read() takes (and trusts) the io input and shoves it all into a well-formed Packet.
|
||||
# Note that read is a destructive process, so any existing data will be lost.
|
||||
#
|
||||
# TODO: This giant if tree is a mess, and worse, is decieving. You need to define
|
||||
# actions both here and in parse(). All read() does is make a (good) guess as to
|
||||
# what @headers to expect, and reads data to them.
|
||||
#
|
||||
# To take strings and turn them into packets without knowing ahead of time what kind of
|
||||
# packet it is, use Packet.parse instead; parse() handles the figuring-out part.
|
||||
#
|
||||
# A note on the :strip => true argument: If :strip is set, defined lengths of data will
|
||||
# be believed, and any trailers (such as frame check sequences) will be chopped off. This
|
||||
# helps to ensure well-formed packets, at the cost of losing perhaps important FCS data.
|
||||
|
@ -236,123 +147,18 @@ module PacketFu
|
|||
# correctly.
|
||||
#
|
||||
# So, to summarize; if you intend to alter the data, use :strip. If you don't, don't. Also,
|
||||
# this is a horrid XXX hack. Stripping is useful (and fun!), but the default behavior really
|
||||
# this is a horrid hack. Stripping is useful (and fun!), but the default behavior really
|
||||
# should be to create payloads correctly, and /not/ treat extra FCS data as a payload.
|
||||
#
|
||||
# Update: This scheme is so lame. Need to fix. Seriously.
|
||||
# Update: still sucks. Really.
|
||||
def read(io,args={})
|
||||
begin
|
||||
if io.size >= 14
|
||||
@eth_header.read(io)
|
||||
eth_proto_num = io[12,2].unpack("n")[0]
|
||||
if eth_proto_num == 0x0800 # It's IP.
|
||||
if 1.respond_to? :ord
|
||||
ipv = io[14].ord
|
||||
else
|
||||
ipv = io[14]
|
||||
end
|
||||
ip_hlen=(ipv & 0x0f) * 4
|
||||
ip_ver=(ipv >> 4) # It's IPv4. Other versions, all bets are off!
|
||||
if ip_ver == 4
|
||||
ip_proto_num = io[23,1].unpack("C")[0]
|
||||
@ip_header.read(io[14,ip_hlen])
|
||||
if ip_proto_num == 0x06 # It's TCP.
|
||||
tcp_len = io[16,2].unpack("n")[0] - 20
|
||||
if args[:strip] # Drops trailers like frame check sequence (FCS). Often desired for cleaner packets.
|
||||
tcp_all = io[ip_hlen+14,tcp_len] # Believe the tcp_len value; chop off anything that's not in range.
|
||||
else
|
||||
tcp_all = io[ip_hlen+14,0xffff] # Don't believe the tcp_len value; suck everything up.
|
||||
end
|
||||
tcp_hlen = ((tcp_all[12,1].unpack("C")[0]) >> 4) * 4
|
||||
if tcp_hlen.to_i >= 20
|
||||
@tcp_header.read(tcp_all)
|
||||
@ip_header.body = @tcp_header
|
||||
else # It's a TCP packet with an impossibly small hlen, so it can't be real TCP. Abort! Abort!
|
||||
@ip_header.body = io[16,io.size-16]
|
||||
end
|
||||
elsif ip_proto_num == 0x11 # It's UDP.
|
||||
udp_len = io[16,2].unpack("n")[0] - 20
|
||||
if args[:strip] # Same deal as with TCP. We might have stuff at the end of the packet that's not part of the payload.
|
||||
@udp_header.read(io[ip_hlen+14,udp_len])
|
||||
else # ... Suck it all up. BTW, this will change the lengths if they are ever recalc'ed. Bummer.
|
||||
@udp_header.read(io[ip_hlen+14,0xffff])
|
||||
end
|
||||
@ip_header.body = @udp_header
|
||||
elsif ip_proto_num == 1 # It's ICMP
|
||||
@icmp_header.read(io[ip_hlen+14,0xffff])
|
||||
@ip_header.body = @icmp_header
|
||||
else # It's an IP packet for a protocol we don't have a decoder for.
|
||||
@ip_header.body = io[16,io.size-16]
|
||||
end
|
||||
else # It's not IPv4, so no idea what should come next. Just dump it all into an ip_header and ip payload.
|
||||
@ip_header.read(io[14,ip_hlen])
|
||||
@ip_header.body = io[16,io.size-16]
|
||||
end
|
||||
@eth_header.body = @ip_header
|
||||
elsif eth_proto_num == 0x0806 # It's ARP
|
||||
@arp_header.read(io[14,0xffff]) # You'll nearly have a trailer and you'll never know what size.
|
||||
@eth_header.body=@arp_header
|
||||
@eth_header.body
|
||||
elsif eth_proto_num == 0x86dd # It's IPv6
|
||||
@ipv6_header.read(io[14,0xffff])
|
||||
@eth_header.body=@ipv6_header
|
||||
else # It's an Ethernet packet for a protocol we don't have a decoder for
|
||||
@eth_header.body = io[14,io.size-14]
|
||||
end
|
||||
if (args[:fix] || args[:recalc])
|
||||
# Unfortunately, we cannot simply recalc with abandon, since
|
||||
# we may have unaccounted trailers that will sneak into the checksum.
|
||||
# The better way to handle this is to put trailers in their own
|
||||
# StructFu field, but I'm not a-gonna right now. :/
|
||||
ip_recalc(:ip_sum) if respond_to? :ip_header
|
||||
recalc(:tcp) if respond_to? :tcp_header
|
||||
recalc(:udp) if respond_to? :udp_header
|
||||
end
|
||||
else # You're not big enough for Ethernet.
|
||||
@invalid_header.read(io)
|
||||
end
|
||||
# @headers[0]
|
||||
self
|
||||
rescue ::Exception => e
|
||||
# remove last header
|
||||
# nested_types = self.headers.collect {|header| header.class}
|
||||
# nested_types.pop # whatever this packet type is, we weren't able to parse it
|
||||
self.headers.pop
|
||||
return_header_type = self.headers[self.headers.length-1].class.to_s
|
||||
retklass = PacketFu::InvalidPacket
|
||||
seekpos = 0
|
||||
target_header = @invalid_header
|
||||
case return_header_type.to_s
|
||||
when "PacketFu::EthHeader"
|
||||
retklass = PacketFu::EthPacket
|
||||
seekpos = 0x0e
|
||||
target_header = @eth_header
|
||||
when "PacketFu::IPHeader"
|
||||
retklass = PacketFu::IPPacket
|
||||
seekpos = 0x0e + @ip_header.ip_hl * 4
|
||||
target_header = @ip_header
|
||||
when "PacketFu::TCPHeader"
|
||||
retklass = PacketFu::TCPPacket
|
||||
seekpos = 0x0e + @ip_header.ip_hl * 4 + @tcpheader.tcp_hlen
|
||||
target_header = @tcp_header
|
||||
when "PacketFu::UDPHeader"
|
||||
retklass = PacketFu::UDPPacket
|
||||
when "PacketFu::ARPHeader"
|
||||
retklass = PacketFu::ARPPacket
|
||||
when "PacketFu::ICMPHeader"
|
||||
retklass = PacketFu::ICMPPacket
|
||||
when "PacketFu::IPv6Header"
|
||||
retklass = PacketFu::IPv6Packet
|
||||
else
|
||||
end
|
||||
|
||||
io = io[seekpos,io.length - seekpos]
|
||||
target_header.body = io
|
||||
p = retklass.new
|
||||
p.headers = self.headers
|
||||
p
|
||||
raise e if $debug
|
||||
# Finally, packet subclasses should take two arguments: the string that is the data
|
||||
# to be transmuted into a packet, as well as args. This superclass method is merely
|
||||
# concerned with handling args common to many packet formats (namely, fixing packets
|
||||
# on the fly)
|
||||
def read(args={})
|
||||
if args[:fix] || args[:recalc]
|
||||
ip_recalc(:ip_sum) if self.is_ip?
|
||||
recalc(:tcp) if self.is_tcp?
|
||||
recalc(:udp) if self.is_udp?
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -366,6 +172,55 @@ module PacketFu
|
|||
peek_data.join
|
||||
end
|
||||
|
||||
# Defines the layer this packet type lives at, based on the number of headers it
|
||||
# requires. Note that this has little to do with the OSI model, since TCP/IP
|
||||
# doesn't really have Session and Presentation layers.
|
||||
#
|
||||
# Ethernet and the like are layer 1, IP, IPv6, and ARP are layer 2,
|
||||
# TCP, UDP, and other transport protocols are layer 3, and application
|
||||
# protocols are at layer 4 or higher. InvalidPackets have an arbitrary
|
||||
# layer 0 to distinguish them.
|
||||
#
|
||||
# Because these don't change much, it's cheaper just to case through them,
|
||||
# and only resort to counting headers if we don't have a match -- this
|
||||
# makes adding protocols somewhat easier, but of course you can just
|
||||
# override this method over there, too. This is merely optimized
|
||||
# for the most likely protocols you see on the Internet.
|
||||
def self.layer
|
||||
case self.name # Lol ran into case's fancy treatment of classes
|
||||
when /InvalidPacket$/; 0
|
||||
when /EthPacket$/; 1
|
||||
when /IPPacket$/, /ARPPacket$/, /IPv6Packet$/; 2
|
||||
when /TCPPacket$/, /UDPPacket$/, /ICMPPacket$/; 3
|
||||
when /HSRPPacket$/; 4
|
||||
else; self.new.headers.size
|
||||
end
|
||||
end
|
||||
|
||||
def layer
|
||||
self.class.layer
|
||||
end
|
||||
|
||||
def self.layer_symbol
|
||||
case self.layer
|
||||
when 0; :invalid
|
||||
when 1; :link
|
||||
when 2; :internet
|
||||
when 3; :transport
|
||||
else; :application
|
||||
end
|
||||
end
|
||||
|
||||
def layer_symbol
|
||||
self.class.layer_symbol
|
||||
end
|
||||
|
||||
# Packet subclasses must override this, since the Packet superclass
|
||||
# can't actually parse anything.
|
||||
def self.can_parse?(str)
|
||||
false
|
||||
end
|
||||
|
||||
# Hexify provides a neatly-formatted dump of binary data, familar to hex readers.
|
||||
def hexify(str)
|
||||
if str.respond_to? :force_encoding
|
||||
|
@ -426,6 +281,21 @@ module PacketFu
|
|||
end
|
||||
end
|
||||
|
||||
alias :orig_kind_of? :kind_of?
|
||||
|
||||
def kind_of?(klass)
|
||||
return true if orig_kind_of? klass
|
||||
packet_types = proto.map {|p| PacketFu.const_get("#{p}Packet")}
|
||||
match = false
|
||||
packet_types.each do |p|
|
||||
if p.ancestors.include? klass
|
||||
match = true
|
||||
break
|
||||
end
|
||||
end
|
||||
return match
|
||||
end
|
||||
|
||||
# For packets, inspect is overloaded as inspect_hex(0).
|
||||
# Not sure if this is a great idea yet, but it sure makes
|
||||
# the irb output more sane.
|
||||
|
@ -458,31 +328,6 @@ module PacketFu
|
|||
end
|
||||
|
||||
alias_method :protocol, :proto
|
||||
|
||||
# Returns true if this is an Invalid packet. Else, false.
|
||||
def is_invalid? ; self.proto.include? "Invalid"; end
|
||||
# Returns true if this is an Ethernet packet. Else, false.
|
||||
def is_ethernet? ; self.proto.include? "Eth"; end
|
||||
alias_method :is_eth?, :is_ethernet?
|
||||
# Returns true if this is an IP packet. Else, false.
|
||||
def is_ip? ; self.proto.include? "IP"; end
|
||||
# Returns true if this is an TCP packet. Else, false.
|
||||
def is_tcp? ; self.proto.include? "TCP"; end
|
||||
# Returns true if this is an UDP packet. Else, false.
|
||||
def is_udp? ; self.proto.include? "UDP"; end
|
||||
# Returns true if this is an HSRP packet. Else, false.
|
||||
def is_hsrp? ; self.proto.include? "HSRP"; end
|
||||
# Returns true if this is an ARP packet. Else, false.
|
||||
def is_arp? ; self.proto.include? "ARP"; end
|
||||
# Returns true if this is an IPv6 packet. Else, false.
|
||||
def is_ipv6? ; self.proto.include? "IPv6" ; end
|
||||
# Returns true if this is an ICMP packet. Else, false.
|
||||
def is_icmp? ; self.proto.include? "ICMP" ; end
|
||||
# Returns true if this is an IPv6 packet. Else, false.
|
||||
def is_ipv6? ; self.proto.include? "IPv6" ; end
|
||||
# Returns true if the outermost layer has data. Else, false.
|
||||
def has_data? ; self.payload.size.zero? ? false : true ; end
|
||||
|
||||
alias_method :length, :size
|
||||
|
||||
def initialize(args={})
|
||||
|
@ -498,6 +343,46 @@ module PacketFu
|
|||
end
|
||||
end
|
||||
|
||||
#method_missing() delegates protocol-specific field actions to the apporpraite
|
||||
#class variable (which contains the associated packet type)
|
||||
#This register-of-protocols style switch will work for the
|
||||
#forseeable future (there aren't /that/ many packet types), and it's a handy
|
||||
#way to know at a glance what packet types are supported.
|
||||
def method_missing(sym, *args, &block)
|
||||
case sym.to_s
|
||||
when /^is_([a-zA-Z0-9]+)\?/
|
||||
ptype = $1
|
||||
if PacketFu.packet_prefixes.index(ptype)
|
||||
self.send(:handle_is_identity, $1)
|
||||
else
|
||||
super
|
||||
end
|
||||
when /^([a-zA-Z0-9]+)_.+/
|
||||
ptype = $1
|
||||
if PacketFu.packet_prefixes.index(ptype)
|
||||
self.instance_variable_get("@#{ptype}_header").send(sym,*args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def respond_to?(sym, include_private = false)
|
||||
if sym.to_s =~ /^(invalid|eth|arp|ip|icmp|udp|hsrp|tcp|ipv6)_/
|
||||
self.instance_variable_get("@#{$1}_header").respond_to? sym
|
||||
elsif sym.to_s =~ /^is_([a-zA-Z0-9]+)\?/
|
||||
if PacketFu.packet_prefixes.index($1)
|
||||
true
|
||||
else
|
||||
super
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
end # class Packet
|
||||
|
||||
@@inspect_style = :pretty
|
|
@ -416,7 +416,7 @@ module PacketFu
|
|||
|
||||
# set_byte_order is pretty much totally deprecated.
|
||||
def set_byte_order(byte_order)
|
||||
PacketFu.instance_variable_set("@byte_order",byte_order)
|
||||
PacketFu.instance_variable_set(:@byte_order,byte_order)
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -450,7 +450,7 @@ module PacketFu
|
|||
arr = args[:arr] || args[:array] || []
|
||||
ts = args[:ts] || args[:timestamp] || Time.now.to_i
|
||||
ts_inc = args[:ts_inc] || args[:timestamp_increment]
|
||||
pkts = PcapFile.new.array_to_file(:endian => PacketFu.instance_variable_get("@byte_order"),
|
||||
pkts = PcapFile.new.array_to_file(:endian => PacketFu.instance_variable_get(:@byte_order),
|
||||
:arr => arr,
|
||||
:ts => ts,
|
||||
:ts_inc => ts_inc)
|
||||
|
@ -468,7 +468,7 @@ module PacketFu
|
|||
append = args[:append]
|
||||
Read.set_byte_order(byte_order) if [:big, :little].include? byte_order
|
||||
pf = PcapFile.new
|
||||
pf.array_to_file(:endian => PacketFu.instance_variable_get("@byte_order"),
|
||||
pf.array_to_file(:endian => PacketFu.instance_variable_get(:@byte_order),
|
||||
:arr => arr,
|
||||
:ts => ts,
|
||||
:ts_inc => ts_inc)
|
|
@ -173,13 +173,29 @@ module PacketFu
|
|||
|
||||
attr_accessor :eth_header, :arp_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless str.size >= 28
|
||||
return false unless str[12,2] == "\x08\x06"
|
||||
true
|
||||
end
|
||||
|
||||
def read(str=nil,args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@arp_header.read(str[14,str.size])
|
||||
@eth_header.body = @arp_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@arp_header = ARPHeader.new(args).read(args[:arp])
|
||||
@eth_header.eth_proto = "\x08\x06"
|
||||
@eth_header.body=@arp_header
|
||||
|
||||
# Please send more flavors to todb-packetfu@planb-security.net.
|
||||
# Please send more flavors to todb+packetfu@planb-security.net.
|
||||
# Most of these initial fingerprints come from one (1) sample.
|
||||
case (args[:flavor].nil?) ? :nil : args[:flavor].to_s.downcase.intern
|
||||
when :windows; @arp_header.body = "\x00" * 64 # 64 bytes of padding
|
||||
|
@ -197,7 +213,6 @@ module PacketFu
|
|||
|
||||
@headers = [@eth_header, @arp_header]
|
||||
super
|
||||
|
||||
end
|
||||
|
||||
# Generates summary data for ARP packets.
|
|
@ -245,10 +245,15 @@ module PacketFu
|
|||
class EthPacket < Packet
|
||||
attr_accessor :eth_header
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@headers = [@eth_header]
|
||||
super
|
||||
def self.can_parse?(str)
|
||||
str.size >= 14
|
||||
end
|
||||
|
||||
def read(str=nil,args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
super(args)
|
||||
return self
|
||||
end
|
||||
|
||||
# Does nothing, really, since there's no length or
|
||||
|
@ -257,6 +262,12 @@ module PacketFu
|
|||
@headers[0].inspect
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@headers = [@eth_header]
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -141,6 +141,33 @@ module PacketFu
|
|||
|
||||
attr_accessor :eth_header, :ip_header, :udp_header, :hsrp_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless str.size >= 54
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless IPPacket.can_parse? str
|
||||
return false unless UDPPacket.can_parse? str
|
||||
temp_packet = UDPPacket.new
|
||||
temp_packet.read(str)
|
||||
if temp_packet.ip_ttl == 1 and [temp_packet.udp_sport,temp_packet.udp_dport] == [1985,1985]
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def read(str=nil, args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ip_header.read(str[14,str.size])
|
||||
@eth_header.body = @ip_header
|
||||
@udp_header.read(str[14+(@ip_header.ip_hlen),str.size])
|
||||
@ip_header.body = @udp_header
|
||||
@hsrp_header.read(str[14+(@ip_header.ip_hlen)+8,str.size])
|
||||
@udp_header.body = @hsrp_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@ip_header = IPHeader.new(args).read(args[:ip])
|
||||
|
@ -150,7 +177,6 @@ module PacketFu
|
|||
@udp_header.body = @hsrp_header
|
||||
@ip_header.body = @udp_header
|
||||
@eth_header.body = @ip_header
|
||||
|
||||
@headers = [@eth_header, @ip_header, @udp_header, @hsrp_header]
|
||||
super
|
||||
end
|
|
@ -114,7 +114,26 @@ module PacketFu
|
|||
class ICMPPacket < Packet
|
||||
|
||||
attr_accessor :eth_header, :ip_header, :icmp_header
|
||||
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless str.size >= 54
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless IPPacket.can_parse? str
|
||||
return false unless str[23,1] == "\x01"
|
||||
return true
|
||||
end
|
||||
|
||||
def read(str=nil, args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ip_header.read(str[14,str.size])
|
||||
@eth_header.body = @ip_header
|
||||
@icmp_header.read(str[14+(@ip_header.ip_hlen),str.size])
|
||||
@ip_header.body = @icmp_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@ip_header = IPHeader.new(args).read(args[:ip])
|
|
@ -30,6 +30,20 @@ module PacketFu
|
|||
class InvalidPacket < Packet
|
||||
attr_accessor :invalid_header
|
||||
|
||||
# Any packet is potentially an invalid packet
|
||||
def self.can_parse?(str)
|
||||
true
|
||||
end
|
||||
|
||||
def self.layer
|
||||
0
|
||||
end
|
||||
|
||||
def read(str=nil,args={})
|
||||
@invalid_header.read(str)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@invalid_header = (args[:invalid] || InvalidHeader.new)
|
||||
@headers = [@invalid_header]
|
|
@ -175,6 +175,11 @@ module PacketFu
|
|||
(ip_hl * 4) + body.to_s.length
|
||||
end
|
||||
|
||||
# Return the claimed header length
|
||||
def ip_hlen
|
||||
(ip_hl * 4)
|
||||
end
|
||||
|
||||
# Calculate the true checksum of the packet.
|
||||
# (Yes, this is the long way to do it, but it's e-z-2-read for mathtards like me.)
|
||||
def ip_calc_sum
|
||||
|
@ -289,6 +294,30 @@ module PacketFu
|
|||
|
||||
attr_accessor :eth_header, :ip_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless str.size >= 34
|
||||
return false unless EthPacket.can_parse? str
|
||||
if str[12,2] == "\x08\x00"
|
||||
if 1.respond_to? :ord
|
||||
ipv = str[14,1][0].ord >> 4
|
||||
else
|
||||
ipv = str[14,1][0] >> 4
|
||||
end
|
||||
return true if ipv == 4
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def read(str=nil, args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ip_header.read(str[14,str.size])
|
||||
@eth_header.body = @ip_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
# Creates a new IPPacket object.
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
|
@ -203,12 +203,27 @@ module PacketFu
|
|||
|
||||
attr_accessor :eth_header, :ipv6_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless str.size >= 54
|
||||
return false unless str[12,2] == "\x86\xdd"
|
||||
true
|
||||
end
|
||||
|
||||
def read(str=nil,args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ipv6_header.read(str[14,str.size])
|
||||
@eth_header.body = @ipv6_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = (args[:eth] || EthHeader.new)
|
||||
@ipv6_header = (args[:ipv6] || IPv6Header.new)
|
||||
@eth_header.eth_proto = 0x86dd
|
||||
@eth_header.body=@ipv6_header
|
||||
|
||||
@headers = [@eth_header, @ipv6_header]
|
||||
super
|
||||
end
|
|
@ -920,8 +920,32 @@ module PacketFu
|
|||
# A hash of return address details, often the output of Utils.whoami?
|
||||
class TCPPacket < Packet
|
||||
|
||||
attr_accessor :eth_header, :ip_header, :tcp_header, :headers
|
||||
|
||||
attr_accessor :eth_header, :ip_header, :tcp_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless str.size >= 54
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless IPPacket.can_parse? str
|
||||
return false unless str[23,1] == "\x06"
|
||||
return true
|
||||
end
|
||||
|
||||
def read(str=nil, args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ip_header.read(str[14,str.size])
|
||||
@eth_header.body = @ip_header
|
||||
if args[:strip]
|
||||
tcp_len = str[16,2].unpack("n")[0] - 20
|
||||
@tcp_header.read(str[14+(@ip_header.ip_hlen),tcp_len])
|
||||
else
|
||||
@tcp_header.read(str[14+(@ip_header.ip_hlen),str.size])
|
||||
end
|
||||
@ip_header.body = @tcp_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = (args[:eth] || EthHeader.new)
|
||||
@ip_header = (args[:ip] || IPHeader.new)
|
|
@ -127,7 +127,31 @@ module PacketFu
|
|||
class UDPPacket < Packet
|
||||
|
||||
attr_accessor :eth_header, :ip_header, :udp_header
|
||||
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless str.size >= 54
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless IPPacket.can_parse? str
|
||||
return false unless str[23,1] == "\x11"
|
||||
return true
|
||||
end
|
||||
|
||||
def read(str=nil, args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ip_header.read(str[14,str.size])
|
||||
@eth_header.body = @ip_header
|
||||
if args[:strip]
|
||||
udp_len = str[16,2].unpack("n")[0] - 20
|
||||
@udp_header.read(str[14+(@ip_header.ip_hlen),udp_len])
|
||||
else
|
||||
@udp_header.read(str[14+(@ip_header.ip_hlen),str.size])
|
||||
end
|
||||
@ip_header.body = @udp_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@ip_header = IPHeader.new(args).read(args[:ip])
|
|
@ -2,6 +2,7 @@
|
|||
# to create meaningful binary data.
|
||||
|
||||
module StructFu
|
||||
|
||||
# Normally, self.size and self.length will refer to the Struct
|
||||
# size as an array. It's a hassle to redefine, so this introduces some
|
||||
# shorthand to get at the size of the resultant string.
|
||||
|
@ -106,6 +107,7 @@ module StructFu
|
|||
|
||||
# Returns a two byte value as a packed string.
|
||||
def to_s
|
||||
@packstr = (self.e == :big) ? "n" : "v"
|
||||
[(self.v || self.d)].pack(@packstr)
|
||||
end
|
||||
|
||||
|
@ -113,10 +115,12 @@ module StructFu
|
|||
|
||||
# Int16be is a two byte value in big-endian format.
|
||||
class Int16be < Int16
|
||||
undef :endian=
|
||||
end
|
||||
|
||||
# Int16le is a two byte value in little-endian format.
|
||||
class Int16le < Int16
|
||||
undef :endian=
|
||||
def initialize(v=nil, e=:little)
|
||||
super(v,e)
|
||||
@packstr = (self.e == :big) ? "n" : "v"
|
||||
|
@ -132,6 +136,7 @@ module StructFu
|
|||
|
||||
# Returns a four byte value as a packed string.
|
||||
def to_s
|
||||
@packstr = (self.e == :big) ? "N" : "V"
|
||||
[(self.v || self.d)].pack(@packstr)
|
||||
end
|
||||
|
||||
|
@ -139,10 +144,12 @@ module StructFu
|
|||
|
||||
# Int32be is a four byte value in big-endian format.
|
||||
class Int32be < Int32
|
||||
undef :endian=
|
||||
end
|
||||
|
||||
# Int32le is a four byte value in little-endian format.
|
||||
class Int32le < Int32
|
||||
undef :endian=
|
||||
def initialize(v=nil, e=:little)
|
||||
super(v,e)
|
||||
end
|
||||
|
@ -166,11 +173,11 @@ module StructFu
|
|||
class IntString < Struct.new(:int, :string, :mode)
|
||||
|
||||
def initialize(string='',int=Int8,mode=nil)
|
||||
unless int.respond_to?(:ancestors) && int.ancestors.include?(StructFu::Int)
|
||||
raise StandardError, "Invalid length (#{int.inspect}) associated with this String."
|
||||
else
|
||||
if int < Int
|
||||
super(int.new,string,mode)
|
||||
calc
|
||||
else
|
||||
raise "IntStrings need a StructFu::Int for a length."
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
module PacketFu
|
||||
|
||||
# Version 1.0.0 was released July 31, 2010
|
||||
# Version 1.0.1 is unreleased.
|
||||
VERSION = "1.0.2"
|
||||
|
||||
def self.version
|
||||
VERSION
|
||||
end
|
||||
|
||||
# Returns the version in a binary format for easy comparisons.
|
||||
def self.binarize_version(str)
|
||||
if(str.respond_to?(:split) && str =~ /^[0-9]+(\.([0-9]+)(\.[0-9]+)?)?$/)
|
||||
bin_major,bin_minor,bin_teeny = str.split(/\x2e/).map {|x| x.to_i}
|
||||
bin_version = (bin_major.to_i << 16) + (bin_minor.to_i << 8) + bin_teeny.to_i
|
||||
else
|
||||
raise ArgumentError, "Compare version malformed. Should be \x22x.y.z\x22"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the version is equal to or greater than the compare version.
|
||||
# If the current version of PacketFu is "0.3.1" for example:
|
||||
#
|
||||
# PacketFu.at_least? "0" # => true
|
||||
# PacketFu.at_least? "0.2.9" # => true
|
||||
# PacketFu.at_least? "0.3" # => true
|
||||
# PacketFu.at_least? "1" # => true after 1.0's release
|
||||
# PacketFu.at_least? "1.12" # => false
|
||||
# PacketFu.at_least? "2" # => false
|
||||
def self.at_least?(str)
|
||||
this_version = binarize_version(self.version)
|
||||
ask_version = binarize_version(str)
|
||||
this_version >= ask_version
|
||||
end
|
||||
|
||||
# Returns true if the current version is older than the compare version.
|
||||
def self.older_than?(str)
|
||||
return false if str == self.version
|
||||
this_version = binarize_version(self.version)
|
||||
ask_version = binarize_version(str)
|
||||
this_version < ask_version
|
||||
end
|
||||
|
||||
# Returns true if the current version is newer than the compare version.
|
||||
def self.newer_than?(str)
|
||||
return false if str == self.version
|
||||
!self.older_than?(str)
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue