pcap_tools 0.0.4 → 0.0.5

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/bin/pcap_tools CHANGED
@@ -20,7 +20,7 @@ OptionParser.new do |opts|
20
20
 
21
21
  end.parse!
22
22
 
23
- data = ARGV.map{|f| puts "Loading #{f}"; PacketFu::PcapFile.file_to_array(f)}
23
+ data = ARGV.map{|f| puts "Loading #{f}"; PcapTools::Parser::load_file(f)}
24
24
 
25
25
  tcps = PcapTools::extract_tcp_streams(data)
26
26
 
@@ -28,24 +28,32 @@ puts "Tcp streams extracted : #{tcps.size}"
28
28
  puts "Parsing mode : #{options[:mode]}"
29
29
  puts
30
30
 
31
+ def format_time t
32
+ "#{t} #{t.nsec / 1000}"
33
+ end
34
+
31
35
  if options[:mode] == :http
32
36
  tcps.each do |tcp|
33
37
  PcapTools::extract_http_calls(tcp).each do |req, resp|
34
- puts ">>>> #{req["pcap-src"]}:#{req["pcap-src-port"]} > #{req["pcap-dst"]}:#{req["pcap-dst-port"]}"
38
+ puts ">>>> #{req["pcap-src"]}:#{req["pcap-src-port"]} > #{req["pcap-dst"]}:#{req["pcap-dst-port"]} #{format_time req.time}"
35
39
  puts "#{req.method} #{req.path}"
36
40
  req.each_capitalized_name.reject{|x| x =~ /^Pcap/ }.each do |x|
37
41
  puts "#{x}: #{req[x]}"
38
42
  end
39
43
  puts
40
44
  puts req.body unless options[:no_body]
41
- puts "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< #{resp.time}"
42
- puts "#{resp.code} #{resp.message}"
43
- resp.each_capitalized_name.reject{|x| x =~ /^Pcap/ }.each do |x|
44
- puts "#{x}: #{resp[x]}"
45
+ puts "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< #{format_time resp.time}"
46
+ if resp
47
+ puts "#{resp.code} #{resp.message}"
48
+ resp.each_capitalized_name.reject{|x| x =~ /^Pcap/ }.each do |x|
49
+ puts "#{x}: #{resp[x]}"
50
+ end
51
+ puts
52
+ puts resp.body unless options[:no_body]
53
+ else
54
+ puts "No response in pcap file"
45
55
  end
46
56
  puts
47
- puts resp.body unless options[:no_body]
48
- puts
49
57
  end
50
58
  end
51
59
  end
@@ -54,7 +62,7 @@ if options[:mode] == :tcp
54
62
  tcps.each do |tcp|
55
63
  tcp.each do |packet|
56
64
  type = packet[:type] == :out ? ">>>>" : "<<<<<"
57
- puts "#{type} #{packet[:from]}:#{packet[:from_port]} > #{packet[:to]}:#{packet[:to_port]}, size #{packet[:data].size}"
65
+ puts "#{type} #{packet[:from]}:#{packet[:from_port]} > #{packet[:to]}:#{packet[:to_port]}, size #{packet[:data].size} #{format_time packet[:time]}"
58
66
  puts packet[:data]
59
67
  puts
60
68
  end
