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 +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:
|