packetgen 2.1.2 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 59b1fc19fea5c7400e6d70e01a83ca68351e646b
4
- data.tar.gz: b9ef7b691f66d23d763e7cf03f98aab5314924b5
3
+ metadata.gz: e306dbee54d246dd2044db6610ba27e3fb539215
4
+ data.tar.gz: 1103a3a6429fb2b4c8391750cdefa8c7d87ac323
5
5
  SHA512:
6
- metadata.gz: bddf2bf621ae0ddd6e30d4f1ee25f24b575d4b42b994203baa135cc58d0fac3ecce8c1549966faa381a8049c0585cd8517dc358c0e2399dc0afab1778121ef77
7
- data.tar.gz: a02f287f900e5f7d21e845fd26c86b146375a87babc61252f4c73cf63c306ff403f0fef8220594d4b8e0d17a9f5f71bb6f2dbef7e61e49c1cc1472aed2180d63
6
+ metadata.gz: 592a69c18ea009c84374aa79125134ee136f0ed9050aefb8610ff484447f9cddc66685fcc3449733d2f6a1160de403d51a4465689029090815db45cdf292e12f
7
+ data.tar.gz: cf57d3f41ea4eaa30964086ec2a1127a0f2aae14a291b2fc7476819484aaa0a14a5c0681bda1f68ad7ba4aa84edc6196cb7684054ea94c22c91a5626eb2ac533
data/.gitignore CHANGED
@@ -9,4 +9,4 @@
9
9
  /tmp/
10
10
  /vendor/
11
11
  *~
12
-
12
+ tags
data/README.md CHANGED
@@ -41,6 +41,11 @@ PacketGen.gen('Eth', src: '00:00:00:00:00:01', dst: '00:00:00:00:00:02').to_w
41
41
  PacketGen.gen('IP', src: '192.168.1.1', dst: '192.168.1.2').to_w
42
42
  # send forged IP packet over Ethernet
43
43
  PacketGen.gen('Eth', src: '00:00:00:00:00:01', dst: '00:00:00:00:00:02').add('IP').to_w('eth1')
44
+ # send a IEEE 802.11 frame
45
+ PacketGen.gen('RadioTap').
46
+ add('Dot11::Management', mac1: client, mac2: bssid, mac3: bssid).
47
+ add('Dot11::DeAuth', reason: 7).
48
+ to_w('wlan0')
44
49
  ```
45
50
 
46
51
  ### Parse packets from binary data
@@ -148,9 +153,11 @@ classes. A special `config` object gives local network configuration:
148
153
 
149
154
  If `pry` gem is installed, it is used as backend for `pgconsole`, else IRB is used.
150
155
 
151
- ## API documentation
156
+ ## See also
152
157
 
153
- see http://www.rubydoc.info/gems/packetgen
158
+ Wiki: https://github.com/sdaubert/packetgen/wiki
159
+
160
+ API documentation: http://www.rubydoc.info/gems/packetgen
154
161
 
155
162
  ## Pull requests?
156
163
 
@@ -165,4 +172,4 @@ Copyright © 2016 Sylvain Daubert
165
172
  All original code maintains its copyright from its original authors and licensing.
166
173
 
167
174
  This is mainly for PcapNG (originally copied from [PacketFu](https://github.com/packetfu/packetfu),
168
- but i am the original author).
175
+ but i am the original author.
data/bin/pgconsole CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'packetgen'
4
4
  require 'packetgen/config'
5
+ require 'packetgen/utils'
5
6
 
6
7
  use_pry = false
7
8
  begin
@@ -13,28 +14,25 @@ rescue LoadError
13
14
  require 'irb/completion'
14
15
  end
15
16
 
16
- include PacketGen
17
+ class PgConsole
18
+ include PacketGen
19
+ include Utils
17
20
 
18
- def parse(binary_str, first_header: nil)
19
- Packet.parse binary_str, first_header: first_header
20
- end
21
-
22
- def capture(options={})
23
- Packet.capture(options) { |packet| yield packet if block_given? }
24
- end
25
-
26
- def read(filename)
27
- Packet.read filename
28
- end
29
-
30
- def write(filename, packets)
31
- Packet.write filename, packets
32
- end
21
+ def config
22
+ PacketGen::Config.instance
23
+ end
33
24
 
34
- @config = PacketGen::Config.new
25
+ PacketGen.singleton_methods.each do |m|
26
+ define_method m, PacketGen.method(m).to_proc
27
+ end
35
28
 
36
- def config
37
- @config
29
+ Utils.singleton_methods.each do |m|
30
+ define_method m, Utils.method(m).to_proc
31
+ end
32
+
33
+ def get_binding
34
+ binding
35
+ end
38
36
  end
39
37
 
40
38
  def start_message(use_pry)
@@ -47,21 +45,21 @@ if use_pry
47
45
  Pry.config.prompt = [
48
46
  proc { |target_self, nest_level, pry|
49
47
  "#{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}> "
50
- },
51
- proc { |target_self, nest_level, pry|
52
- "#{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}* "
48
+ },
49
+ proc { |target_self, nest_level, pry|
50
+ "#{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}* "
53
51
  }
54
52
  ]
55
53
  Pry.config.prompt_name = 'pg'
56
- Pry.start
54
+ PgConsole.new.get_binding.pry
57
55
  else
58
56
  IRB.setup nil
59
- irb = IRB::Irb.new(IRB::WorkSpace.new(binding))
57
+ irb = IRB::Irb.new(IRB::WorkSpace.new(PgConsole.new.get_binding))
60
58
  IRB.conf[:MAIN_CONTEXT] = irb.context
61
59
  irb.context.auto_indent_mode = true
62
- irb.context.prompt_i = "pg(%m)>"
63
- irb.context.prompt_s = "pg(%m)*"
64
- irb.context.prompt_c = "pg(%m)*"
60
+ irb.context.prompt_i = "pg> "
61
+ irb.context.prompt_s = "pg* "
62
+ irb.context.prompt_c = "pg* "
65
63
 
66
64
  trap("SIGINT") do
67
65
  irb.signal_handle
@@ -75,4 +73,3 @@ else
75
73
  IRB.irb_at_exit
76
74
  end
77
75
  end
78
-
@@ -3,52 +3,68 @@
3
3
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
4
  # This program is published under MIT license.
5
5
 
6
- require 'network_interface'
7
6
  require 'socket'
7
+ require 'singleton'
8
+ require 'network_interface'
8
9
 
9
10
  module PacketGen
10
11
 
11
12
  # Config class to provide +config+ object to pgconsole
12
13
  # @author Sylvain Daubert
13
14
  # @author Kent 'picat' Gruber
15
+ # @since 2.1.3 Config is singleton
14
16
  class Config
17
+ include Singleton
15
18
 
16
19
  # Default network interface
17
20
  # @return [String]
18
- attr_reader :iface
19
- # MAC address of default interface
20
- # @return [String]
21
- attr_reader :hwaddr
22
- # IP address of default interface
23
- # @return [String]
24
- attr_reader :ipaddr
25
- # IPv6 address of default interface
26
- # @return [String]
27
- attr_reader :ip6addr
21
+ attr_reader :default_iface
22
+
23
+ def initialize
24
+ begin
25
+ default_iface = Pcap.lookupdev
26
+ rescue PCAPRUB::BindingError
27
+ default_iface = NetworkInterface.interfaces.select { |i| i =~ /lo/ }.first
28
+ end
29
+ @default_iface = default_iface
30
+ @hwaddr = {}
31
+ @ipaddr = {}
32
+ @ip6addr = {}
28
33
 
29
- # Create a configuration object. If +iface+ is not set,
30
- # attempt to find it automatically or default to the
31
- # first available loopback interface.
32
- # @param [String,nil] iface
33
- def initialize(iface=nil)
34
- if iface.nil?
35
- begin
36
- iface = Pcap.lookupdev
37
- rescue PCAPRUB::BindingError
38
- iface = NetworkInterface.interfaces.select { |iface| iface =~ /lo/ }.first
34
+ NetworkInterface.interfaces.each do |iface|
35
+ addresses = NetworkInterface.addresses(iface)
36
+ @hwaddr[iface] = case RbConfig::CONFIG['target_os']
37
+ when /darwin/
38
+ addresses[Socket::AF_LINK][0]['addr'] if addresses[Socket::AF_LINK]
39
+ else
40
+ addresses[Socket::AF_PACKET][0]['addr'] if addresses[Socket::AF_PACKET]
41
+ end
42
+ @ipaddr[iface] = addresses[Socket::AF_INET][0]['addr'] if addresses[Socket::AF_INET]
43
+ if addresses[Socket::AF_INET6]
44
+ @ip6addr[iface] = addresses[Socket::AF_INET6].map { |hsh| hsh['addr'] }
39
45
  end
40
46
  end
41
- @iface = iface
47
+ end
42
48
 
43
- addresses = NetworkInterface.addresses(iface)
44
- @hwaddr = case RbConfig::CONFIG['target_os']
45
- when /darwin/
46
- addresses[Socket::AF_LINK][0]['addr'] if addresses[Socket::AF_LINK]
47
- else
48
- addresses[Socket::AF_PACKET][0]['addr'] if addresses[Socket::AF_PACKET]
49
- end
50
- @ipaddr = addresses[Socket::AF_INET][0]['addr'] if addresses[Socket::AF_INET]
51
- @ip6addr = addresses[Socket::AF_INET6][0]['addr'] if addresses[Socket::AF_INET6]
49
+ # Get MAC address for given network interface
50
+ # @param [String,nil] iface network interface. Il +nil+, use default one.
51
+ # @return [String]
52
+ def hwaddr(iface=nil)
53
+ @hwaddr[iface || @default_iface]
54
+ end
55
+
56
+ # Get IP address for given network interface
57
+ # @param [String,nil] iface network interface. Il +nil+, use default one.
58
+ # @return [String]
59
+ def ipaddr(iface=nil)
60
+ @ipaddr[iface || @default_iface]
61
+ end
62
+
63
+ # Get IPv6 addresses for given network interface
64
+ # @param [String,nil] iface network interface. Il +nil+, use default one.
65
+ # @return [Array<String>]
66
+ def ip6addr(iface=nil)
67
+ @ip6addr[iface || @default_iface]
52
68
  end
53
69
  end
54
70
  end
@@ -48,7 +48,7 @@ module PacketGen
48
48
  # @!attribute op
49
49
  # 16-bit operation code
50
50
  # # @return [Integer]
51
- define_field :op, Types::Int16, default: 1
51
+ define_field :op, Types::Int16Enum, enum: { 'request' => 1, 'reply' => 2 }
52
52
  # @!attribute sha
53
53
  # source hardware address
54
54
  # @return [Eth::MacAddr]
@@ -119,4 +119,3 @@ module PacketGen
119
119
  Dot1q.bind_header ARP, ethertype: 0x806
120
120
  end
121
121
  end
122
-
@@ -8,6 +8,8 @@ module PacketGen
8
8
  module Header
9
9
 
10
10
  # @abstract Base class for ASN.1 header types.
11
+ # This class implement minimal {Base} API to mimic a {Base} object.
12
+ #
11
13
  # Subclasses may define magic methods:
12
14
  # * {#parse?}.
13
15
  # @author Sylvain Daubert
@@ -56,6 +58,8 @@ module PacketGen
56
58
  @method_name = protocol_name.downcase.sub(/::/, '_')
57
59
  end
58
60
 
61
+ # Called by {Packet#parse} when guessing first header to check if header
62
+ # is correct
59
63
  # @return [true]
60
64
  def parse?
61
65
  true
@@ -6,19 +6,6 @@ module PacketGen
6
6
  # @author Sylvain Daubert
7
7
  class Question < Base
8
8
 
9
- # @!attribute name
10
- # Question domain name
11
- # @return [String]
12
- define_field :name, Name, default: '.'
13
- # @!attribute type
14
- # 16-bit question type
15
- # @return [Integer]
16
- define_field :type, Types::Int16, default: 1
17
- # @!attribute rrclass
18
- # 16-bit question class
19
- # @return [Integer]
20
- define_field :rrclass, Types::Int16, default: 1
21
-
22
9
  # Ressource Record types
23
10
  TYPES = {
24
11
  'A' => 1,
@@ -49,7 +36,7 @@ module PacketGen
49
36
  'TKEY' => 249,
50
37
  'TSIG' => 250,
51
38
  '*' => 255
52
- }
39
+ }.freeze
53
40
 
54
41
  # Ressource Record classes
55
42
  CLASSES = {
@@ -58,7 +45,20 @@ module PacketGen
58
45
  'HS' => 4,
59
46
  'NONE' => 254,
60
47
  '*' => 255
61
- }
48
+ }.freeze
49
+
50
+ # @!attribute name
51
+ # Question domain name
52
+ # @return [String]
53
+ define_field :name, Name, default: '.'
54
+ # @!attribute type
55
+ # 16-bit question type
56
+ # @return [Integer]
57
+ define_field :type, Types::Int16Enum, default: 1, enum: TYPES
58
+ # @!attribute rrclass
59
+ # 16-bit question class
60
+ # @return [Integer]
61
+ define_field :rrclass, Types::Int16Enum, default: 1, enum: CLASSES
62
62
 
63
63
  # @param [DNS] dns
64
64
  # @param [Hash] options
@@ -72,20 +72,6 @@ module PacketGen
72
72
  self.rrclass = options[:rrclass] if options[:rrclass]
73
73
  end
74
74
 
75
- # Setter for type
76
- # @param [Integer] val
77
- # @return [Integer,String]
78
- def type=(val)
79
- v = case val
80
- when String
81
- self.class::TYPES[val.upcase]
82
- else
83
- val
84
- end
85
- raise ArgumentError, "unknown type #{val.inspect}" unless v
86
- self[:type].read v
87
- end
88
-
89
75
  # Setter for class
90
76
  # @param [Integer] val
91
77
  # @return [Integer,String]
@@ -109,7 +95,7 @@ module PacketGen
109
95
 
110
96
  # Get human readable type
111
97
  # @return [String]
112
- def human_type
98
+ def human_type
113
99
  self.class::TYPES.key(type) || "0x%04x" % type
114
100
  end
115
101
 
@@ -50,7 +50,14 @@ module PacketGen
50
50
  # Check version field
51
51
  # @see [Base#parse?]
52
52
  def parse?
53
- version == 0
53
+ version == 0 and length >= 8
54
+ end
55
+
56
+ # Calculate length field
57
+ # @return [Integer] calculated length
58
+ # @since 2.1.3
59
+ def calc_length
60
+ self[:length].value = self.sz - self[:body].sz
54
61
  end
55
62
 
56
63
  # send PPI packet on wire. Dot11 FCS trailer should be set.
@@ -64,7 +71,7 @@ module PacketGen
64
71
  end
65
72
  self.add_class PPI
66
73
 
67
- # Radiotap header
74
+ # Radiotap header (see http://www.radiotap.org/)
68
75
  # @author Sylvain Daubert
69
76
  class RadioTap < Base
70
77
  # @!attribute version
@@ -75,7 +82,7 @@ module PacketGen
75
82
  define_field :pad, Types::Int8, default: 0
76
83
  # @!attribute length
77
84
  # @return [Integer] 16-bit RadioTap header length
78
- define_field :length, Types::Int16le
85
+ define_field :length, Types::Int16le, default: 8
79
86
  # @!attribute present_flags
80
87
  # @return [Integer] 32-bit integer
81
88
  define_field :present_flags, Types::Int32le
@@ -103,7 +110,14 @@ module PacketGen
103
110
  # Check version field
104
111
  # @see [Base#parse?]
105
112
  def parse?
106
- version == 0
113
+ version == 0 and length >= 8
114
+ end
115
+
116
+ # Calculate length field
117
+ # @return [Integer] calculated length
118
+ # @since 2.1.3
119
+ def calc_length
120
+ self[:length].value = self.sz - self[:body].sz
107
121
  end
108
122
 
109
123
  # send RadioTap packet on wire. Dot11 FCS trailer should be set.
@@ -120,7 +134,7 @@ module PacketGen
120
134
  # IEEE 802.11 header
121
135
  # @abstract This is a base class to demultiplex different IEEE 802.11 frames when
122
136
  # parsing.
123
- # A IEEE 802.11 header may consists of at least:
137
+ # A IEEE 802.11 header may consist of at least:
124
138
  # * a {#frame_ctrl} ({Types::Int16}),
125
139
  # * a {#id}/duration ({Types::Int16le}),
126
140
  # * and a {#mac1} ({Eth::MacAddr}).
@@ -134,10 +148,52 @@ module PacketGen
134
148
  # * a {#body} (a {Types::String} or another {Base} class),
135
149
  # * a Frame check sequence ({#fcs}, of type {Types::Int32le})
136
150
  #
137
- # == header accessors
151
+ # == Header accessors
138
152
  # As Dot11 header types are defined under Dot11 namespace, Dot11 header accessors
139
153
  # have a specific name. By example, to access to a {Dot11::Beacon} header,
140
154
  # accessor is +#dot11_beacon+.
155
+ #
156
+ # == Create Dot11 packets
157
+ # As {Dot11} is an abstract class, you have to use one of its subclasses to
158
+ # instanciate a IEEE802.11 header.
159
+ #
160
+ # === IEEE802.11 control frames
161
+ # Control frames may be created this way:
162
+ # pkt = PacketGen.gen('Dot11::Control', subtype: 13) # Ack control frame
163
+ # pkt.dot11_control # => PacketGen::Header::Dot11::Control
164
+ #
165
+ # === IEEE802.11 management frames
166
+ # Management frames may be created this way:
167
+ # pkt = PacketGen.gen('Dot11::Management')
168
+ # pkt.dot11_management # => PacketGen::Header::Dot11::Management
169
+ # Management frames are usually specialized, AssociationRequest by example:
170
+ # pkt.add('Dot11::AssoReq')
171
+ # pkt.dot11_assoreq # => PacketGen::Header::Dot11::AssoReq
172
+ # Management frames also may contain some elements (see IEEE 802.11 standard):
173
+ # pkt.dot11_assoreq.add_elements(type: 'SSID', value: "My SSID")
174
+ # pkt.dot11_assoreq.add_elements(type: 'Rates', value: supported_rates)
175
+ #
176
+ # === IEEE802.11 data frames
177
+ # Data frames may be created this way:
178
+ # pkt = PacketGen.gen('Dot11::Data')
179
+ # pkt.dot11_data # => PacketGen::Header::Dot11::Data
180
+ #
181
+ # == Parse Dot11 packets
182
+ # When parsing a Dot11 packet, Dot11 subclass is created from +type+ value.
183
+ # Dot11 header should then be accessed through +Packet#dot11_management+,
184
+ # +Packet#dot11_control+ or +Packet#dot11_data+.
185
+ #
186
+ # A +Packet#dot11+ may exist for unknown types.
187
+ #
188
+ # == Send Dot11 packets
189
+ # To send a Dot11 packet, a RadioTap header is needed:
190
+ # pkt = PacketGen.gen('RadioTap')
191
+ # pkt.add('Dot11::Management', mac1: client, mac2: bssid, mac3: bssid)
192
+ # pkt.add('Dot11::Beacon')
193
+ # pkt.dot11_beacon.add_element(type: 'SSID', value: 'My SSID')
194
+ # pkt.dot11_beacon.add_element(type: 'Rates', value: "\x85\x0c")
195
+ # pkt.calc
196
+ # pkt.to_w('wlan0')
141
197
  # @author Sylvain Daubert
142
198
  class Dot11 < Base
143
199
 
@@ -211,6 +267,15 @@ module PacketGen
211
267
  define_bit_fields_on :frame_ctrl, :subtype, 4, :type, 2, :proto_version, 2,
212
268
  :order, :wep, :md, :pwmngt, :retry, :mf, :from_ds, :to_ds
213
269
 
270
+
271
+ # @!attribute sequence_number (12-bit field from {#sequence_ctrl})
272
+ # @return [Integer]
273
+ # @since 2.1.3
274
+ # @!attribute fragment_number (4-bit field from {#sequence_ctrl})
275
+ # @return [Integer]
276
+ # @since 2.1.3
277
+ define_bit_fields_on :sequence_ctrl, :sequence_number, 12, :fragment_number, 4
278
+
214
279
  alias duration id
215
280
  # @private
216
281
  alias old_fields fields
@@ -302,7 +367,7 @@ module PacketGen
302
367
  pcap = PCAPRUB::Pcap.open_live(iface, PCAP_SNAPLEN, PCAP_PROMISC,
303
368
  PCAP_TIMEOUT)
304
369
  str = self.to_s
305
- pcap.inject str << [crc32].pack('V')
370
+ pcap.inject str
306
371
  end
307
372
 
308
373
  private
@@ -317,7 +382,7 @@ module PacketGen
317
382
  unless @applicable_fields.include? :ht_ctrl
318
383
  idx = @applicable_fields.index(:body)
319
384
  @applicable_fields[idx, 0] = :ht_ctrl
320
- end
385
+ end
321
386
  else
322
387
  @applicable_fields -= %i(ht_ctrl)
323
388
  end