@@ -0,0 +1,218 @@
1
+ require 'bindata'
2
+
3
+ module PcapTools
4
+
5
+ module Parser
6
+
7
+ module HasParent
8
+
9
+ attr_accessor :parent
10
+
11
+ end
12
+
13
+ class PcapFile < BinData::Record
14
+ endian :little
15
+
16
+ struct :header do
17
+ uint32 :magic
18
+ uint16 :major
19
+ uint16 :minor
20
+ int32 :this_zone
21
+ uint32 :sig_figs
22
+ uint32 :snaplen
23
+ uint32 :linktype
24
+ end
25
+
26
+ array :packets, :read_until => :eof do
27
+ uint32 :ts_sec
28
+ uint32 :ts_usec
29
+ uint32 :incl_len
30
+ uint32 :orig_len
31
+ string :data, :length => :incl_len
32
+ end
33
+
34
+ end
35
+
36
+ # Present IP addresses in a human readable way
37
+ class IPAddr < BinData::Primitive
38
+ array :octets, :type => :uint8, :initial_length => 4
39
+
40
+ def set(val)
41
+ ints = val.split(/\./).collect { |int| int.to_i }
42
+ self.octets = ints
43
+ end
44
+
45
+ def get
46
+ self.octets.collect { |octet| "%d" % octet }.join(".")
47
+ end
48
+ end
49
+
50
+ # TCP Protocol Data Unit
51
+ class TCP_PDU < BinData::Record
52
+ mandatory_parameter :packet_length
53
+
54
+ endian :big
55
+
56
+ uint16 :src_port
57
+ uint16 :dst_port
58
+ uint32 :seq
59
+ uint32 :ack_seq
60
+ bit4 :doff
61
+ bit4 :res1
62
+ bit2 :res2
63
+ bit1 :urg
64
+ bit1 :ack
65
+ bit1 :psh
66
+ bit1 :rst
67
+ bit1 :syn
68
+ bit1 :fin
69
+ uint16 :window
70
+ uint16 :checksum
71
+ uint16 :urg_ptr
72
+ string :options, :read_length => :options_length_in_bytes
73
+ string :payload, :read_length => lambda { packet_length - payload.rel_offset }
74
+
75
+ def options_length_in_bytes
76
+ (doff - 5 ) * 4
77
+ end
78
+
79
+ def type
80
+ "TCP"
81
+ end
82
+
83
+ include HasParent
84
+
85
+ end
86
+
87
+ # UDP Protocol Data Unit
88
+ class UDP_PDU < BinData::Record
89
+ mandatory_parameter :packet_length
90
+
91
+ endian :big
92
+
93
+ uint16 :src_port
94
+ uint16 :dst_port
95
+ uint16 :len
96
+ uint16 :checksum
97
+ string :payload, :read_length => lambda { packet_length - payload.rel_offset }
98
+
99
+ def type
100
+ "UDP"
101
+ end
102
+
103
+ include HasParent
104
+ end
105
+
106
+ # IP Protocol Data Unit
107
+ class IP_PDU < BinData::Record
108
+ endian :big
109
+
110
+ bit4 :version, :asserted_value => 4
111
+ bit4 :header_length
112
+ uint8 :tos
113
+ uint16 :total_length
114
+ uint16 :ident
115
+ bit3 :flags
116
+ bit13 :frag_offset
117
+ uint8 :ttl
118
+ uint8 :protocol
119
+ uint16 :checksum
120
+ ip_addr :src_addr
121
+ ip_addr :dst_addr
122
+ string :options, :read_length => :options_length_in_bytes
123
+ choice :payload, :selection => :protocol do
124
+ tcp_pdu 6, :packet_length => :payload_length_in_bytes
125
+ udp_pdu 17, :packet_length => :payload_length_in_bytes
126
+ string :default, :read_length => :payload_length_in_bytes
127
+ end
128
+
129
+ def header_length_in_bytes
130
+ header_length * 4
131
+ end
132
+
133
+ def options_length_in_bytes
134
+ header_length_in_bytes - options.rel_offset
135
+ end
136
+
137
+ def payload_length_in_bytes
138
+ total_length - header_length_in_bytes
139
+ end
140
+
141
+ def type
142
+ "IP"
143
+ end
144
+
145
+ include HasParent
146
+ end
147
+
148
+ class MacAddr < BinData::Primitive
149
+ array :octets, :type => :uint8, :initial_length => 6
150
+
151
+ def set(val)
152
+ ints = val.split(/\./).collect { |int| int.to_i }
153
+ self.octets = ints
154
+ end
155
+
156
+ def get
157
+ self.octets.collect { |octet| "%02x" % octet }.join(":")
158
+ end
159
+ end
160
+
161
+ IPV4 = 0x0800
162
+ class Ethernet < BinData::Record
163
+ endian :big
164
+
165
+ mac_addr :dst
166
+ mac_addr :src
167
+ uint16 :protocol
168
+ choice :payload, :selection => :protocol do
169
+ ip_pdu IPV4
170
+ rest :default
171
+ end
172
+
173
+ include HasParent
174
+ end
175
+
176
+ class LinuxCookedCapture < BinData::Record
177
+ endian :big
178
+
179
+ uint16 :type
180
+ uint16 :address_type
181
+ uint16 :address_len
182
+ array :octets, :type => :uint8, :initial_length => 8
183
+ uint16 :protocol
184
+ choice :payload, :selection => :protocol do
185
+ ip_pdu IPV4
186
+ rest :default
187
+ end
188
+
189
+ include HasParent
190
+ end
191
+
192
+ def load_file f
193
+ packets = []
194
+ File.open(f, 'rb') do |io|
195
+ content = PcapFile.read(io)
196
+ raise 'Wrong endianess' unless content.header.magic.to_i.to_s(16) == "a1b2c3d4"
197
+ content.packets.each do |original_packet|
198
+ packet = case content.header.linktype
199
+ when 113 then LinuxCookedCapture.read(original_packet.data)
200
+ when 1 then Ethernet.read(original_packet.data)
201
+ else raise "Unknown network #{content.header.linktype}"
202
+ end
203
+ packet.parent = original_packet
204
+ while packet.respond_to?(:payload) && packet.payload.is_a?(BinData::Choice)
205
+ packet.payload.parent = packet
206
+ packet = packet.payload
207
+ end
208
+ packets << packet
209
+ end
210
+ end
211
+ packets
212
+ end
213
+
214
+ module_function :load_file
215
+
216
+ end
217
+
218
+ end
data/lib/pcap_tools.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  require 'rubygems'
2
- require 'packetfu'
3
2
  require 'net/http'
4
3
  require 'zlib'
5
4
 
5
+ require File.join(File.dirname(__FILE__), 'pcap_parser')
6
+
6
7
  module Net
7
8
 
8
9
  class HTTPRequest
@@ -28,7 +29,8 @@ module PcapTools
28
29
  def insert_tcp sym, packet
29
30
  data = packet.payload
30
31
  return if data.size == 0
31
- self << {:type => sym, :data => data, :from => packet.ip_saddr, :to => packet.ip_daddr, :from_port => packet.tcp_src, :to_port => packet.tcp_dst}
32
+ timestamp = Time.at(packet.parent.parent.parent.ts_sec, packet.parent.parent.parent.ts_usec)
33
+ self << {:type => sym, :data => data, :from => packet.parent.src_addr, :to => packet.parent.dst_addr, :from_port => packet.src_port, :to_port => packet.dst_port, :time => timestamp}
32
34
  end
33
35
 
34
36
  def rebuild_packets
@@ -66,25 +68,25 @@ module PcapTools
66
68
  packets = []
67
69
  captures.each do |capture|
68
70
  capture.each do |packet|
69
- packets << PacketFu::Packet.parse(packet)
71
+ packets << packet
70
72
  end
71
73
  end
72
74
 
73
75
  streams = []
74
76
  packets.each_with_index do |packet, k|
75
- if packet.is_a?(PacketFu::TCPPacket) && packet.tcp_flags.syn == 1 && packet.tcp_flags.ack == 0
77
+ if packet.respond_to?(:type) && packet.type == "TCP" && packet.syn == 1 && packet.ack == 0
76
78
  kk = k
77
79
  tcp = TcpStream.new
78
80
  while kk < packets.size
79
81
  packet2 = packets[kk]
80
- if packet2.is_a?(PacketFu::TCPPacket)
81
- if packet.tcp_dst == packet2.tcp_dst && packet.tcp_src == packet2.tcp_src
82
+ if packet2.respond_to?(:type) && packet.type == "TCP"
83
+ if packet.dst_port == packet2.dst_port && packet.src_port == packet2.src_port
82
84
  tcp.insert_tcp :out, packet2
83
- break if packet.tcp_flags.fin == 1 || packet2.tcp_flags.fin == 1
85
+ break if packet.fin == 1 || packet2.fin == 1
84
86
  end
85
- if packet.tcp_dst == packet2.tcp_src && packet.tcp_src == packet2.tcp_dst
87
+ if packet.dst_port == packet2.src_port && packet.src_port == packet2.dst_port
86
88
  tcp.insert_tcp :in, packet2
87
- break if packet.tcp_flags.fin == 1 || packet2.tcp_flags.fin == 1
89
+ break if packet.fin == 1 || packet2.fin == 1
88
90
  end
89
91
  end
90
92
  kk += 1
data/pcap_tools.gemspec CHANGED
@@ -2,7 +2,7 @@ require 'rake'
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'pcap_tools'
5
- s.version = '0.0.4'
5
+ s.version = '0.0.5'
6
6
  s.authors = ['Bertrand Paquet']
7
7
  s.email = 'bertrand.paquet@gmail.com'
8
8
  s.summary = 'Tools for extracting data from pcap files'
@@ -11,5 +11,5 @@ Gem::Specification.new do |s|
11
11
  s.files = `git ls-files`.split($/)
12
12
  s.license = 'BSD'
13
13
 
14
- s.add_runtime_dependency('packetfu', '>= 1.1.9')
14
+ s.add_runtime_dependency('bindata', '>= 1.6.0')
15
15
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pcap_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,16 +9,16 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-26 00:00:00.000000000 Z
12
+ date: 2013-11-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: packetfu
15
+ name: bindata
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 1.1.9
21
+ version: 1.6.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
- version: 1.1.9
29
+ version: 1.6.0
30
30
  description:
31
31
  email: bertrand.paquet@gmail.com
32
32
  executables:
@@ -36,6 +36,7 @@ extra_rdoc_files: []
36
36
  files:
37
37
  - README.markdown
38
38
  - bin/pcap_tools
39
+ - lib/pcap_parser.rb
39
40
  - lib/pcap_tools.rb
40
41
  - pcap_tools.gemspec
41
42
  homepage: https://github.com/bpaquet/pcap_tools
@@ -64,3 +65,4 @@ signing_key:
64
65
  specification_version: 3
65
66
  summary: Tools for extracting data from pcap files
66
67
  test_files: []
68
+ has_rdoc: