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