packetfu 1.1.1 → 1.1.2
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.
- data/{README → README.rdoc} +2 -2
- metadata +58 -94
- data/INSTALL +0 -40
- data/LICENSE +0 -28
- data/examples/100kpackets.rb +0 -41
- data/examples/ackscan.rb +0 -38
- data/examples/arp.rb +0 -60
- data/examples/arphood.rb +0 -59
- data/examples/dissect_thinger.rb +0 -22
- data/examples/ethernet.rb +0 -10
- data/examples/examples.rb +0 -3
- data/examples/ids.rb +0 -4
- data/examples/idsv2.rb +0 -6
- data/examples/new-simple-stats.rb +0 -52
- data/examples/oui.txt +0 -84177
- data/examples/packetfu-shell.rb +0 -113
- data/examples/simple-sniffer.rb +0 -40
- data/examples/simple-stats.rb +0 -50
- data/examples/slammer.rb +0 -33
- data/examples/uniqpcap.rb +0 -15
- data/lib/packetfu.rb +0 -147
- data/lib/packetfu/capture.rb +0 -169
- data/lib/packetfu/config.rb +0 -58
- data/lib/packetfu/inject.rb +0 -65
- data/lib/packetfu/packet.rb +0 -533
- data/lib/packetfu/pcap.rb +0 -594
- data/lib/packetfu/protos/arp.rb +0 -268
- data/lib/packetfu/protos/eth.rb +0 -296
- data/lib/packetfu/protos/hsrp.rb +0 -206
- data/lib/packetfu/protos/icmp.rb +0 -179
- data/lib/packetfu/protos/invalid.rb +0 -55
- data/lib/packetfu/protos/ip.rb +0 -378
- data/lib/packetfu/protos/ipv6.rb +0 -250
- data/lib/packetfu/protos/tcp.rb +0 -1127
- data/lib/packetfu/protos/udp.rb +0 -240
- data/lib/packetfu/structfu.rb +0 -294
- data/lib/packetfu/utils.rb +0 -194
- data/lib/packetfu/version.rb +0 -50
- data/test/all_tests.rb +0 -41
- data/test/arp_test.pcap +0 -0
- data/test/eth_test.pcap +0 -0
- data/test/ethpacket_spec.rb +0 -74
- data/test/icmp_test.pcap +0 -0
- data/test/ip_test.pcap +0 -0
- data/test/packet_spec.rb +0 -73
- data/test/packet_subclasses_spec.rb +0 -13
- data/test/packetfu_spec.rb +0 -90
- data/test/ptest.rb +0 -16
- data/test/sample-ipv6.pcap +0 -0
- data/test/sample.pcap +0 -0
- data/test/sample2.pcap +0 -0
- data/test/sample_hsrp_pcapr.cap +0 -0
- data/test/structfu_spec.rb +0 -335
- data/test/tcp_spec.rb +0 -101
- data/test/tcp_test.pcap +0 -0
- data/test/test_arp.rb +0 -135
- data/test/test_eth.rb +0 -91
- data/test/test_hsrp.rb +0 -20
- data/test/test_icmp.rb +0 -54
- data/test/test_inject.rb +0 -31
- data/test/test_invalid.rb +0 -28
- data/test/test_ip.rb +0 -69
- data/test/test_ip6.rb +0 -68
- data/test/test_octets.rb +0 -37
- data/test/test_packet.rb +0 -174
- data/test/test_pcap.rb +0 -209
- data/test/test_structfu.rb +0 -112
- data/test/test_tcp.rb +0 -327
- data/test/test_udp.rb +0 -73
- data/test/udp_test.pcap +0 -0
- data/test/vlan-pcapr.cap +0 -0
data/lib/packetfu/protos/ipv6.rb
DELETED
@@ -1,250 +0,0 @@
|
|
1
|
-
module PacketFu
|
2
|
-
|
3
|
-
# AddrIpv6 handles addressing for IPv6Header
|
4
|
-
#
|
5
|
-
# ==== Header Definition
|
6
|
-
#
|
7
|
-
# Int32 :a1
|
8
|
-
# Int32 :a2
|
9
|
-
# Int32 :a3
|
10
|
-
# Int32 :a4
|
11
|
-
class AddrIpv6 < Struct.new(:a1, :a2, :a3, :a4)
|
12
|
-
|
13
|
-
include StructFu
|
14
|
-
|
15
|
-
def initialize(args={})
|
16
|
-
super(
|
17
|
-
Int32.new(args[:a1]),
|
18
|
-
Int32.new(args[:a2]),
|
19
|
-
Int32.new(args[:a3]),
|
20
|
-
Int32.new(args[:a4]))
|
21
|
-
end
|
22
|
-
|
23
|
-
# Returns the address in string format.
|
24
|
-
def to_s
|
25
|
-
self.to_a.map {|x| x.to_s}.join
|
26
|
-
end
|
27
|
-
|
28
|
-
# Returns the address as a fairly ginormous integer.
|
29
|
-
def to_i
|
30
|
-
(a1.to_i << 96) + (a2.to_i << 64) + (a3.to_i << 32) + a4.to_i
|
31
|
-
end
|
32
|
-
|
33
|
-
# Returns the address as a colon-delimited hex string.
|
34
|
-
def to_x
|
35
|
-
IPAddr.new(self.to_i, Socket::AF_INET6).to_s
|
36
|
-
end
|
37
|
-
|
38
|
-
# Reads in a string and casts it as an IPv6 address
|
39
|
-
def read(str)
|
40
|
-
force_binary(str)
|
41
|
-
return self if str.nil?
|
42
|
-
self[:a1].read str[0,4]
|
43
|
-
self[:a2].read str[4,4]
|
44
|
-
self[:a3].read str[8,4]
|
45
|
-
self[:a4].read str[12,4]
|
46
|
-
self
|
47
|
-
end
|
48
|
-
|
49
|
-
# Reads in a colon-delimited hex string and casts it as an IPv6 address.
|
50
|
-
def read_x(str)
|
51
|
-
addr = IPAddr.new(str).to_i
|
52
|
-
self[:a1]=Int32.new(addr >> 96)
|
53
|
-
self[:a2]=Int32.new((addr & 0x00000000ffffffff0000000000000000) >> 64)
|
54
|
-
self[:a3]=Int32.new((addr & 0x0000000000000000ffffffff00000000) >> 32)
|
55
|
-
self[:a4]=Int32.new(addr & 0x000000000000000000000000ffffffff)
|
56
|
-
self
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
# IPv6Header is complete IPv6 struct, used in IPv6Packet.
|
62
|
-
#
|
63
|
-
# ==== Header Definition
|
64
|
-
#
|
65
|
-
# Fixnum (4 bits) :ipv6_v Default: 6 # Versiom
|
66
|
-
# Fixnum (8 bits) :ipv6_class Defualt: 0 # Class
|
67
|
-
# Fixnum (20 bits) :ipv6_label Defualt: 0 # Label
|
68
|
-
# Int16 :ipv6_len Default: calc # Payload length
|
69
|
-
# Int8 :ipv6_next # Next Header
|
70
|
-
# Int8 :ipv6_hop Default: 0xff # Hop limit
|
71
|
-
# AddrIpv6 :ipv6_src
|
72
|
-
# AddrIpv6 :ipv6_dst
|
73
|
-
# String :body
|
74
|
-
class IPv6Header < Struct.new(:ipv6_v, :ipv6_class, :ipv6_label,
|
75
|
-
:ipv6_len, :ipv6_next, :ipv6_hop,
|
76
|
-
:ipv6_src, :ipv6_dst, :body)
|
77
|
-
include StructFu
|
78
|
-
|
79
|
-
def initialize(args={})
|
80
|
-
super(
|
81
|
-
(args[:ipv6_v] || 6),
|
82
|
-
(args[:ipv6_class] || 0),
|
83
|
-
(args[:ipv6_label] || 0),
|
84
|
-
Int16.new(args[:ipv6_len]),
|
85
|
-
Int8.new(args[:ipv6_next]),
|
86
|
-
Int8.new(args[:ipv6_hop] || 0xff),
|
87
|
-
AddrIpv6.new.read(args[:ipv6_src] || ("\x00" * 16)),
|
88
|
-
AddrIpv6.new.read(args[:ipv6_dst] || ("\x00" * 16)),
|
89
|
-
StructFu::String.new.read(args[:body])
|
90
|
-
)
|
91
|
-
end
|
92
|
-
|
93
|
-
# Returns the object in string form.
|
94
|
-
def to_s
|
95
|
-
bytes_v_class_label = [(self.ipv6_v << 28) +
|
96
|
-
(self.ipv6_class << 20) +
|
97
|
-
self.ipv6_label].pack("N")
|
98
|
-
bytes_v_class_label + (self.to_a[3,6].map {|x| x.to_s}.join)
|
99
|
-
end
|
100
|
-
|
101
|
-
# Reads a string to populate the object.
|
102
|
-
def read(str)
|
103
|
-
force_binary(str)
|
104
|
-
return self if str.nil?
|
105
|
-
self[:ipv6_v] = str[0,1].unpack("C").first >> 4
|
106
|
-
self[:ipv6_class] = (str[0,2].unpack("n").first & 0x0ff0) >> 4
|
107
|
-
self[:ipv6_label] = str[0,4].unpack("N").first & 0x000fffff
|
108
|
-
self[:ipv6_len].read(str[4,2])
|
109
|
-
self[:ipv6_next].read(str[6,1])
|
110
|
-
self[:ipv6_hop].read(str[7,1])
|
111
|
-
self[:ipv6_src].read(str[8,16])
|
112
|
-
self[:ipv6_dst].read(str[24,16])
|
113
|
-
self[:body].read(str[40,str.size]) if str.size > 40
|
114
|
-
self
|
115
|
-
end
|
116
|
-
|
117
|
-
# Setter for the version (usually, 6).
|
118
|
-
def ipv6_v=(i); self[:ip_v] = i.to_i; end
|
119
|
-
# Getter for the version (usually, 6).
|
120
|
-
def ipv6_v; self[:ipv6_v].to_i; end
|
121
|
-
# Setter for the traffic class.
|
122
|
-
def ipv6_class=(i); self[:ip_class] = i.to_i; end
|
123
|
-
# Getter for the traffic class.
|
124
|
-
def ipv6_class; self[:ipv6_class].to_i; end
|
125
|
-
# Setter for the flow label.
|
126
|
-
def ipv6_label=(i); self[:ip_label] = i.to_i; end
|
127
|
-
# Getter for the flow label.
|
128
|
-
def ipv6_label; self[:ipv6_label].to_i; end
|
129
|
-
# Setter for the payload length.
|
130
|
-
def ipv6_len=(i); typecast i; end
|
131
|
-
# Getter for the payload length.
|
132
|
-
def ipv6_len; self[:ipv6_len].to_i; end
|
133
|
-
# Setter for the next protocol header.
|
134
|
-
def ipv6_next=(i); typecast i; end
|
135
|
-
# Getter for the next protocol header.
|
136
|
-
def ipv6_next; self[:ipv6_next].to_i; end
|
137
|
-
# Setter for the hop limit.
|
138
|
-
def ipv6_hop=(i); typecast i; end
|
139
|
-
# Getter for the hop limit.
|
140
|
-
def ipv6_hop; self[:ipv6_hop].to_i; end
|
141
|
-
# Setter for the source address.
|
142
|
-
def ipv6_src=(i); typecast i; end
|
143
|
-
# Getter for the source address.
|
144
|
-
def ipv6_src; self[:ipv6_src].to_i; end
|
145
|
-
# Setter for the destination address.
|
146
|
-
def ipv6_dst=(i); typecast i; end
|
147
|
-
# Getter for the destination address.
|
148
|
-
def ipv6_dst; self[:ipv6_dst].to_i; end
|
149
|
-
|
150
|
-
# Calculates the payload length.
|
151
|
-
def ipv6_calc_len
|
152
|
-
self[:ipv6_len] = body.to_s.length
|
153
|
-
end
|
154
|
-
|
155
|
-
# Recalculates the calculatable fields for this object.
|
156
|
-
def ipv6_recalc(arg=:all)
|
157
|
-
case arg
|
158
|
-
when :ipv6_len
|
159
|
-
ipv6_calc_len
|
160
|
-
when :all
|
161
|
-
ipv6_recalc(:len)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# Get the source address in a more readable form.
|
166
|
-
def ipv6_saddr
|
167
|
-
self[:ipv6_src].to_x
|
168
|
-
end
|
169
|
-
|
170
|
-
# Set the source address in a more readable form.
|
171
|
-
def ipv6_saddr=(str)
|
172
|
-
self[:ipv6_src].read_x(str)
|
173
|
-
end
|
174
|
-
|
175
|
-
# Get the destination address in a more readable form.
|
176
|
-
def ipv6_daddr
|
177
|
-
self[:ipv6_dst].to_x
|
178
|
-
end
|
179
|
-
|
180
|
-
# Set the destination address in a more readable form.
|
181
|
-
def ipv6_daddr=(str)
|
182
|
-
self[:ipv6_dst].read_x(str)
|
183
|
-
end
|
184
|
-
|
185
|
-
# Readability aliases
|
186
|
-
|
187
|
-
alias :ipv6_src_readable :ipv6_saddr
|
188
|
-
alias :ipv6_dst_readable :ipv6_daddr
|
189
|
-
|
190
|
-
end # class IPv6Header
|
191
|
-
|
192
|
-
# IPv6Packet is used to construct IPv6 Packets. They contain an EthHeader and an IPv6Header, and in
|
193
|
-
# the distant, unknowable future, will take interesting IPv6ish payloads.
|
194
|
-
#
|
195
|
-
# This mostly complete, but not very useful. It's intended primarily as an example protocol.
|
196
|
-
#
|
197
|
-
# == Parameters
|
198
|
-
#
|
199
|
-
# :eth
|
200
|
-
# A pre-generated EthHeader object.
|
201
|
-
# :ip
|
202
|
-
# A pre-generated IPHeader object.
|
203
|
-
# :flavor
|
204
|
-
# TODO: Sets the "flavor" of the IPv6 packet. No idea what this will look like, haven't done much IPv6 fingerprinting.
|
205
|
-
# :config
|
206
|
-
# A hash of return address details, often the output of Utils.whoami?
|
207
|
-
class IPv6Packet < Packet
|
208
|
-
|
209
|
-
attr_accessor :eth_header, :ipv6_header
|
210
|
-
|
211
|
-
def self.can_parse?(str)
|
212
|
-
return false unless EthPacket.can_parse? str
|
213
|
-
return false unless str.size >= 54
|
214
|
-
return false unless str[12,2] == "\x86\xdd"
|
215
|
-
true
|
216
|
-
end
|
217
|
-
|
218
|
-
def read(str=nil,args={})
|
219
|
-
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
220
|
-
@eth_header.read(str)
|
221
|
-
@ipv6_header.read(str[14,str.size])
|
222
|
-
@eth_header.body = @ipv6_header
|
223
|
-
super(args)
|
224
|
-
self
|
225
|
-
end
|
226
|
-
|
227
|
-
def initialize(args={})
|
228
|
-
@eth_header = (args[:eth] || EthHeader.new)
|
229
|
-
@ipv6_header = (args[:ipv6] || IPv6Header.new)
|
230
|
-
@eth_header.eth_proto = 0x86dd
|
231
|
-
@eth_header.body=@ipv6_header
|
232
|
-
@headers = [@eth_header, @ipv6_header]
|
233
|
-
super
|
234
|
-
end
|
235
|
-
|
236
|
-
# Peek provides summary data on packet contents.
|
237
|
-
def peek(args={})
|
238
|
-
peek_data = ["6 "]
|
239
|
-
peek_data << "%-5d" % self.to_s.size
|
240
|
-
peek_data << "%-31s" % self.ipv6_saddr
|
241
|
-
peek_data << "-> "
|
242
|
-
peek_data << "%-31s" % self.ipv6_daddr
|
243
|
-
peek_data << " N:"
|
244
|
-
peek_data << self.ipv6_next.to_s(16)
|
245
|
-
peek_data.join
|
246
|
-
end
|
247
|
-
|
248
|
-
end
|
249
|
-
|
250
|
-
end
|
data/lib/packetfu/protos/tcp.rb
DELETED
@@ -1,1127 +0,0 @@
|
|
1
|
-
module PacketFu
|
2
|
-
|
3
|
-
# Implements the Explict Congestion Notification for TCPHeader.
|
4
|
-
#
|
5
|
-
# ==== Header Definition
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# Fixnum (1 bit) :n
|
9
|
-
# Fixnum (1 bit) :c
|
10
|
-
# Fixnum (1 bit) :e
|
11
|
-
class TcpEcn < Struct.new(:n, :c, :e)
|
12
|
-
|
13
|
-
include StructFu
|
14
|
-
|
15
|
-
def initialize(args={})
|
16
|
-
super(args[:n], args[:c], args[:e]) if args
|
17
|
-
end
|
18
|
-
|
19
|
-
# Returns the TcpEcn field as an integer... even though it's going
|
20
|
-
# to be split across a byte boundary.
|
21
|
-
def to_i
|
22
|
-
(n.to_i << 2) + (c.to_i << 1) + e.to_i
|
23
|
-
end
|
24
|
-
|
25
|
-
# Reads a string to populate the object.
|
26
|
-
def read(str)
|
27
|
-
force_binary(str)
|
28
|
-
return self if str.nil? || str.size < 2
|
29
|
-
if 1.respond_to? :ord
|
30
|
-
byte1 = str[0].ord
|
31
|
-
byte2 = str[1].ord
|
32
|
-
else
|
33
|
-
byte1 = str[0]
|
34
|
-
byte2 = str[1]
|
35
|
-
end
|
36
|
-
self[:n] = byte1 & 0b00000001 == 0b00000001 ? 1 : 0
|
37
|
-
self[:c] = byte2 & 0b10000000 == 0b10000000 ? 1 : 0
|
38
|
-
self[:e] = byte2 & 0b01000000 == 0b01000000 ? 1 : 0
|
39
|
-
self
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
# Implements the Header Length for TCPHeader.
|
45
|
-
#
|
46
|
-
# ==== Header Definition
|
47
|
-
#
|
48
|
-
# Fixnum (4 bits) :hlen
|
49
|
-
class TcpHlen < Struct.new(:hlen)
|
50
|
-
|
51
|
-
include StructFu
|
52
|
-
|
53
|
-
def initialize(args={})
|
54
|
-
super(args[:hlen])
|
55
|
-
end
|
56
|
-
|
57
|
-
# Returns the TcpHlen field as an integer. Note these will become the high
|
58
|
-
# bits at the TCP header's offset, even though the lower 4 bits
|
59
|
-
# will be further chopped up.
|
60
|
-
def to_i
|
61
|
-
hlen.to_i & 0b1111
|
62
|
-
end
|
63
|
-
|
64
|
-
# Reads a string to populate the object.
|
65
|
-
def read(str)
|
66
|
-
force_binary(str)
|
67
|
-
return self if str.nil? || str.size.zero?
|
68
|
-
if 1.respond_to? :ord
|
69
|
-
self[:hlen] = (str[0].ord & 0b11110000) >> 4
|
70
|
-
else
|
71
|
-
self[:hlen] = (str[0] & 0b11110000) >> 4
|
72
|
-
end
|
73
|
-
self
|
74
|
-
end
|
75
|
-
|
76
|
-
# Returns the object in string form.
|
77
|
-
def to_s
|
78
|
-
[self.to_i].pack("C")
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
# Implements the Reserved bits for TCPHeader.
|
84
|
-
#
|
85
|
-
# ==== Header Definition
|
86
|
-
#
|
87
|
-
#
|
88
|
-
# Fixnum (1 bit) :r1
|
89
|
-
# Fixnum (1 bit) :r2
|
90
|
-
# Fixnum (1 bit) :r3
|
91
|
-
class TcpReserved < Struct.new(:r1, :r2, :r3)
|
92
|
-
|
93
|
-
include StructFu
|
94
|
-
|
95
|
-
def initialize(args={})
|
96
|
-
super(
|
97
|
-
args[:r1] || 0,
|
98
|
-
args[:r2] || 0,
|
99
|
-
args[:r3] || 0) if args.kind_of? Hash
|
100
|
-
end
|
101
|
-
|
102
|
-
# Returns the Reserved field as an integer.
|
103
|
-
def to_i
|
104
|
-
(r1.to_i << 2) + (r2.to_i << 1) + r3.to_i
|
105
|
-
end
|
106
|
-
|
107
|
-
# Reads a string to populate the object.
|
108
|
-
def read(str)
|
109
|
-
force_binary(str)
|
110
|
-
return self if str.nil? || str.size.zero?
|
111
|
-
if 1.respond_to? :ord
|
112
|
-
byte = str[0].ord
|
113
|
-
else
|
114
|
-
byte = str[0]
|
115
|
-
end
|
116
|
-
self[:r1] = byte & 0b00000100 == 0b00000100 ? 1 : 0
|
117
|
-
self[:r2] = byte & 0b00000010 == 0b00000010 ? 1 : 0
|
118
|
-
self[:r3] = byte & 0b00000001 == 0b00000001 ? 1 : 0
|
119
|
-
self
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
# Implements flags for TCPHeader.
|
125
|
-
#
|
126
|
-
# ==== Header Definition
|
127
|
-
#
|
128
|
-
# Fixnum (1 bit) :urg
|
129
|
-
# Fixnum (1 bit) :ack
|
130
|
-
# Fixnum (1 bit) :psh
|
131
|
-
# Fixnum (1 bit) :rst
|
132
|
-
# Fixnum (1 bit) :syn
|
133
|
-
# Fixnum (1 bit) :fin
|
134
|
-
#
|
135
|
-
# Flags can typically be set by setting them either to 1 or 0, or to true or false.
|
136
|
-
class TcpFlags < Struct.new(:urg, :ack, :psh, :rst, :syn, :fin)
|
137
|
-
|
138
|
-
include StructFu
|
139
|
-
|
140
|
-
def initialize(args={})
|
141
|
-
# This technique attemts to ensure that flags are always 0 (off)
|
142
|
-
# or 1 (on). Statements like nil and false shouldn't be lurking in here.
|
143
|
-
if args.nil? || args.size.zero?
|
144
|
-
super( 0, 0, 0, 0, 0, 0)
|
145
|
-
else
|
146
|
-
super(
|
147
|
-
(args[:urg] ? 1 : 0),
|
148
|
-
(args[:ack] ? 1 : 0),
|
149
|
-
(args[:psh] ? 1 : 0),
|
150
|
-
(args[:rst] ? 1 : 0),
|
151
|
-
(args[:syn] ? 1 : 0),
|
152
|
-
(args[:fin] ? 1 : 0)
|
153
|
-
)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
# Returns the TcpFlags as an integer.
|
158
|
-
# Also not a great candidate for to_s due to the short bitspace.
|
159
|
-
def to_i
|
160
|
-
(urg.to_i << 5) + (ack.to_i << 4) + (psh.to_i << 3) +
|
161
|
-
(rst.to_i << 2) + (syn.to_i << 1) + fin.to_i
|
162
|
-
end
|
163
|
-
|
164
|
-
# Helper to determine if this flag is a 1 or a 0.
|
165
|
-
def zero_or_one(i=0)
|
166
|
-
if i == 0 || i == false || i == nil
|
167
|
-
0
|
168
|
-
else
|
169
|
-
1
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# Setter for the Urgent flag.
|
174
|
-
def urg=(i); self[:urg] = zero_or_one(i); end
|
175
|
-
# Setter for the Acknowlege flag.
|
176
|
-
def ack=(i); self[:ack] = zero_or_one(i); end
|
177
|
-
# Setter for the Push flag.
|
178
|
-
def psh=(i); self[:psh] = zero_or_one(i); end
|
179
|
-
# Setter for the Reset flag.
|
180
|
-
def rst=(i); self[:rst] = zero_or_one(i); end
|
181
|
-
# Setter for the Synchronize flag.
|
182
|
-
def syn=(i); self[:syn] = zero_or_one(i); end
|
183
|
-
# Setter for the Finish flag.
|
184
|
-
def fin=(i); self[:fin] = zero_or_one(i); end
|
185
|
-
|
186
|
-
# Reads a string to populate the object.
|
187
|
-
def read(str)
|
188
|
-
force_binary(str)
|
189
|
-
return self if str.nil?
|
190
|
-
if 1.respond_to? :ord
|
191
|
-
byte = str[0].ord
|
192
|
-
else
|
193
|
-
byte = str[0]
|
194
|
-
end
|
195
|
-
self[:urg] = byte & 0b00100000 == 0b00100000 ? 1 : 0
|
196
|
-
self[:ack] = byte & 0b00010000 == 0b00010000 ? 1 : 0
|
197
|
-
self[:psh] = byte & 0b00001000 == 0b00001000 ? 1 : 0
|
198
|
-
self[:rst] = byte & 0b00000100 == 0b00000100 ? 1 : 0
|
199
|
-
self[:syn] = byte & 0b00000010 == 0b00000010 ? 1 : 0
|
200
|
-
self[:fin] = byte & 0b00000001 == 0b00000001 ? 1 : 0
|
201
|
-
self
|
202
|
-
end
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
end
|
207
|
-
|
208
|
-
module PacketFu
|
209
|
-
|
210
|
-
# TcpOption is the base class for all TCP options. Note that TcpOption#len
|
211
|
-
# returns the size of the entire option, while TcpOption#optlen is the struct
|
212
|
-
# for the TCP Option Length field.
|
213
|
-
#
|
214
|
-
# Subclassed options should set the correct TcpOption#kind by redefining
|
215
|
-
# initialize. They should also deal with various value types there by setting
|
216
|
-
# them explicitly with an accompanying StructFu#typecast for the setter.
|
217
|
-
#
|
218
|
-
# By default, values are presumed to be strings, unless they are Numeric, in
|
219
|
-
# which case a guess is made to the width of the Numeric based on the given
|
220
|
-
# optlen.
|
221
|
-
#
|
222
|
-
# Note that normally, optlen is /not/ enforced for directly setting values,
|
223
|
-
# so the user is perfectly capable of setting incorrect lengths.
|
224
|
-
class TcpOption < Struct.new(:kind, :optlen, :value)
|
225
|
-
|
226
|
-
include StructFu
|
227
|
-
|
228
|
-
def initialize(args={})
|
229
|
-
super(
|
230
|
-
Int8.new(args[:kind]),
|
231
|
-
Int8.new(args[:optlen])
|
232
|
-
)
|
233
|
-
if args[:value].kind_of? Numeric
|
234
|
-
self[:value] = case args[:optlen]
|
235
|
-
when 3; Int8.new(args[:value])
|
236
|
-
when 4; Int16.new(args[:value])
|
237
|
-
when 6; Int32.new(args[:value])
|
238
|
-
else; StructFu::String.new.read(args[:value])
|
239
|
-
end
|
240
|
-
else
|
241
|
-
self[:value] = StructFu::String.new.read(args[:value])
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
# Returns the object in string form.
|
246
|
-
def to_s
|
247
|
-
self[:kind].to_s +
|
248
|
-
(self[:optlen].value.nil? ? nil : self[:optlen]).to_s +
|
249
|
-
(self[:value].nil? ? nil : self[:value]).to_s
|
250
|
-
end
|
251
|
-
|
252
|
-
# Reads a string to populate the object.
|
253
|
-
def read(str)
|
254
|
-
force_binary(str)
|
255
|
-
return self if str.nil?
|
256
|
-
self[:kind].read(str[0,1])
|
257
|
-
if str[1,1]
|
258
|
-
self[:optlen].read(str[1,1])
|
259
|
-
if str[2,1] && optlen.value > 2
|
260
|
-
self[:value].read(str[2,optlen.value-2])
|
261
|
-
end
|
262
|
-
end
|
263
|
-
self
|
264
|
-
end
|
265
|
-
|
266
|
-
# The default decode for an unknown option. Known options should redefine this.
|
267
|
-
def decode
|
268
|
-
unk = "unk-#{self.kind.to_i}"
|
269
|
-
(self[:optlen].to_i > 2 && self[:value].to_s.size > 1) ? [unk,self[:value]].join(":") : unk
|
270
|
-
end
|
271
|
-
|
272
|
-
# Setter for the "kind" byte of this option.
|
273
|
-
def kind=(i); typecast i; end
|
274
|
-
# Setter for the "option length" byte for this option.
|
275
|
-
def optlen=(i); typecast i; end
|
276
|
-
|
277
|
-
# Setter for the value of this option.
|
278
|
-
def value=(i)
|
279
|
-
if i.kind_of? Numeric
|
280
|
-
typecast i
|
281
|
-
elsif i.respond_to? :to_s
|
282
|
-
self[:value] = i
|
283
|
-
else
|
284
|
-
self[:value] = ''
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
# Generally, encoding a value is going to be just a read. Some
|
289
|
-
# options will treat things a little differently; TS for example,
|
290
|
-
# takes two values and concatenates them.
|
291
|
-
def encode(str)
|
292
|
-
self[:value] = self.class.new(:value => str).value
|
293
|
-
end
|
294
|
-
|
295
|
-
# Returns true if this option has an optlen. Some don't.
|
296
|
-
def has_optlen?
|
297
|
-
(kind.value && kind.value < 2) ? false : true
|
298
|
-
end
|
299
|
-
|
300
|
-
# Returns true if this option has a value. Some don't.
|
301
|
-
def has_value?
|
302
|
-
(value.respond_to? :to_s && value.to_s.size > 0) ? false : true
|
303
|
-
end
|
304
|
-
|
305
|
-
# End of Line option. Usually used to terminate a string of options.
|
306
|
-
#
|
307
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option000.htm
|
308
|
-
class EOL < TcpOption
|
309
|
-
def initialize(args={})
|
310
|
-
super(
|
311
|
-
args.merge(:kind => 0)
|
312
|
-
)
|
313
|
-
end
|
314
|
-
|
315
|
-
def decode
|
316
|
-
"EOL"
|
317
|
-
end
|
318
|
-
|
319
|
-
end
|
320
|
-
|
321
|
-
# No Operation option. Usually used to pad out options to fit a 4-byte alignment.
|
322
|
-
#
|
323
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option001.htm
|
324
|
-
class NOP < TcpOption
|
325
|
-
def initialize(args={})
|
326
|
-
super(
|
327
|
-
args.merge(:kind => 1)
|
328
|
-
)
|
329
|
-
end
|
330
|
-
|
331
|
-
def decode
|
332
|
-
"NOP"
|
333
|
-
end
|
334
|
-
|
335
|
-
end
|
336
|
-
|
337
|
-
# Maximum Segment Size option.
|
338
|
-
#
|
339
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option002.htm
|
340
|
-
class MSS < TcpOption
|
341
|
-
def initialize(args={})
|
342
|
-
super(
|
343
|
-
args.merge(:kind => 2,
|
344
|
-
:optlen => 4
|
345
|
-
)
|
346
|
-
)
|
347
|
-
self[:value] = Int16.new(args[:value])
|
348
|
-
end
|
349
|
-
|
350
|
-
def value=(i); typecast i; end
|
351
|
-
|
352
|
-
# MSS options with lengths other than 4 are malformed.
|
353
|
-
def decode
|
354
|
-
if self[:optlen].to_i == 4
|
355
|
-
"MSS:#{self[:value].to_i}"
|
356
|
-
else
|
357
|
-
"MSS-bad:#{self[:value]}"
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
end
|
362
|
-
|
363
|
-
# Window Size option.
|
364
|
-
#
|
365
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option003.htm
|
366
|
-
class WS < TcpOption
|
367
|
-
def initialize(args={})
|
368
|
-
super(
|
369
|
-
args.merge(:kind => 3,
|
370
|
-
:optlen => 3
|
371
|
-
)
|
372
|
-
)
|
373
|
-
self[:value] = Int8.new(args[:value])
|
374
|
-
end
|
375
|
-
|
376
|
-
def value=(i); typecast i; end
|
377
|
-
|
378
|
-
# WS options with lengths other than 3 are malformed.
|
379
|
-
def decode
|
380
|
-
if self[:optlen].to_i == 3
|
381
|
-
"WS:#{self[:value].to_i}"
|
382
|
-
else
|
383
|
-
"WS-bad:#{self[:value]}"
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
end
|
388
|
-
|
389
|
-
# Selective Acknowlegment OK option.
|
390
|
-
#
|
391
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option004.htm
|
392
|
-
class SACKOK < TcpOption
|
393
|
-
def initialize(args={})
|
394
|
-
super(
|
395
|
-
args.merge(:kind => 4,
|
396
|
-
:optlen => 2)
|
397
|
-
)
|
398
|
-
end
|
399
|
-
|
400
|
-
# SACKOK options with sizes other than 2 are malformed.
|
401
|
-
def decode
|
402
|
-
if self[:optlen].to_i == 2
|
403
|
-
"SACKOK"
|
404
|
-
else
|
405
|
-
"SACKOK-bad:#{self[:value]}"
|
406
|
-
end
|
407
|
-
end
|
408
|
-
|
409
|
-
end
|
410
|
-
|
411
|
-
# Selective Acknowledgement option.
|
412
|
-
#
|
413
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option004.htm
|
414
|
-
#
|
415
|
-
# Note that SACK always takes its optlen from the size of the string.
|
416
|
-
class SACK < TcpOption
|
417
|
-
def initialize(args={})
|
418
|
-
super(
|
419
|
-
args.merge(:kind => 5,
|
420
|
-
:optlen => ((args[:value] || "").size + 2)
|
421
|
-
)
|
422
|
-
)
|
423
|
-
end
|
424
|
-
|
425
|
-
def optlen=(i); typecast i; end
|
426
|
-
|
427
|
-
def value=(i)
|
428
|
-
self[:optlen] = Int8.new(i.to_s.size + 2)
|
429
|
-
self[:value] = StructFu::String.new(i)
|
430
|
-
end
|
431
|
-
|
432
|
-
def decode
|
433
|
-
"SACK:#{self[:value]}"
|
434
|
-
end
|
435
|
-
|
436
|
-
def encode(str)
|
437
|
-
temp_obj = self.class.new(:value => str)
|
438
|
-
self[:value] = temp_obj.value
|
439
|
-
self[:optlen] = temp_obj.optlen.value
|
440
|
-
self
|
441
|
-
end
|
442
|
-
|
443
|
-
end
|
444
|
-
|
445
|
-
# Echo option.
|
446
|
-
#
|
447
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option006.htm
|
448
|
-
class ECHO < TcpOption
|
449
|
-
def initialize(args={})
|
450
|
-
super(
|
451
|
-
args.merge(:kind => 6,
|
452
|
-
:optlen => 6
|
453
|
-
)
|
454
|
-
)
|
455
|
-
end
|
456
|
-
|
457
|
-
# ECHO options with lengths other than 6 are malformed.
|
458
|
-
def decode
|
459
|
-
if self[:optlen].to_i == 6
|
460
|
-
"ECHO:#{self[:value]}"
|
461
|
-
else
|
462
|
-
"ECHO-bad:#{self[:value]}"
|
463
|
-
end
|
464
|
-
end
|
465
|
-
|
466
|
-
end
|
467
|
-
|
468
|
-
# Echo Reply option.
|
469
|
-
#
|
470
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option007.htm
|
471
|
-
class ECHOREPLY < TcpOption
|
472
|
-
def initialize(args={})
|
473
|
-
super(
|
474
|
-
args.merge(:kind => 7,
|
475
|
-
:optlen => 6
|
476
|
-
)
|
477
|
-
)
|
478
|
-
end
|
479
|
-
|
480
|
-
# ECHOREPLY options with lengths other than 6 are malformed.
|
481
|
-
def decode
|
482
|
-
if self[:optlen].to_i == 6
|
483
|
-
"ECHOREPLY:#{self[:value]}"
|
484
|
-
else
|
485
|
-
"ECHOREPLY-bad:#{self[:value]}"
|
486
|
-
end
|
487
|
-
end
|
488
|
-
|
489
|
-
end
|
490
|
-
|
491
|
-
# Timestamp option
|
492
|
-
#
|
493
|
-
# http://www.networksorcery.com/enp/protocol/tcp/option008.htm
|
494
|
-
class TS < TcpOption
|
495
|
-
def initialize(args={})
|
496
|
-
super(
|
497
|
-
args.merge(:kind => 8,
|
498
|
-
:optlen => 10
|
499
|
-
)
|
500
|
-
)
|
501
|
-
self[:value] = StructFu::String.new.read(args[:value] || "\x00" * 8)
|
502
|
-
end
|
503
|
-
|
504
|
-
# TS options with lengths other than 10 are malformed.
|
505
|
-
def decode
|
506
|
-
if self[:optlen].to_i == 10
|
507
|
-
val1,val2 = self[:value].unpack("NN")
|
508
|
-
"TS:#{val1};#{val2}"
|
509
|
-
else
|
510
|
-
"TS-bad:#{self[:value]}"
|
511
|
-
end
|
512
|
-
end
|
513
|
-
|
514
|
-
# TS options are in the format of "TS:[timestamp value];[timestamp secret]" Both
|
515
|
-
# should be written as decimal numbers.
|
516
|
-
def encode(str)
|
517
|
-
if str =~ /^([0-9]+);([0-9]+)$/
|
518
|
-
tsval,tsecr = str.split(";").map {|x| x.to_i}
|
519
|
-
if tsval <= 0xffffffff && tsecr <= 0xffffffff
|
520
|
-
self[:value] = StructFu::String.new([tsval,tsecr].pack("NN"))
|
521
|
-
else
|
522
|
-
self[:value] = StructFu::String.new(str)
|
523
|
-
end
|
524
|
-
else
|
525
|
-
self[:value] = StructFu::String.new(str)
|
526
|
-
end
|
527
|
-
end
|
528
|
-
|
529
|
-
end
|
530
|
-
|
531
|
-
end
|
532
|
-
|
533
|
-
class TcpOptions < Array
|
534
|
-
|
535
|
-
include StructFu
|
536
|
-
|
537
|
-
# If args[:pad] is set, the options line is automatically padded out
|
538
|
-
# with NOPs.
|
539
|
-
def to_s(args={})
|
540
|
-
opts = self.map {|x| x.to_s}.join
|
541
|
-
if args[:pad]
|
542
|
-
unless (opts.size % 4).zero?
|
543
|
-
(4 - (opts.size % 4)).times { opts << "\x01" }
|
544
|
-
end
|
545
|
-
end
|
546
|
-
opts
|
547
|
-
end
|
548
|
-
|
549
|
-
# Reads a string to populate the object.
|
550
|
-
def read(str)
|
551
|
-
self.clear
|
552
|
-
PacketFu.force_binary(str)
|
553
|
-
return self if(!str.respond_to? :to_s || str.nil?)
|
554
|
-
i = 0
|
555
|
-
while i < str.to_s.size
|
556
|
-
this_opt = case str[i,1].unpack("C").first
|
557
|
-
when 0; TcpOption::EOL.new
|
558
|
-
when 1; TcpOption::NOP.new
|
559
|
-
when 2; TcpOption::MSS.new
|
560
|
-
when 3; TcpOption::WS.new
|
561
|
-
when 4; TcpOption::SACKOK.new
|
562
|
-
when 5; TcpOption::SACK.new
|
563
|
-
when 6; TcpOption::ECHO.new
|
564
|
-
when 7; TcpOption::ECHOREPLY.new
|
565
|
-
when 8; TcpOption::TS.new
|
566
|
-
else; TcpOption.new
|
567
|
-
end
|
568
|
-
this_opt.read str[i,str.size]
|
569
|
-
unless this_opt.has_optlen?
|
570
|
-
this_opt.value = nil
|
571
|
-
this_opt.optlen = nil
|
572
|
-
end
|
573
|
-
self << this_opt
|
574
|
-
i += this_opt.sz
|
575
|
-
end
|
576
|
-
self
|
577
|
-
end
|
578
|
-
|
579
|
-
# Decode parses the TcpOptions object's member options, and produces a
|
580
|
-
# human-readable string by iterating over each element's decode() function.
|
581
|
-
# If TcpOptions elements were not initially created as TcpOptions, an
|
582
|
-
# attempt will be made to convert them.
|
583
|
-
#
|
584
|
-
# The output of decode is suitable as input for TcpOptions#encode.
|
585
|
-
def decode
|
586
|
-
decoded = self.map do |x|
|
587
|
-
if x.kind_of? TcpOption
|
588
|
-
x.decode
|
589
|
-
else
|
590
|
-
x = TcpOptions.new.read(x).decode
|
591
|
-
end
|
592
|
-
end
|
593
|
-
decoded.join(",")
|
594
|
-
end
|
595
|
-
|
596
|
-
# Encode takes a human-readable string and appends the corresponding
|
597
|
-
# binary options to the TcpOptions object. To completely replace the contents
|
598
|
-
# of the object, use TcpOptions#encode! instead.
|
599
|
-
#
|
600
|
-
# Options are comma-delimited, and are identical to the output of the
|
601
|
-
# TcpOptions#decode function. Note that the syntax can be unforgiving, so
|
602
|
-
# it may be easier to create the subclassed TcpOptions themselves directly,
|
603
|
-
# but this method can be less typing if you know what you're doing.
|
604
|
-
#
|
605
|
-
# Note that by using TcpOptions#encode, strings supplied as values which
|
606
|
-
# can be converted to numbers will be converted first.
|
607
|
-
#
|
608
|
-
# === Example
|
609
|
-
#
|
610
|
-
# t = TcpOptions.new
|
611
|
-
# t.encode("MS:1460,WS:6")
|
612
|
-
# t.to_s # => "\002\004\005\264\002\003\006"
|
613
|
-
# t.encode("NOP")
|
614
|
-
# t.to_s # => "\002\004\005\264\002\003\006\001"
|
615
|
-
def encode(str)
|
616
|
-
opts = str.split(/[\s]*,[\s]*/)
|
617
|
-
opts.each do |o|
|
618
|
-
kind,value = o.split(/[\s]*:[\s]*/)
|
619
|
-
klass = TcpOption.const_get(kind.upcase)
|
620
|
-
value = value.to_i if value =~ /^[0-9]+$/
|
621
|
-
this_opt = klass.new
|
622
|
-
this_opt.encode(value)
|
623
|
-
self << this_opt
|
624
|
-
end
|
625
|
-
self
|
626
|
-
end
|
627
|
-
|
628
|
-
# Like TcpOption#encode, except the entire contents are replaced.
|
629
|
-
def encode!(str)
|
630
|
-
self.clear if self.size > 0
|
631
|
-
encode(str)
|
632
|
-
end
|
633
|
-
|
634
|
-
end
|
635
|
-
|
636
|
-
end
|
637
|
-
|
638
|
-
module PacketFu
|
639
|
-
|
640
|
-
# TCPHeader is a complete TCP struct, used in TCPPacket. Most IP traffic is TCP-based, by
|
641
|
-
# volume.
|
642
|
-
#
|
643
|
-
# For more on TCP packets, see http://www.networksorcery.com/enp/protocol/tcp.htm
|
644
|
-
#
|
645
|
-
# ==== Header Definition
|
646
|
-
#
|
647
|
-
# Int16 :tcp_src Default: random
|
648
|
-
# Int16 :tcp_dst
|
649
|
-
# Int32 :tcp_seq Default: random
|
650
|
-
# Int32 :tcp_ack
|
651
|
-
# TcpHlen :tcp_hlen Default: 5 # Must recalc as options are set.
|
652
|
-
# TcpReserved :tcp_reserved Default: 0
|
653
|
-
# TcpEcn :tcp_ecn
|
654
|
-
# TcpFlags :tcp_flags
|
655
|
-
# Int16 :tcp_win, Default: 0 # WinXP's default syn packet
|
656
|
-
# Int16 :tcp_sum, Default: calculated # Must set this upon generation.
|
657
|
-
# Int16 :tcp_urg
|
658
|
-
# TcpOptions :tcp_opts
|
659
|
-
# String :body
|
660
|
-
#
|
661
|
-
# See also TcpHlen, TcpReserved, TcpEcn, TcpFlags, TcpOpts
|
662
|
-
class TCPHeader < Struct.new(:tcp_src, :tcp_dst,
|
663
|
-
:tcp_seq,
|
664
|
-
:tcp_ack,
|
665
|
-
:tcp_hlen, :tcp_reserved, :tcp_ecn, :tcp_flags, :tcp_win,
|
666
|
-
:tcp_sum, :tcp_urg,
|
667
|
-
:tcp_opts, :body)
|
668
|
-
include StructFu
|
669
|
-
|
670
|
-
def initialize(args={})
|
671
|
-
@random_seq = rand(0xffffffff)
|
672
|
-
@random_src = rand_port
|
673
|
-
super(
|
674
|
-
Int16.new(args[:tcp_src] || tcp_calc_src),
|
675
|
-
Int16.new(args[:tcp_dst]),
|
676
|
-
Int32.new(args[:tcp_seq] || tcp_calc_seq),
|
677
|
-
Int32.new(args[:tcp_ack]),
|
678
|
-
TcpHlen.new(:hlen => (args[:tcp_hlen] || 5)),
|
679
|
-
TcpReserved.new(args[:tcp_reserved] || 0),
|
680
|
-
TcpEcn.new(args[:tcp_ecn]),
|
681
|
-
TcpFlags.new(args[:tcp_flags]),
|
682
|
-
Int16.new(args[:tcp_win] || 0x4000),
|
683
|
-
Int16.new(args[:tcp_sum] || 0),
|
684
|
-
Int16.new(args[:tcp_urg]),
|
685
|
-
TcpOptions.new.read(args[:tcp_opts]),
|
686
|
-
StructFu::String.new.read(args[:body])
|
687
|
-
)
|
688
|
-
end
|
689
|
-
|
690
|
-
attr_accessor :flavor
|
691
|
-
|
692
|
-
# Helper function to create the string for Hlen, Reserved, ECN, and Flags.
|
693
|
-
def bits_to_s
|
694
|
-
bytes = []
|
695
|
-
bytes[0] = (self[:tcp_hlen].to_i << 4) +
|
696
|
-
(self[:tcp_reserved].to_i << 1) +
|
697
|
-
self[:tcp_ecn].n.to_i
|
698
|
-
bytes[1] = (self[:tcp_ecn].c.to_i << 7) +
|
699
|
-
(self[:tcp_ecn].e.to_i << 6) +
|
700
|
-
self[:tcp_flags].to_i
|
701
|
-
bytes.pack("CC")
|
702
|
-
end
|
703
|
-
|
704
|
-
# Returns the object in string form.
|
705
|
-
def to_s
|
706
|
-
hdr = self.to_a.map do |x|
|
707
|
-
if x.kind_of? TcpHlen
|
708
|
-
bits_to_s
|
709
|
-
elsif x.kind_of? TcpReserved
|
710
|
-
next
|
711
|
-
elsif x.kind_of? TcpEcn
|
712
|
-
next
|
713
|
-
elsif x.kind_of? TcpFlags
|
714
|
-
next
|
715
|
-
else
|
716
|
-
x.to_s
|
717
|
-
end
|
718
|
-
end
|
719
|
-
hdr.flatten.join
|
720
|
-
end
|
721
|
-
|
722
|
-
# Reads a string to populate the object.
|
723
|
-
def read(str)
|
724
|
-
force_binary(str)
|
725
|
-
return self if str.nil?
|
726
|
-
self[:tcp_src].read(str[0,2])
|
727
|
-
self[:tcp_dst].read(str[2,2])
|
728
|
-
self[:tcp_seq].read(str[4,4])
|
729
|
-
self[:tcp_ack].read(str[8,4])
|
730
|
-
self[:tcp_hlen].read(str[12,1])
|
731
|
-
self[:tcp_reserved].read(str[12,1])
|
732
|
-
self[:tcp_ecn].read(str[12,2])
|
733
|
-
self[:tcp_flags].read(str[13,1])
|
734
|
-
self[:tcp_win].read(str[14,2])
|
735
|
-
self[:tcp_sum].read(str[16,2])
|
736
|
-
self[:tcp_urg].read(str[18,2])
|
737
|
-
self[:tcp_opts].read(str[20,((self[:tcp_hlen].to_i * 4) - 20)])
|
738
|
-
self[:body].read(str[(self[:tcp_hlen].to_i * 4),str.size])
|
739
|
-
self
|
740
|
-
end
|
741
|
-
|
742
|
-
# Setter for the TCP source port.
|
743
|
-
def tcp_src=(i); typecast i; end
|
744
|
-
# Getter for the TCP source port.
|
745
|
-
def tcp_src; self[:tcp_src].to_i; end
|
746
|
-
# Setter for the TCP destination port.
|
747
|
-
def tcp_dst=(i); typecast i; end
|
748
|
-
# Getter for the TCP destination port.
|
749
|
-
def tcp_dst; self[:tcp_dst].to_i; end
|
750
|
-
# Setter for the TCP sequence number.
|
751
|
-
def tcp_seq=(i); typecast i; end
|
752
|
-
# Getter for the TCP sequence number.
|
753
|
-
def tcp_seq; self[:tcp_seq].to_i; end
|
754
|
-
# Setter for the TCP ackowlegement number.
|
755
|
-
def tcp_ack=(i); typecast i; end
|
756
|
-
# Getter for the TCP ackowlegement number.
|
757
|
-
def tcp_ack; self[:tcp_ack].to_i; end
|
758
|
-
# Setter for the TCP window size number.
|
759
|
-
def tcp_win=(i); typecast i; end
|
760
|
-
# Getter for the TCP window size number.
|
761
|
-
def tcp_win; self[:tcp_win].to_i; end
|
762
|
-
# Setter for the TCP checksum.
|
763
|
-
def tcp_sum=(i); typecast i; end
|
764
|
-
# Getter for the TCP checksum.
|
765
|
-
def tcp_sum; self[:tcp_sum].to_i; end
|
766
|
-
# Setter for the TCP urgent field.
|
767
|
-
def tcp_urg=(i); typecast i; end
|
768
|
-
# Getter for the TCP urgent field.
|
769
|
-
def tcp_urg; self[:tcp_urg].to_i; end
|
770
|
-
|
771
|
-
# Getter for the TCP Header Length value.
|
772
|
-
def tcp_hlen; self[:tcp_hlen].to_i; end
|
773
|
-
# Setter for the TCP Header Length value. Can take
|
774
|
-
# either a string or an integer. Note that if it's
|
775
|
-
# a string, the top four bits are used.
|
776
|
-
def tcp_hlen=(i)
|
777
|
-
case i
|
778
|
-
when PacketFu::TcpHlen
|
779
|
-
self[:tcp_hlen] = i
|
780
|
-
when Numeric
|
781
|
-
self[:tcp_hlen] = TcpHlen.new(:hlen => i.to_i)
|
782
|
-
else
|
783
|
-
self[:tcp_hlen].read(i)
|
784
|
-
end
|
785
|
-
end
|
786
|
-
|
787
|
-
# Getter for the TCP Reserved field.
|
788
|
-
def tcp_reserved; self[:tcp_reserved].to_i; end
|
789
|
-
# Setter for the TCP Reserved field.
|
790
|
-
def tcp_reserved=(i)
|
791
|
-
case i
|
792
|
-
when PacketFu::TcpReserved
|
793
|
-
self[:tcp_reserved]=i
|
794
|
-
when Numeric
|
795
|
-
args = {}
|
796
|
-
args[:r1] = (i & 0b100) >> 2
|
797
|
-
args[:r2] = (i & 0b010) >> 1
|
798
|
-
args[:r3] = (i & 0b001)
|
799
|
-
self[:tcp_reserved] = TcpReserved.new(args)
|
800
|
-
else
|
801
|
-
self[:tcp_reserved].read(i)
|
802
|
-
end
|
803
|
-
end
|
804
|
-
|
805
|
-
# Getter for the ECN bits.
|
806
|
-
def tcp_ecn; self[:tcp_ecn].to_i; end
|
807
|
-
# Setter for the ECN bits.
|
808
|
-
def tcp_ecn=(i)
|
809
|
-
case i
|
810
|
-
when PacketFu::TcpEcn
|
811
|
-
self[:tcp_ecn]=i
|
812
|
-
when Numeric
|
813
|
-
args = {}
|
814
|
-
args[:n] = (i & 0b100) >> 2
|
815
|
-
args[:c] = (i & 0b010) >> 1
|
816
|
-
args[:e] = (i & 0b001)
|
817
|
-
self[:tcp_ecn] = TcpEcn.new(args)
|
818
|
-
else
|
819
|
-
self[:tcp_ecn].read(i)
|
820
|
-
end
|
821
|
-
end
|
822
|
-
|
823
|
-
# Getter for TCP Options.
|
824
|
-
def tcp_opts; self[:tcp_opts].to_s; end
|
825
|
-
# Setter for TCP Options.
|
826
|
-
def tcp_opts=(i)
|
827
|
-
case i
|
828
|
-
when PacketFu::TcpOptions
|
829
|
-
self[:tcp_opts]=i
|
830
|
-
else
|
831
|
-
self[:tcp_opts].read(i)
|
832
|
-
end
|
833
|
-
end
|
834
|
-
|
835
|
-
# Resets the sequence number to a new random number.
|
836
|
-
def tcp_calc_seq; @random_seq; end
|
837
|
-
# Resets the source port to a new random number.
|
838
|
-
def tcp_calc_src; @random_src; end
|
839
|
-
|
840
|
-
# Returns the actual length of the TCP options.
|
841
|
-
def tcp_opts_len
|
842
|
-
self[:tcp_opts].to_s.size
|
843
|
-
end
|
844
|
-
|
845
|
-
# Sets and returns the true length of the TCP Header.
|
846
|
-
# TODO: Think about making all the option stuff safer.
|
847
|
-
def tcp_calc_hlen
|
848
|
-
self[:tcp_hlen] = TcpHlen.new(:hlen => ((20 + tcp_opts_len) / 4))
|
849
|
-
end
|
850
|
-
|
851
|
-
# Generates a random high port. This is affected by packet flavor.
|
852
|
-
def rand_port
|
853
|
-
rand(0xffff - 1025) + 1025
|
854
|
-
end
|
855
|
-
|
856
|
-
# Gets a more readable option list.
|
857
|
-
def tcp_options
|
858
|
-
self[:tcp_opts].decode
|
859
|
-
end
|
860
|
-
|
861
|
-
# Gets a more readable flags list
|
862
|
-
def tcp_flags_dotmap
|
863
|
-
dotmap = tcp_flags.members.map do |flag|
|
864
|
-
status = self.tcp_flags.send flag
|
865
|
-
status == 0 ? "." : flag.to_s.upcase[0].chr
|
866
|
-
end
|
867
|
-
dotmap.join
|
868
|
-
end
|
869
|
-
|
870
|
-
# Sets a more readable option list.
|
871
|
-
def tcp_options=(arg)
|
872
|
-
self[:tcp_opts].encode arg
|
873
|
-
end
|
874
|
-
|
875
|
-
# Equivalent to tcp_src.
|
876
|
-
def tcp_sport
|
877
|
-
self.tcp_src.to_i
|
878
|
-
end
|
879
|
-
|
880
|
-
# Equivalent to tcp_src=.
|
881
|
-
def tcp_sport=(arg)
|
882
|
-
self.tcp_src=(arg)
|
883
|
-
end
|
884
|
-
|
885
|
-
# Equivalent to tcp_dst.
|
886
|
-
def tcp_dport
|
887
|
-
self.tcp_dst.to_i
|
888
|
-
end
|
889
|
-
|
890
|
-
# Equivalent to tcp_dst=.
|
891
|
-
def tcp_dport=(arg)
|
892
|
-
self.tcp_dst=(arg)
|
893
|
-
end
|
894
|
-
|
895
|
-
# Recalculates calculated fields for TCP (except checksum which is at the Packet level).
|
896
|
-
def tcp_recalc(arg=:all)
|
897
|
-
case arg
|
898
|
-
when :tcp_hlen
|
899
|
-
tcp_calc_hlen
|
900
|
-
when :tcp_src
|
901
|
-
@random_tcp_src = rand_port
|
902
|
-
when :tcp_sport
|
903
|
-
@random_tcp_src = rand_port
|
904
|
-
when :tcp_seq
|
905
|
-
@random_tcp_seq = rand(0xffffffff)
|
906
|
-
when :all
|
907
|
-
tcp_calc_hlen
|
908
|
-
@random_tcp_src = rand_port
|
909
|
-
@random_tcp_seq = rand(0xffffffff)
|
910
|
-
else
|
911
|
-
raise ArgumentError, "No such field `#{arg}'"
|
912
|
-
end
|
913
|
-
end
|
914
|
-
|
915
|
-
# Readability aliases
|
916
|
-
|
917
|
-
alias :tcp_flags_readable :tcp_flags_dotmap
|
918
|
-
|
919
|
-
def tcp_ack_readable
|
920
|
-
"0x%08x" % tcp_ack
|
921
|
-
end
|
922
|
-
|
923
|
-
def tcp_seq_readable
|
924
|
-
"0x%08x" % tcp_seq
|
925
|
-
end
|
926
|
-
|
927
|
-
def tcp_sum_readable
|
928
|
-
"0x%04x" % tcp_sum
|
929
|
-
end
|
930
|
-
|
931
|
-
def tcp_opts_readable
|
932
|
-
tcp_options
|
933
|
-
end
|
934
|
-
|
935
|
-
end
|
936
|
-
|
937
|
-
# TCPPacket is used to construct TCP packets. They contain an EthHeader, an IPHeader, and a TCPHeader.
|
938
|
-
#
|
939
|
-
# == Example
|
940
|
-
#
|
941
|
-
# tcp_pkt = PacketFu::TCPPacket.new
|
942
|
-
# tcp_pkt.tcp_flags.syn=1
|
943
|
-
# tcp_pkt.tcp_dst=80
|
944
|
-
# tcp_pkt.tcp_win=5840
|
945
|
-
# tcp_pkt.tcp_options="mss:1460,sack.ok,ts:#{rand(0xffffffff)};0,nop,ws:7"
|
946
|
-
#
|
947
|
-
# tcp_pkt.ip_saddr=[rand(0xff),rand(0xff),rand(0xff),rand(0xff)].join('.')
|
948
|
-
# tcp_pkt.ip_daddr=[rand(0xff),rand(0xff),rand(0xff),rand(0xff)].join('.')
|
949
|
-
#
|
950
|
-
# tcp_pkt.recalc
|
951
|
-
# tcp_pkt.to_f('/tmp/tcp.pcap')
|
952
|
-
#
|
953
|
-
# == Parameters
|
954
|
-
# :eth
|
955
|
-
# A pre-generated EthHeader object.
|
956
|
-
# :ip
|
957
|
-
# A pre-generated IPHeader object.
|
958
|
-
# :flavor
|
959
|
-
# TODO: Sets the "flavor" of the TCP packet. This will include TCP options and the initial window
|
960
|
-
# size, per stack. There is a lot of variety here, and it's one of the most useful methods to
|
961
|
-
# remotely fingerprint devices. :flavor will span both ip and tcp for consistency.
|
962
|
-
# :type
|
963
|
-
# TODO: Set up particular types of packets (syn, psh_ack, rst, etc). This can change the initial flavor.
|
964
|
-
# :config
|
965
|
-
# A hash of return address details, often the output of Utils.whoami?
|
966
|
-
class TCPPacket < Packet
|
967
|
-
|
968
|
-
attr_accessor :eth_header, :ip_header, :tcp_header
|
969
|
-
|
970
|
-
def self.can_parse?(str)
|
971
|
-
return false unless str.size >= 54
|
972
|
-
return false unless EthPacket.can_parse? str
|
973
|
-
return false unless IPPacket.can_parse? str
|
974
|
-
return false unless str[23,1] == "\x06"
|
975
|
-
return true
|
976
|
-
end
|
977
|
-
|
978
|
-
def read(str=nil, args={})
|
979
|
-
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
980
|
-
@eth_header.read(str)
|
981
|
-
@ip_header.read(str[14,str.size])
|
982
|
-
@eth_header.body = @ip_header
|
983
|
-
if args[:strip]
|
984
|
-
tcp_len = str[16,2].unpack("n")[0] - 20
|
985
|
-
@tcp_header.read(str[14+(@ip_header.ip_hlen),tcp_len])
|
986
|
-
else
|
987
|
-
@tcp_header.read(str[14+(@ip_header.ip_hlen),str.size])
|
988
|
-
end
|
989
|
-
@ip_header.body = @tcp_header
|
990
|
-
super(args)
|
991
|
-
self
|
992
|
-
end
|
993
|
-
|
994
|
-
def initialize(args={})
|
995
|
-
@eth_header = (args[:eth] || EthHeader.new)
|
996
|
-
@ip_header = (args[:ip] || IPHeader.new)
|
997
|
-
@tcp_header = (args[:tcp] || TCPHeader.new)
|
998
|
-
@tcp_header.flavor = args[:flavor].to_s.downcase
|
999
|
-
|
1000
|
-
@ip_header.body = @tcp_header
|
1001
|
-
@eth_header.body = @ip_header
|
1002
|
-
@headers = [@eth_header, @ip_header, @tcp_header]
|
1003
|
-
|
1004
|
-
@ip_header.ip_proto=0x06
|
1005
|
-
super
|
1006
|
-
if args[:flavor]
|
1007
|
-
tcp_calc_flavor(@tcp_header.flavor)
|
1008
|
-
else
|
1009
|
-
tcp_calc_sum
|
1010
|
-
end
|
1011
|
-
end
|
1012
|
-
|
1013
|
-
# Sets the correct flavor for TCP Packets. Recognized flavors are:
|
1014
|
-
# windows, linux, freebsd
|
1015
|
-
def tcp_calc_flavor(str)
|
1016
|
-
ts_val = Time.now.to_i + rand(0x4fffffff)
|
1017
|
-
ts_sec = rand(0xffffff)
|
1018
|
-
case @tcp_header.flavor = str.to_s.downcase
|
1019
|
-
when "windows" # WinXP's default syn
|
1020
|
-
@tcp_header.tcp_win = 0x4000
|
1021
|
-
@tcp_header.tcp_options="MSS:1460,NOP,NOP,SACKOK"
|
1022
|
-
@tcp_header.tcp_src = rand(5000 - 1026) + 1026
|
1023
|
-
@ip_header.ip_ttl = 64
|
1024
|
-
when "linux" # Ubuntu Linux 2.6.24-19-generic default syn
|
1025
|
-
@tcp_header.tcp_win = 5840
|
1026
|
-
@tcp_header.tcp_options="MSS:1460,SACKOK,TS:#{ts_val};0,NOP,WS:7"
|
1027
|
-
@tcp_header.tcp_src = rand(61_000 - 32_000) + 32_000
|
1028
|
-
@ip_header.ip_ttl = 64
|
1029
|
-
when "freebsd" # Freebsd
|
1030
|
-
@tcp_header.tcp_win = 0xffff
|
1031
|
-
@tcp_header.tcp_options="MSS:1460,NOP,WS:3,NOP,NOP,TS:#{ts_val};#{ts_sec},SACKOK,EOL,EOL"
|
1032
|
-
@ip_header.ip_ttl = 64
|
1033
|
-
else
|
1034
|
-
@tcp_header.tcp_options="MSS:1460,NOP,NOP,SACKOK"
|
1035
|
-
end
|
1036
|
-
tcp_calc_sum
|
1037
|
-
end
|
1038
|
-
|
1039
|
-
# tcp_calc_sum() computes the TCP checksum, and is called upon intialization. It usually
|
1040
|
-
# should be called just prior to dropping packets to a file or on the wire.
|
1041
|
-
#--
|
1042
|
-
# This is /not/ delegated down to @tcp_header since we need info
|
1043
|
-
# from the IP header, too.
|
1044
|
-
#++
|
1045
|
-
def tcp_calc_sum
|
1046
|
-
checksum = (ip_src.to_i >> 16)
|
1047
|
-
checksum += (ip_src.to_i & 0xffff)
|
1048
|
-
checksum += (ip_dst.to_i >> 16)
|
1049
|
-
checksum += (ip_dst.to_i & 0xffff)
|
1050
|
-
checksum += 0x06 # TCP Protocol.
|
1051
|
-
checksum += (ip_len.to_i - ((ip_hl.to_i) * 4))
|
1052
|
-
checksum += tcp_src
|
1053
|
-
checksum += tcp_dst
|
1054
|
-
checksum += (tcp_seq.to_i >> 16)
|
1055
|
-
checksum += (tcp_seq.to_i & 0xffff)
|
1056
|
-
checksum += (tcp_ack.to_i >> 16)
|
1057
|
-
checksum += (tcp_ack.to_i & 0xffff)
|
1058
|
-
checksum += ((tcp_hlen << 12) +
|
1059
|
-
(tcp_reserved << 9) +
|
1060
|
-
(tcp_ecn.to_i << 6) +
|
1061
|
-
tcp_flags.to_i
|
1062
|
-
)
|
1063
|
-
checksum += tcp_win
|
1064
|
-
checksum += tcp_urg
|
1065
|
-
|
1066
|
-
chk_tcp_opts = (tcp_opts.to_s.size % 2 == 0 ? tcp_opts.to_s : tcp_opts.to_s + "\x00")
|
1067
|
-
chk_tcp_opts.unpack("n*").each {|x| checksum = checksum + x }
|
1068
|
-
if (ip_len - ((ip_hl + tcp_hlen) * 4)) >= 0
|
1069
|
-
real_tcp_payload = payload[0,( ip_len - ((ip_hl + tcp_hlen) * 4) )] # Can't forget those pesky FCSes!
|
1070
|
-
else
|
1071
|
-
real_tcp_payload = payload # Something's amiss here so don't bother figuring out where the real payload is.
|
1072
|
-
end
|
1073
|
-
chk_payload = (real_tcp_payload.size % 2 == 0 ? real_tcp_payload : real_tcp_payload + "\x00") # Null pad if it's odd.
|
1074
|
-
chk_payload.unpack("n*").each {|x| checksum = checksum+x }
|
1075
|
-
checksum = checksum % 0xffff
|
1076
|
-
checksum = 0xffff - checksum
|
1077
|
-
checksum == 0 ? 0xffff : checksum
|
1078
|
-
@tcp_header.tcp_sum = checksum
|
1079
|
-
end
|
1080
|
-
|
1081
|
-
# Recalculates various fields of the TCP packet.
|
1082
|
-
#
|
1083
|
-
# ==== Parameters
|
1084
|
-
#
|
1085
|
-
# :all
|
1086
|
-
# Recomputes all calculated fields.
|
1087
|
-
# :tcp_sum
|
1088
|
-
# Recomputes the TCP checksum.
|
1089
|
-
# :tcp_hlen
|
1090
|
-
# Recomputes the TCP header length. Useful after options are added.
|
1091
|
-
def tcp_recalc(arg=:all)
|
1092
|
-
case arg
|
1093
|
-
when :tcp_sum
|
1094
|
-
tcp_calc_sum
|
1095
|
-
when :tcp_hlen
|
1096
|
-
@tcp_header.tcp_recalc :tcp_hlen
|
1097
|
-
when :all
|
1098
|
-
@tcp_header.tcp_recalc :all
|
1099
|
-
tcp_calc_sum
|
1100
|
-
else
|
1101
|
-
raise ArgumentError, "No such field `#{arg}'"
|
1102
|
-
end
|
1103
|
-
end
|
1104
|
-
|
1105
|
-
# TCP packets are denoted by a "T ", followed by size,
|
1106
|
-
# source and dest information, packet flags, sequence
|
1107
|
-
# number, and IPID.
|
1108
|
-
def peek_format
|
1109
|
-
peek_data = ["T "]
|
1110
|
-
peek_data << "%-5d" % self.to_s.size
|
1111
|
-
peek_data << "%-21s" % "#{self.ip_saddr}:#{self.tcp_src}"
|
1112
|
-
peek_data << "->"
|
1113
|
-
peek_data << "%21s" % "#{self.ip_daddr}:#{self.tcp_dst}"
|
1114
|
-
flags = ' ['
|
1115
|
-
flags << self.tcp_flags_dotmap
|
1116
|
-
flags << '] '
|
1117
|
-
peek_data << flags
|
1118
|
-
peek_data << "S:"
|
1119
|
-
peek_data << "%08x" % self.tcp_seq
|
1120
|
-
peek_data << "|I:"
|
1121
|
-
peek_data << "%04x" % self.ip_id
|
1122
|
-
peek_data.join
|
1123
|
-
end
|
1124
|
-
|
1125
|
-
end
|
1126
|
-
|
1127
|
-
end
|