reassemble_tcp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ M2MxOGE4MTU0NTE2OTVkMmJkYmE3MzA4Y2VmMmRmNDc1ZmMzOTJhNw==
5
+ data.tar.gz: !binary |-
6
+ YmExYjJkOWMyOTNmZTc1NDMzMjg3NWMzNzQ0YTAyMGJhNDFmNGQ4Zg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ OGE2OWIxODBiMGI1YmViOTNjMzEwODk2ZDgxMjczNzdjOWYyZWJhYzcxNjFh
10
+ NGY5MTk0ZDg2NzBhYjEwNTMxMWMzMTg0ZWI3ZjMwMTU5ZjVhZmY3YTM5MGY1
11
+ MzU4ZWIyMTA3Zjk4NmI1MzcwNDdjMTk1MDk3ZjhlMDFmNjRkZDU=
12
+ data.tar.gz: !binary |-
13
+ OTFiYWY2MGU0ZjcxM2RkZmE5ZWQ4NzgzYTU2NDI3ZjkxY2NmMjI5YzBlZWRl
14
+ M2M0YzI1ZTFmMzk2OWUzNjA1N2Q4ZTljMzE1ZjdiOTg5YTU2YWI5NTJjNmIw
15
+ YjM0NTY4NTQxOTdiY2QzYmY3OWE4OGFhYjI2ZWYzMDI4YjlmNzk=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in reassemble_tcp.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 masatanish
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # ReassembleTcp
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'reassemble_tcp'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install reassemble_tcp
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,55 @@
1
+ module ReassembleTcp
2
+
3
+ # packet stream for the same direction and sequence(or acknowledge) number
4
+ class PacketStream
5
+ attr_reader :direction, :seq_ack_num, :range, :pkts
6
+
7
+ # @param [Symbol] direction :send or :recv
8
+ # @param [PacketFu::Packet] pkt packet
9
+ def initialize(direction, pkt)
10
+ case direction
11
+ when :send
12
+ @seq_ack_num = pkt.tcp_seq.to_i
13
+ when :recv
14
+ @seq_ack_num = pkt.tcp_ack.to_i
15
+ else
16
+ raise ArgumentError, "direction should be :send or :recv"
17
+ end
18
+ @direction = direction
19
+ @range = pkt.timestamp..pkt.timestamp
20
+ @pkts = [pkt]
21
+ end
22
+
23
+ # @param [Symbol] direction :send or :recv
24
+ # @param [PacketFu::Packet] pkt packet
25
+ # @return [Array<PacketFu::Packet>]
26
+ def append(direction, pkt)
27
+ raise ArgumentError unless match?(direction, pkt)
28
+ @pkts << pkt
29
+ @range = Range.new(*[@range.begin, @range.end, pkt.timestamp].minmax)
30
+ @pkts
31
+ end
32
+ alias :<< :append
33
+
34
+ # @return [Float] unix time value
35
+ def last_timestamp
36
+ @range.last
37
+ end
38
+
39
+ # @param [Symbol] direction :send or :recv
40
+ # @param [PacketFu::Packet] pkt packet
41
+ # @return [Boolean]
42
+ def match?(direction, pkt)
43
+ num = (direction == :send) ? pkt.tcp_seq.to_i : pkt.tcp_ack.to_i
44
+ @direction == direction && @seq_ack_num == num
45
+ end
46
+
47
+ # reassemble tcp stream data
48
+ # @return [String] reassembled data
49
+ def data
50
+ pkts = @pkts.sort_by!{|pk| pk.timestamp }
51
+ pkts.map{|pkt| pkt.payload }.select{|pay| pay !~ /\A\0+\Z/}.compact.join
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,33 @@
1
+ require 'packetfu'
2
+
3
+ # extension for PacketFu
4
+ module PacketFu
5
+ class Timestamp
6
+ def to_f
7
+ sec.to_i + (usec.to_i / 1000_000.0)
8
+ end
9
+ end
10
+
11
+ class PcapFile
12
+ def self.read_packets_with_timestamp(fname, &block)
13
+ count = 0
14
+ packets = [] unless block
15
+ read(fname) do |packet|
16
+ pkt = Packet.parse(packet.data.to_s)
17
+ pkt.timestamp = packet.timestamp.to_f
18
+ if block
19
+ count += 1
20
+ yield pkt
21
+ else
22
+ packets << pkt
23
+ end
24
+ end
25
+ block ? count : packets
26
+ end
27
+ end
28
+
29
+ class Packet
30
+ # @return [Fixnum] timestamp unix time
31
+ attr_accessor :timestamp
32
+ end
33
+ end
@@ -0,0 +1,98 @@
1
+ require 'packetfu'
2
+
3
+ module ReassembleTcp
4
+
5
+ # TCP connection object
6
+ class TcpConnection
7
+ attr_reader :syn, :timestamp_range
8
+
9
+ # @param [PacketFu::TCPPacket] pkt SYN packet
10
+ def initialize(pkt)
11
+ raise ArgumentError, "#{pkt} is not PacketFu::TCPPacket." unless pkt.kind_of? PacketFu::TCPPacket
12
+ raise ArgumentError, "#{pkt} is not TCP packet." unless pkt.is_tcp?
13
+ raise ArgumentError, "#{pkt} is not SYN packet." unless pkt.tcp_flags[:syn] == 1
14
+
15
+ @syn = pkt
16
+ @timestamp_range = (@syn.timestamp .. Float::INFINITY)
17
+ @stream_array = []
18
+ end
19
+
20
+ def send?(pkt)
21
+ ((@syn.tcp_dport == pkt.tcp_dport and @syn.ip_dst == pkt.ip_dst) and
22
+ (@syn.tcp_sport == pkt.tcp_sport and @syn.ip_src == pkt.ip_src))
23
+ end
24
+
25
+ def recv?(pkt)
26
+ ((@syn.tcp_sport == pkt.tcp_dport and @syn.ip_src == pkt.ip_dst) and
27
+ (@syn.tcp_dport == pkt.tcp_sport and @syn.ip_dst == pkt.ip_src))
28
+ end
29
+
30
+ def src_ip
31
+ @syn.ip_src_readable
32
+ end
33
+
34
+ def src_port
35
+ @syn.tcp_sport
36
+ end
37
+
38
+ def dst_ip
39
+ @syn.ip_dst_readable
40
+ end
41
+
42
+ def dst_port
43
+ @syn.tcp_dport
44
+ end
45
+
46
+ def direction(pkt)
47
+ raise ArgumentError, "#{pkt} is not matched with this connection" unless match?(pkt)
48
+ return :send if send?(pkt)
49
+ return :recv if recv?(pkt)
50
+ raise "error"
51
+ end
52
+
53
+ def match?(pkt)
54
+ raise ArgumentError, "#{pkt} is not TCP packet." unless pkt.is_tcp?
55
+ # target packet is the same source and destination
56
+ return false unless (send?(pkt) || recv?(pkt))
57
+ # target packet is in connection's time range
58
+ return @timestamp_range.include? pkt.timestamp
59
+ end
60
+
61
+ def append(pkt)
62
+ raise ArgumentError, "#{pkt} is not matched with this connection" unless match?(pkt)
63
+ if pkt.tcp_flags[:fin] == 1 || pkt.tcp_flags[:rst] == 1
64
+ # FIN or RST packet is treated as end of the TCP stream.
65
+ # and it determines time range of the TCP stream.
66
+ # FIN packet is not included int Streams
67
+ @timestamp_range = (@syn.timestamp .. pkt.timestamp)
68
+ return
69
+ end
70
+ dir = direction(pkt)
71
+ # store packets into packet stream
72
+ pkt_stm = @stream_array.find{|stm| stm.match? dir, pkt }
73
+ if pkt_stm.nil?
74
+ @stream_array << PacketStream.new(dir, pkt)
75
+ else
76
+ pkt_stm.append(dir, pkt)
77
+ end
78
+ end
79
+ alias :<< :append
80
+
81
+ # @yieldparam [Range] time time range of stream data
82
+ # @yieldparam [Symbol] direction :send or :recv
83
+ # @yieldparam [String] data reassembled TCP data
84
+ def tcpdata(&block)
85
+ arr = []
86
+ @stream_array.sort_by{|pstm| pstm.last_timestamp }.each do |s|
87
+ next if s.data.nil? || s.data.empty?
88
+ if block.nil?
89
+ arr << [s.range, s.direction, s.data]
90
+ else
91
+ yield s.range, s.direction, s.data
92
+ end
93
+ end
94
+ return arr
95
+ end
96
+ end
97
+ end
98
+
@@ -0,0 +1,3 @@
1
+ module ReassembleTcp
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,54 @@
1
+ require 'reassemble_tcp/version'
2
+ require 'reassemble_tcp/version'
3
+ require 'reassemble_tcp/packetfu_extend'
4
+ require 'reassemble_tcp/packet_stream'
5
+ require 'reassemble_tcp/tcp_connection'
6
+
7
+ module ReassembleTcp
8
+
9
+ # get TCP connections from pcap file
10
+ # @param [String] filepath pcapfile path
11
+ # @return [Array<ReassembleTcp::TcpConnection>] tcp connections
12
+ def self.tcp_connections(filepath)
13
+ streams = []
14
+ PacketFu::PcapFile.read_packets_with_timestamp(filepath) {|pkt|
15
+ next unless pkt.is_ip? and pkt.is_tcp?
16
+ stm = streams.find{|ts| ts.match?(pkt) }
17
+ if pkt.tcp_flags[:syn] == 1 && pkt.tcp_flags[:ack] == 0
18
+ next unless stm.nil?
19
+ streams << TcpConnection.new(pkt)
20
+ else
21
+ next if stm.nil?
22
+ stm << pkt
23
+ end
24
+ }
25
+ streams
26
+ end
27
+
28
+ # get reassembled tcp data
29
+ # @param [String] filepath pcapfile path
30
+ # @yield [time, from, to, data]
31
+ # @yieldparam [Time] time packet timestamp
32
+ # @yieldparam [String] from source IP address
33
+ # @yieldparam [String] to destination IP address
34
+ # @yieldparam [String] data tcp resassembled data
35
+ def self.tcp_data_stream(filepath, &block)
36
+ stream_data = {}
37
+ ReassembleTcp.tcp_connections(filepath).each do |conn|
38
+ dst = conn.dst_ip
39
+ src = conn.src_ip
40
+ conn.tcpdata do |range, dir, data|
41
+ next if data.nil? || data.empty?
42
+ from, to = (dir == :send ) ? [src, dst] : [dst, src]
43
+ p range.last
44
+ etime = Time.at(range.last)
45
+ stream_data[etime] = [from, to, data]
46
+ end
47
+ end
48
+ stream_data.keys.sort.each do |etime|
49
+ from, to, data = stream_data[etime]
50
+ yield etime, from, to, data
51
+ end
52
+ nil
53
+ end
54
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'reassemble_tcp/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "reassemble_tcp"
8
+ spec.version = ReassembleTcp::VERSION
9
+ spec.authors = ["masatanish"]
10
+ spec.email = ["masatanish@gmail.com"]
11
+ spec.description = %q{Reassemble TCP fragment data from pcap file like Wireshark.}
12
+ spec.summary = %q{Reassemble TCP fragment data from pcap file like Wireshark.}
13
+ spec.homepage = "https://github.com/masatanish/reassemble_tcp"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "packetfu", "~> 1.1.9"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ end
Binary file
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe ReassembleTcp do
4
+ it 'should have a version number' do
5
+ ReassembleTcp::VERSION.should_not be_nil
6
+ end
7
+
8
+ let(:test_pcap_path){ File.expand_path(File.dirname(__FILE__) + '/data/test.pcap') }
9
+
10
+ describe '.tcp_connections' do
11
+ subject { ReassembleTcp.tcp_connections(path) }
12
+ context 'with test pcap file' do
13
+ let(:path) { test_pcap_path }
14
+ it { expect(subject).to be_a Array }
15
+ it 'should return 4 connections' do
16
+ expect(subject.size).to eq 4
17
+ end
18
+ it 'should return an array of ReassembleTcp::TcpConnection' do
19
+ expect(subject.first).to be_a ReassembleTcp::TcpConnection
20
+ end
21
+ end
22
+ end
23
+
24
+ describe '.tcp_data_stream' do
25
+ pending 'not implemented yet.'
26
+ end
27
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'reassemble_tcp'
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reassemble_tcp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - masatanish
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: packetfu
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.9
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.1.9
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Reassemble TCP fragment data from pcap file like Wireshark.
70
+ email:
71
+ - masatanish@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rspec
78
+ - .travis.yml
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - lib/reassemble_tcp.rb
84
+ - lib/reassemble_tcp/packet_stream.rb
85
+ - lib/reassemble_tcp/packetfu_extend.rb
86
+ - lib/reassemble_tcp/tcp_connection.rb
87
+ - lib/reassemble_tcp/version.rb
88
+ - reassemble_tcp.gemspec
89
+ - spec/data/test.pcap
90
+ - spec/reassemble_tcp_spec.rb
91
+ - spec/spec_helper.rb
92
+ homepage: https://github.com/masatanish/reassemble_tcp
93
+ licenses:
94
+ - MIT
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 2.1.5
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: Reassemble TCP fragment data from pcap file like Wireshark.
116
+ test_files:
117
+ - spec/data/test.pcap
118
+ - spec/reassemble_tcp_spec.rb
119
+ - spec/spec_helper.rb