packetfu 1.1.10 → 1.1.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/.gitignore +3 -0
- data/.travis.yml +8 -0
- data/CONTRIBUTING.md +47 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +1 -1
- data/README.rdoc +35 -30
- data/Rakefile +4 -4
- data/bench/octets.rb +9 -9
- data/examples/100kpackets.rb +12 -12
- data/examples/ackscan.rb +16 -16
- data/examples/arp.rb +35 -35
- data/examples/arphood.rb +36 -36
- data/examples/dissect_thinger.rb +6 -6
- data/examples/new-simple-stats.rb +23 -23
- data/examples/packetfu-shell.rb +25 -25
- data/examples/simple-sniffer.rb +9 -9
- data/examples/simple-stats.rb +23 -23
- data/examples/slammer.rb +3 -3
- data/gem-public_cert.pem +21 -0
- data/lib/packetfu.rb +149 -127
- data/lib/packetfu/capture.rb +169 -169
- data/lib/packetfu/config.rb +52 -52
- data/lib/packetfu/inject.rb +56 -56
- data/lib/packetfu/packet.rb +531 -528
- data/lib/packetfu/pcap.rb +579 -579
- data/lib/packetfu/protos/arp.rb +90 -90
- data/lib/packetfu/protos/arp/header.rb +158 -158
- data/lib/packetfu/protos/arp/mixin.rb +36 -36
- data/lib/packetfu/protos/eth.rb +44 -44
- data/lib/packetfu/protos/eth/header.rb +243 -243
- data/lib/packetfu/protos/eth/mixin.rb +3 -3
- data/lib/packetfu/protos/hsrp.rb +69 -69
- data/lib/packetfu/protos/hsrp/header.rb +107 -107
- data/lib/packetfu/protos/hsrp/mixin.rb +29 -29
- data/lib/packetfu/protos/icmp.rb +71 -71
- data/lib/packetfu/protos/icmp/header.rb +82 -82
- data/lib/packetfu/protos/icmp/mixin.rb +14 -14
- data/lib/packetfu/protos/invalid.rb +49 -49
- data/lib/packetfu/protos/ip.rb +69 -69
- data/lib/packetfu/protos/ip/header.rb +291 -291
- data/lib/packetfu/protos/ip/mixin.rb +40 -40
- data/lib/packetfu/protos/ipv6.rb +50 -50
- data/lib/packetfu/protos/ipv6/header.rb +188 -188
- data/lib/packetfu/protos/ipv6/mixin.rb +29 -29
- data/lib/packetfu/protos/tcp.rb +176 -176
- data/lib/packetfu/protos/tcp/ecn.rb +35 -35
- data/lib/packetfu/protos/tcp/flags.rb +74 -74
- data/lib/packetfu/protos/tcp/header.rb +268 -268
- data/lib/packetfu/protos/tcp/hlen.rb +32 -32
- data/lib/packetfu/protos/tcp/mixin.rb +46 -46
- data/lib/packetfu/protos/tcp/option.rb +321 -321
- data/lib/packetfu/protos/tcp/options.rb +95 -95
- data/lib/packetfu/protos/tcp/reserved.rb +35 -35
- data/lib/packetfu/protos/udp.rb +159 -123
- data/lib/packetfu/protos/udp/header.rb +91 -91
- data/lib/packetfu/protos/udp/mixin.rb +3 -3
- data/lib/packetfu/structfu.rb +280 -280
- data/lib/packetfu/utils.rb +292 -225
- data/lib/packetfu/version.rb +41 -41
- data/packetfu.gemspec +14 -3
- data/spec/arp_spec.rb +191 -0
- data/spec/eth_spec.rb +148 -0
- data/spec/icmp_spec.rb +97 -0
- data/spec/ip_spec.rb +78 -0
- data/spec/ipv6_spec.rb +81 -0
- data/spec/packet_spec.rb +61 -59
- data/spec/packet_subclasses_spec.rb +9 -10
- data/spec/packetfu_spec.rb +55 -62
- data/spec/sample3.pcap +0 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/structfu_spec.rb +270 -271
- data/spec/tcp_spec.rb +76 -77
- data/spec/udp_spec.rb +32 -0
- data/spec/utils_spec.rb +95 -0
- data/test/all_tests.rb +14 -17
- data/test/func_lldp.rb +3 -3
- data/test/ptest.rb +2 -2
- data/test/test_capture.rb +45 -45
- data/test/test_eth.rb +70 -68
- data/test/test_hsrp.rb +9 -9
- data/test/test_inject.rb +18 -18
- data/test/test_invalid.rb +16 -16
- data/test/test_octets.rb +23 -21
- data/test/test_packet.rb +156 -154
- data/test/test_pcap.rb +172 -170
- data/test/test_structfu.rb +99 -97
- data/test/test_tcp.rb +322 -320
- data/test/test_udp.rb +78 -76
- metadata +108 -44
- metadata.gz.sig +2 -0
- data/spec/ethpacket_spec.rb +0 -74
- data/test/test_arp.rb +0 -135
- data/test/test_icmp.rb +0 -62
- data/test/test_ip.rb +0 -50
- data/test/test_ip6.rb +0 -68
data/lib/packetfu/inject.rb
CHANGED
@@ -1,66 +1,66 @@
|
|
1
1
|
# -*- coding: binary -*-
|
2
2
|
module PacketFu
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
# The Inject class handles injecting arrays of binary data on the wire.
|
5
|
+
#
|
6
|
+
# To inject single packets, use PacketFu::Packet.to_w() instead.
|
7
|
+
class Inject
|
8
|
+
attr_accessor :array, :stream, :show_live # Leave these public and open.
|
9
|
+
attr_reader :iface, :snaplen, :promisc, :timeout # Cant change after the init.
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
def initialize(args={})
|
12
|
+
@array = [] # Where the packet array goes.
|
13
|
+
@stream = [] # Where the stream goes.
|
14
|
+
@iface = args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo"
|
15
|
+
@snaplen = args[:snaplen] || 0xffff
|
16
|
+
@promisc = args[:promisc] || false # Sensible for some Intel wifi cards
|
17
|
+
@timeout = args[:timeout] || 1
|
18
|
+
@show_live = nil
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
21
|
+
# Takes an array, and injects them onto an interface. Note that
|
22
|
+
# complete packets (Ethernet headers on down) are expected.
|
23
|
+
#
|
24
|
+
# === Parameters
|
25
|
+
#
|
26
|
+
# :array || arr
|
27
|
+
# An array of binary data (usually packet.to_s style).
|
28
|
+
# :int || sleep
|
29
|
+
# Number of seconds to sleep between injections (in float format)
|
30
|
+
# :show_live || :live
|
31
|
+
# If true, puts data about what was injected to stdout.
|
32
|
+
#
|
33
|
+
# === Example
|
34
|
+
#
|
35
|
+
# inj = PacketFu::Inject.new
|
36
|
+
# inj.array_to_wire(:array => [pkt1, pkt2, pkt3], :sleep => 0.1)
|
37
|
+
#
|
38
|
+
def array_to_wire(args={})
|
39
|
+
pkt_array = args[:array] || args[:arr] || @array
|
40
|
+
interval = args[:int] || args[:sleep]
|
41
|
+
show_live = args[:show_live] || args[:live] || @show_live
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
43
|
+
@stream = Pcap.open_live(@iface,@snaplen,@promisc,@timeout)
|
44
|
+
pkt_count = 0
|
45
|
+
pkt_array.each do |pkt|
|
46
|
+
@stream.inject(pkt)
|
47
|
+
sleep interval if interval
|
48
|
+
pkt_count +=1
|
49
|
+
puts "Sent Packet \##{pkt_count} (#{pkt.size})" if show_live
|
50
|
+
end
|
51
|
+
# Return # of packets sent, array size, and array total size
|
52
|
+
[pkt_count, pkt_array.size, pkt_array.join.size]
|
53
|
+
end
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
55
|
+
# Equivalent to array_to_wire
|
56
|
+
def a2w(args={})
|
57
|
+
array_to_wire(args)
|
58
|
+
end
|
59
59
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
# Equivalent to array_to_wire
|
61
|
+
def inject(args={})
|
62
|
+
array_to_wire(args)
|
63
|
+
end
|
64
64
|
|
65
|
-
|
65
|
+
end
|
66
66
|
end
|
data/lib/packetfu/packet.rb
CHANGED
@@ -1,534 +1,537 @@
|
|
1
1
|
# -*- coding: binary -*-
|
2
2
|
module PacketFu
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
4
|
+
# Packet is the parent class of EthPacket, IPPacket, UDPPacket, TCPPacket, and all
|
5
|
+
# other packets. It acts as both a singleton class, so things like
|
6
|
+
# Packet.parse can happen, and as an abstract class to provide
|
7
|
+
# subclasses some structure.
|
8
|
+
class Packet
|
9
|
+
|
10
|
+
attr_reader :flavor # Packet Headers are responsible for their own specific flavor methods.
|
11
|
+
attr_accessor :headers # All packets have a header collection, useful for determining protocol trees.
|
12
|
+
attr_accessor :iface # Default inferface to send packets to
|
13
|
+
attr_accessor :inspect_style # Default is :dissect, can also be :hex or :default
|
14
|
+
|
15
|
+
# Register subclasses in PacketFu.packet_class to do all kinds of neat things
|
16
|
+
# that obviates those long if/else trees for parsing. It's pretty sweet.
|
17
|
+
def self.inherited(subclass)
|
18
|
+
PacketFu.add_packet_class(subclass)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Force strings into binary.
|
22
|
+
def self.force_binary(str)
|
23
|
+
str.force_encoding Encoding::BINARY if str.respond_to? :force_encoding
|
24
|
+
end
|
25
|
+
|
26
|
+
# Parse() creates the correct packet type based on the data, and returns the apporpiate
|
27
|
+
# Packet subclass object.
|
28
|
+
#
|
29
|
+
# There is an assumption here that all incoming packets are either EthPacket
|
30
|
+
# or InvalidPacket types. This will be addressed pretty soon.
|
31
|
+
#
|
32
|
+
# If application-layer parsing is /not/ desired, that should be indicated explicitly
|
33
|
+
# with an argument of :parse_app => false. Otherwise, app-layer parsing will happen.
|
34
|
+
#
|
35
|
+
# It is no longer neccisary to manually add packet types here.
|
36
|
+
def self.parse(packet=nil,args={})
|
37
|
+
parse_app = true if(args[:parse_app].nil? or args[:parse_app])
|
38
|
+
force_binary(packet)
|
39
|
+
if parse_app
|
40
|
+
classes = PacketFu.packet_classes_by_layer
|
41
|
+
else
|
42
|
+
classes = PacketFu.packet_classes_by_layer_without_application
|
43
|
+
end
|
44
|
+
p = classes.detect { |pclass| pclass.can_parse?(packet) }.new
|
45
|
+
parsed_packet = p.read(packet,args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def handle_is_identity(ptype)
|
49
|
+
idx = PacketFu.packet_prefixes.index(ptype.to_s.downcase)
|
50
|
+
if idx
|
51
|
+
self.kind_of? PacketFu.packet_classes[idx]
|
52
|
+
else
|
53
|
+
raise NoMethodError, "Undefined method `is_#{ptype}?' for #{self.class}."
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get the binary string of the entire packet.
|
58
|
+
def to_s
|
59
|
+
@headers[0].to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
# In the event of no proper decoding, at least send it to the inner-most header.
|
63
|
+
def write(io)
|
64
|
+
@headers[0].write(io)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Get the outermost payload (body) of the packet; this is why all packet headers
|
68
|
+
# should have a body type.
|
69
|
+
def payload
|
70
|
+
@headers.last.body
|
71
|
+
end
|
72
|
+
|
73
|
+
# Set the outermost payload (body) of the packet.
|
74
|
+
def payload=(args)
|
75
|
+
@headers.last.body=(args)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Converts a packet to libpcap format. Bit of a hack?
|
79
|
+
def to_pcap(args={})
|
80
|
+
p = PcapPacket.new(:endian => args[:endian],
|
81
|
+
:timestamp => Timestamp.new.to_s,
|
82
|
+
:incl_len => self.to_s.size,
|
83
|
+
:orig_len => self.to_s.size,
|
84
|
+
:data => self)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Put the entire packet into a libpcap file. XXX: this is a
|
88
|
+
# hack for now just to confirm that packets are getting created
|
89
|
+
# correctly. Now with append! XXX: Document this!
|
90
|
+
def to_f(filename=nil,mode='w')
|
91
|
+
filename ||= 'out.pcap'
|
92
|
+
mode = mode.to_s[0,1] + "b"
|
93
|
+
raise ArgumentError, "Unknown mode: #{mode.to_s}" unless mode =~ /^[wa]/
|
94
|
+
if(mode == 'w' || !(File.exists?(filename)))
|
95
|
+
data = [PcapHeader.new, self.to_pcap].map {|x| x.to_s}.join
|
96
|
+
else
|
97
|
+
data = self.to_pcap
|
98
|
+
end
|
99
|
+
File.open(filename, mode) {|f| f.write data}
|
100
|
+
return [filename, 1, data.size]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Put the entire packet on the wire by creating a temporary PacketFu::Inject object.
|
104
|
+
# TODO: Do something with auto-checksumming?
|
105
|
+
def to_w(iface=nil)
|
106
|
+
iface = (iface || self.iface || PacketFu::Config.new.config[:iface]).to_s
|
107
|
+
inj = PacketFu::Inject.new(:iface => iface)
|
108
|
+
inj.array = [@headers[0].to_s]
|
109
|
+
inj.inject
|
110
|
+
end
|
111
|
+
|
112
|
+
# Recalculates all the calcuated fields for all headers in the packet.
|
113
|
+
# This is important since read() wipes out all the calculated fields
|
114
|
+
# such as length and checksum and what all.
|
115
|
+
def recalc(arg=:all)
|
116
|
+
case arg
|
117
|
+
when :ip
|
118
|
+
ip_recalc(:all)
|
119
|
+
when :ipv6
|
120
|
+
ipv6_recalc(:all)
|
121
|
+
when :icmp
|
122
|
+
icmp_recalc(:all)
|
123
|
+
when :udp
|
124
|
+
udp_recalc(:all)
|
125
|
+
when :tcp
|
126
|
+
tcp_recalc(:all)
|
127
|
+
when :all
|
128
|
+
ip_recalc(:all) if @ip_header
|
129
|
+
ipv6_recalc(:all) if @ipv6_header
|
130
|
+
icmp_recalc(:all) if @icmp_header
|
131
|
+
udp_recalc(:all) if @udp_header
|
132
|
+
tcp_recalc(:all) if @tcp_header
|
133
|
+
else
|
134
|
+
raise ArgumentError, "Recalculating `#{arg}' unsupported. Try :all"
|
135
|
+
end
|
136
|
+
@headers[0]
|
137
|
+
end
|
138
|
+
|
139
|
+
# Read() takes (and trusts) the io input and shoves it all into a well-formed Packet.
|
140
|
+
# Note that read is a destructive process, so any existing data will be lost.
|
141
|
+
#
|
142
|
+
# A note on the :strip => true argument: If :strip is set, defined lengths of data will
|
143
|
+
# be believed, and any trailers (such as frame check sequences) will be chopped off. This
|
144
|
+
# helps to ensure well-formed packets, at the cost of losing perhaps important FCS data.
|
145
|
+
#
|
146
|
+
# If :strip is false, header lengths are /not/ believed, and all data will be piped in.
|
147
|
+
# When capturing from the wire, this is usually fine, but recalculating the length before
|
148
|
+
# saving or re-transmitting will absolutely change the data payload; FCS data will become
|
149
|
+
# part of the TCP data as far as tcp_len is concerned. Some effort has been made to preserve
|
150
|
+
# the "real" payload for the purposes of checksums, but currently, it's impossible to seperate
|
151
|
+
# new payload data from old trailers, so things like pkt.payload += "some data" will not work
|
152
|
+
# correctly.
|
153
|
+
#
|
154
|
+
# So, to summarize; if you intend to alter the data, use :strip. If you don't, don't. Also,
|
155
|
+
# this is a horrid hack. Stripping is useful (and fun!), but the default behavior really
|
156
|
+
# should be to create payloads correctly, and /not/ treat extra FCS data as a payload.
|
157
|
+
#
|
158
|
+
# Finally, packet subclasses should take two arguments: the string that is the data
|
159
|
+
# to be transmuted into a packet, as well as args. This superclass method is merely
|
160
|
+
# concerned with handling args common to many packet formats (namely, fixing packets
|
161
|
+
# on the fly)
|
162
|
+
def read(args={})
|
163
|
+
if args[:fix] || args[:recalc]
|
164
|
+
ip_recalc(:ip_sum) if self.is_ip?
|
165
|
+
recalc(:tcp) if self.is_tcp?
|
166
|
+
recalc(:udp) if self.is_udp?
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Packets are bundles of lots of objects, so copying them
|
171
|
+
# is a little complicated -- a dup of a packet is actually
|
172
|
+
# full of pass-by-reference stuff in the @headers, so
|
173
|
+
# if you change one, you're changing all this copies, too.
|
174
|
+
#
|
175
|
+
# Normally, this doesn't seem to be a big deal, and it's
|
176
|
+
# a pretty decent performance tradeoff. But, if you're going
|
177
|
+
# to be creating a template packet to base a bunch of slightly
|
178
|
+
# different ones off of (like a fuzzer might), you'll want
|
179
|
+
# to use clone()
|
180
|
+
def clone
|
181
|
+
Packet.parse(self.to_s)
|
182
|
+
end
|
183
|
+
|
184
|
+
# If two packets are represented as the same binary string, and
|
185
|
+
# they're both actually PacketFu packets of the same sort, they're equal.
|
186
|
+
#
|
187
|
+
# The intuitive result is that a packet of a higher layer (like DNSPacket)
|
188
|
+
# can be equal to a packet of a lower level (like UDPPacket) as long as
|
189
|
+
# the bytes are equal (this can come up if a transport-layer packet has
|
190
|
+
# a hand-crafted payload that is identical to what would have been created
|
191
|
+
# by using an application layer packet)
|
192
|
+
def ==(other)
|
193
|
+
return false unless other.kind_of? self.class
|
194
|
+
return false unless other.respond_to? :to_s
|
195
|
+
self.to_s == other.to_s
|
196
|
+
end
|
197
|
+
|
198
|
+
# Peek provides summary data on packet contents.
|
199
|
+
#
|
200
|
+
# Each packet type should provide a peek_format.
|
201
|
+
def peek(args={})
|
202
|
+
idx = @headers.reverse.map {|h| h.respond_to? peek_format}.index(true)
|
203
|
+
if idx
|
204
|
+
@headers.reverse[idx].peek_format
|
205
|
+
else
|
206
|
+
peek_format
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# The peek_format is used to display a single line
|
211
|
+
# of packet data useful for eyeballing. It should not exceed
|
212
|
+
# 80 characters. The Packet superclass defines an example
|
213
|
+
# peek_format, but it should hardly ever be triggered, since
|
214
|
+
# peek traverses the @header list in reverse to find a suitable
|
215
|
+
# format.
|
216
|
+
#
|
217
|
+
# === Format
|
218
|
+
#
|
219
|
+
# * A one or two character protocol initial. It should be unique
|
220
|
+
# * The packet size
|
221
|
+
# * Useful data in a human-usable form.
|
222
|
+
#
|
223
|
+
# Ideally, related peek_formats will all line up with each other
|
224
|
+
# when printed to the screen.
|
225
|
+
#
|
226
|
+
# === Example
|
227
|
+
#
|
228
|
+
# tcp_packet.peek
|
229
|
+
# #=> "T 1054 10.10.10.105:55000 -> 192.168.145.105:80 [......] S:adc7155b|I:8dd0"
|
230
|
+
# tcp_packet.peek.size
|
231
|
+
# #=> 79
|
232
|
+
#
|
233
|
+
def peek_format
|
234
|
+
peek_data = ["? "]
|
235
|
+
peek_data << "%-5d" % self.to_s.size
|
236
|
+
peek_data << "%68s" % self.to_s[0,34].unpack("H*")[0]
|
237
|
+
peek_data.join
|
238
|
+
end
|
239
|
+
|
240
|
+
# Defines the layer this packet type lives at, based on the number of headers it
|
241
|
+
# requires. Note that this has little to do with the OSI model, since TCP/IP
|
242
|
+
# doesn't really have Session and Presentation layers.
|
243
|
+
#
|
244
|
+
# Ethernet and the like are layer 1, IP, IPv6, and ARP are layer 2,
|
245
|
+
# TCP, UDP, and other transport protocols are layer 3, and application
|
246
|
+
# protocols are at layer 4 or higher. InvalidPackets have an arbitrary
|
247
|
+
# layer 0 to distinguish them.
|
248
|
+
#
|
249
|
+
# Because these don't change much, it's cheaper just to case through them,
|
250
|
+
# and only resort to counting headers if we don't have a match -- this
|
251
|
+
# makes adding protocols somewhat easier, but of course you can just
|
252
|
+
# override this method over there, too. This is merely optimized
|
253
|
+
# for the most likely protocols you see on the Internet.
|
254
|
+
def self.layer
|
255
|
+
case self.name # Lol ran into case's fancy treatment of classes
|
256
|
+
when /InvalidPacket$/; 0
|
257
|
+
when /EthPacket$/; 1
|
258
|
+
when /IPPacket$/, /ARPPacket$/, /LLDPPacket$/, /IPv6Packet$/; 2
|
259
|
+
when /TCPPacket$/, /UDPPacket$/, /ICMPPacket$/; 3
|
260
|
+
when /HSRPPacket$/; 4
|
261
|
+
else; self.new.headers.size
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def layer
|
266
|
+
self.class.layer
|
267
|
+
end
|
268
|
+
|
269
|
+
def self.layer_symbol
|
270
|
+
case self.layer
|
271
|
+
when 0; :invalid
|
272
|
+
when 1; :link
|
273
|
+
when 2; :internet
|
274
|
+
when 3; :transport
|
275
|
+
else; :application
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def layer_symbol
|
280
|
+
self.class.layer_symbol
|
281
|
+
end
|
282
|
+
|
283
|
+
# Packet subclasses must override this, since the Packet superclass
|
284
|
+
# can't actually parse anything.
|
285
|
+
def self.can_parse?(str)
|
286
|
+
false
|
287
|
+
end
|
288
|
+
|
289
|
+
# Hexify provides a neatly-formatted dump of binary data, familar to hex readers.
|
290
|
+
def hexify(str)
|
291
|
+
str.force_encoding(Encoding::BINARY) if str.respond_to? :force_encoding
|
292
|
+
hexascii_lines = str.to_s.unpack("H*")[0].scan(/.{1,32}/)
|
293
|
+
regex = Regexp.new('[\x00-\x1f\x7f-\xff]', nil, 'n')
|
294
|
+
chars = str.to_s.gsub(regex,'.')
|
295
|
+
chars_lines = chars.scan(/.{1,16}/)
|
296
|
+
ret = []
|
297
|
+
hexascii_lines.size.times {|i| ret << "%-48s %s" % [hexascii_lines[i].gsub(/(.{2})/,"\\1 "),chars_lines[i]]}
|
298
|
+
ret.join("\n")
|
299
|
+
end
|
300
|
+
|
301
|
+
# If @inspect_style is :default (or :ugly), the inspect output is the usual
|
302
|
+
# inspect.
|
303
|
+
#
|
304
|
+
# If @inspect_style is :hex (or :pretty), the inspect output is
|
305
|
+
# a much more compact hexdump-style, with a shortened set of packet header
|
306
|
+
# names at the top.
|
307
|
+
#
|
308
|
+
# If @inspect_style is :dissect (or :verbose), the inspect output is the
|
309
|
+
# longer, but more readable, dissection of the packet. This is the default.
|
310
|
+
#
|
311
|
+
# TODO: Have an option for colors. Everyone loves colorized irb output.
|
312
|
+
def inspect_hex(arg=0)
|
313
|
+
case arg
|
314
|
+
when :layers
|
315
|
+
ret = []
|
316
|
+
@headers.size.times do |i|
|
317
|
+
ret << hexify(@headers[i])
|
318
|
+
end
|
319
|
+
ret
|
320
|
+
when (0..9)
|
321
|
+
if @headers[arg]
|
322
|
+
hexify(@headers[arg])
|
323
|
+
else
|
324
|
+
nil
|
325
|
+
end
|
326
|
+
when :all
|
327
|
+
inspect_hex(0)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def dissection_table
|
332
|
+
table = []
|
333
|
+
@headers.each_with_index do |header,table_idx|
|
334
|
+
proto = header.class.name.sub(/^.*::/,"")
|
335
|
+
table << [proto,[]]
|
336
|
+
header.class.members.each do |elem|
|
337
|
+
elem_sym = elem.to_sym # to_sym needed for 1.8
|
338
|
+
next if elem_sym == :body
|
339
|
+
elem_type_value = []
|
340
|
+
elem_type_value[0] = elem
|
341
|
+
readable_element = "#{elem}_readable"
|
342
|
+
if header.respond_to? readable_element
|
343
|
+
elem_type_value[1] = header.send(readable_element)
|
344
|
+
else
|
345
|
+
elem_type_value[1] = header.send(elem)
|
346
|
+
end
|
347
|
+
elem_type_value[2] = header[elem.to_sym].class.name
|
348
|
+
table[table_idx][1] << elem_type_value
|
349
|
+
end
|
350
|
+
end
|
351
|
+
table
|
352
|
+
if @headers.last.members.map {|x| x.to_sym }.include? :body
|
353
|
+
body_part = [:body, self.payload, @headers.last.body.class.name]
|
354
|
+
end
|
355
|
+
table << body_part
|
356
|
+
end
|
357
|
+
|
358
|
+
# Renders the dissection_table suitable for screen printing. Can take
|
359
|
+
# one or two arguments. If just the one, only that layer will be displayed
|
360
|
+
# take either a range or a number -- if a range, only protos within
|
361
|
+
# that range will be rendered. If an integer, only that proto
|
362
|
+
# will be rendered.
|
363
|
+
def dissect
|
364
|
+
dtable = self.dissection_table
|
365
|
+
hex_body = nil
|
366
|
+
if dtable.last.kind_of?(Array) and dtable.last.first == :body
|
367
|
+
body = dtable.pop
|
368
|
+
hex_body = hexify(body[1])
|
369
|
+
end
|
370
|
+
elem_widths = [0,0,0]
|
371
|
+
dtable.each do |proto_table|
|
372
|
+
proto_table[1].each do |elems|
|
373
|
+
elems.each_with_index do |e,i|
|
374
|
+
width = e.size
|
375
|
+
elem_widths[i] = width if width > elem_widths[i]
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
total_width = elem_widths.inject(0) {|sum,x| sum+x}
|
380
|
+
table = ""
|
381
|
+
dtable.each do |proto|
|
382
|
+
table << "--"
|
383
|
+
table << proto[0]
|
384
|
+
if total_width > proto[0].size
|
385
|
+
table << ("-" * (total_width - proto[0].size + 2))
|
386
|
+
else
|
387
|
+
table << ("-" * (total_width + 2))
|
388
|
+
end
|
389
|
+
table << "\n"
|
390
|
+
proto[1].each do |elems|
|
391
|
+
table << " "
|
392
|
+
elems_table = []
|
393
|
+
(0..2).each do |i|
|
394
|
+
elems_table << ("%-#{elem_widths[i]}s" % elems[i])
|
395
|
+
end
|
396
|
+
table << elems_table.join("\s")
|
397
|
+
table << "\n"
|
398
|
+
end
|
399
|
+
end
|
400
|
+
if hex_body && !hex_body.empty?
|
401
|
+
table << "-" * 66
|
402
|
+
table << "\n"
|
403
|
+
table << "00-01-02-03-04-05-06-07-08-09-0a-0b-0c-0d-0e-0f---0123456789abcdef\n"
|
404
|
+
table << "-" * 66
|
405
|
+
table << "\n"
|
406
|
+
table << hex_body
|
407
|
+
end
|
408
|
+
table
|
409
|
+
end
|
410
|
+
|
411
|
+
alias :orig_kind_of? :kind_of?
|
412
|
+
|
413
|
+
def kind_of?(klass)
|
414
|
+
return true if orig_kind_of? klass
|
415
|
+
packet_types = proto.map {|p| PacketFu.const_get("#{p}Packet")}
|
416
|
+
match = false
|
417
|
+
packet_types.each do |p|
|
418
|
+
if p.ancestors.include? klass
|
419
|
+
match = true
|
420
|
+
break
|
421
|
+
end
|
422
|
+
end
|
423
|
+
return match
|
424
|
+
end
|
425
|
+
|
426
|
+
# For packets, inspect is overloaded as inspect_hex(0).
|
427
|
+
# Not sure if this is a great idea yet, but it sure makes
|
428
|
+
# the irb output more sane.
|
429
|
+
#
|
430
|
+
# If you hate this, you can run PacketFu.toggle_inspect to return
|
431
|
+
# to the typical (and often unreadable) Object#inspect format.
|
432
|
+
def inspect
|
433
|
+
case @inspect_style
|
434
|
+
when :dissect
|
435
|
+
self.dissect
|
436
|
+
when :hex
|
437
|
+
self.proto.join("|") + "\n" + self.inspect_hex
|
438
|
+
else
|
439
|
+
super
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
# Returns the size of the packet (as a binary string)
|
444
|
+
def size
|
445
|
+
self.to_s.size
|
446
|
+
end
|
447
|
+
|
448
|
+
# Returns an array of protocols contained in this packet. For example:
|
449
|
+
#
|
450
|
+
# t = PacketFu::TCPPacket.new
|
451
|
+
# => 00 1a c5 00 00 00 00 1a c5 00 00 00 08 00 45 00 ..............E.
|
452
|
+
# 00 28 3c ab 00 00 ff 06 7f 25 00 00 00 00 00 00 .(<......%......
|
453
|
+
# 00 00 93 5e 00 00 ad 4f e4 a4 00 00 00 00 50 00 ...^...O......P.
|
454
|
+
# 40 00 4a 92 00 00 @.J...
|
455
|
+
# t.proto
|
456
|
+
# => ["Eth", "IP", "TCP"]
|
457
|
+
#
|
458
|
+
def proto
|
459
|
+
type_array = []
|
460
|
+
self.headers.each {|header| type_array << header.class.to_s.split('::').last.gsub(/Header$/,'')}
|
461
|
+
type_array
|
462
|
+
end
|
463
|
+
|
464
|
+
alias_method :protocol, :proto
|
465
|
+
alias_method :length, :size
|
466
|
+
|
467
|
+
# the Packet class should not be instantiated directly, since it's an
|
468
|
+
# abstract class that real packet types inherit from. Sadly, this
|
469
|
+
# makes the Packet class more difficult to test directly.
|
470
|
+
def initialize(args={})
|
471
|
+
if self.class.name =~ /(::|^)PacketFu::Packet$/
|
472
|
+
raise NoMethodError, "method `new' called for abstract class #{self.class.name}"
|
473
|
+
end
|
474
|
+
@inspect_style = args[:inspect_style] || PacketFu.inspect_style || :dissect
|
475
|
+
if args[:config]
|
476
|
+
args[:config].each_pair do |k,v|
|
477
|
+
case k
|
478
|
+
when :eth_daddr; @eth_header.eth_daddr=v if @eth_header
|
479
|
+
when :eth_saddr; @eth_header.eth_saddr=v if @eth_header
|
480
|
+
when :ip_saddr; @ip_header.ip_saddr=v if @ip_header
|
481
|
+
when :iface; @iface = v
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
# Delegate to PacketFu's inspect_style, since the
|
488
|
+
# class variable name is the same. Yay for namespace
|
489
|
+
# pollution!
|
490
|
+
def inspect_style=()
|
491
|
+
PacketFu.inspect_style(arg)
|
492
|
+
end
|
493
|
+
|
494
|
+
#method_missing() delegates protocol-specific field actions to the apporpraite
|
495
|
+
#class variable (which contains the associated packet type)
|
496
|
+
#This register-of-protocols style switch will work for the
|
497
|
+
#forseeable future (there aren't /that/ many packet types), and it's a handy
|
498
|
+
#way to know at a glance what packet types are supported.
|
499
|
+
def method_missing(sym, *args, &block)
|
500
|
+
case sym.to_s
|
501
|
+
when /^is_([a-zA-Z0-9]+)\?/
|
502
|
+
ptype = $1
|
503
|
+
if PacketFu.packet_prefixes.index(ptype)
|
504
|
+
self.send(:handle_is_identity, $1)
|
505
|
+
else
|
506
|
+
super
|
507
|
+
end
|
508
|
+
when /^([a-zA-Z0-9]+)_.+/
|
509
|
+
ptype = $1
|
510
|
+
if PacketFu.packet_prefixes.index(ptype)
|
511
|
+
self.instance_variable_get("@#{ptype}_header").send(sym,*args, &block)
|
512
|
+
else
|
513
|
+
super
|
514
|
+
end
|
515
|
+
else
|
516
|
+
super
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
def respond_to?(sym, include_private = false)
|
521
|
+
if sym.to_s =~ /^(invalid|eth|arp|ip|icmp|udp|hsrp|tcp|ipv6)_/
|
522
|
+
self.instance_variable_get("@#{$1}_header").respond_to? sym
|
523
|
+
elsif sym.to_s =~ /^is_([a-zA-Z0-9]+)\?/
|
524
|
+
if PacketFu.packet_prefixes.index($1)
|
525
|
+
true
|
526
|
+
else
|
527
|
+
super
|
528
|
+
end
|
529
|
+
else
|
530
|
+
super
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
end # class Packet
|
532
535
|
end
|
533
536
|
|
534
537
|
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|