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 +17 -9
- data/lib/pcap_parser.rb +218 -0
- data/lib/pcap_tools.rb +11 -9
- data/pcap_tools.gemspec +2 -2
- metadata +7 -5
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}";
|
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
|
-
|
43
|
-
|
44
|
-
|
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
|
data/lib/pcap_parser.rb
ADDED
@@ -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
|
-
|
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 <<
|
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.
|
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.
|
81
|
-
if packet.
|
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.
|
85
|
+
break if packet.fin == 1 || packet2.fin == 1
|
84
86
|
end
|
85
|
-
if packet.
|
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.
|
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.
|
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('
|
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
|
+
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-
|
12
|
+
date: 2013-11-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
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.
|
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.
|
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:
|