pcap_tools 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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: