packetman 0.1.0 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d179b4712440f17879c705887caeec69fd857517
4
- data.tar.gz: 7879478eab7e6ecca5d75bff051a5dcad4006465
2
+ SHA256:
3
+ metadata.gz: 2820eacc8a12d33ab3ce8799c4c37d350457775f741173b86766fe5e8236951e
4
+ data.tar.gz: e977ac8d6076e95671ac2ccf0539c14d38653b01395e9fe838bcfdf2c55d76b7
5
5
  SHA512:
6
- metadata.gz: 59f189ec8d961cc829938163d21daffea43b4ce14f5b0487d49339ad59abf6cbefabfbb5254e2b949132987ee06df4b584664d9e0d1f7973c33835c1328cd59a
7
- data.tar.gz: 4e6b9134085f93800d07dd8a5e218c48d1c25260832b837c4f2e4fc365545234589232e0af344b8e1687eb90f6aa93d91875a341cfcf1165c1bec3d2341fd3a5
6
+ metadata.gz: '06068e575d4aa980070dadb455f1032910e43d03c39fcc28416eb66517d0b4ba120c595110ece205b478ddd9d18d5ada0a26980276f1d02f1ad7327778323128'
7
+ data.tar.gz: f64e896df598544f0f3174cda71dc430189de8a735f6d0c2639dcbef9877cfa2f4b596fe00fa4bfbd7f0102e4b9a87e529f66a5078598b85fd4118e5e5d84e79
@@ -0,0 +1,55 @@
1
+ version: 2.1
2
+ jobs:
3
+ build:
4
+ parameters:
5
+ ruby_version:
6
+ description: Version of ruby to test
7
+ type: string
8
+ environment:
9
+ GEM_HOME: vendor/bundle
10
+ docker:
11
+ - image: circleci/ruby:<< parameters.ruby_version >>
12
+ working_directory: /tmp/project
13
+ steps:
14
+ - checkout
15
+ - restore_cache:
16
+ keys:
17
+ - v1-bundle-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_SHA1 }}
18
+ - v1-bundle-{{ .Environment.CIRCLE_JOB }}-
19
+ - run:
20
+ name: Bundle install
21
+ command: >
22
+ gem install bundler -i vendor/bundle
23
+ && bundle install --path vendor/bundle
24
+ - run:
25
+ name: Prepare CodeClimate
26
+ command: >
27
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
28
+ && chmod +x ./cc-test-reporter
29
+ - save_cache:
30
+ key: v1-bundle-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_SHA1 }}
31
+ paths:
32
+ - /tmp/project/vendor
33
+ - run:
34
+ name: Run Tests
35
+ command: >
36
+ ./cc-test-reporter before-build
37
+ bundle exec rake spec
38
+ ./cc-test-reporter after-build
39
+
40
+ workflows:
41
+ version: 2
42
+ build:
43
+ jobs:
44
+ - build:
45
+ name: ruby_2_3_8
46
+ ruby_version: 2.3.8
47
+ - build:
48
+ name: ruby_2_4_10
49
+ ruby_version: 2.4.10
50
+ - build:
51
+ name: ruby_2_5_8
52
+ ruby_version: 2.5.8
53
+ - build:
54
+ name: ruby_2_6_6
55
+ ruby_version: 2.6.6
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ /vendor/
data/README.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # Packetman
2
2
 
3
- Advanced tcpdump and Wireshark capture generator.
3
+ Advanced tcpdump and Wireshark filter string generator.
4
4
 
5
- [![Code Climate](https://codeclimate.com/github/jescholl/packetman/badges/gpa.svg)](https://codeclimate.com/github/jescholl/packetman) [![Test Coverage](https://codeclimate.com/github/jescholl/packetman/badges/coverage.svg)](https://codeclimate.com/github/jescholl/packetman/coverage) [![Circle CI](https://circleci.com/gh/jescholl/packetman.svg?style=svg)](https://circleci.com/gh/jescholl/packetman)
5
+ [![Gem Version](https://badge.fury.io/rb/packetman.svg)](http://badge.fury.io/rb/packetman)
6
+ [![Test Coverage](https://codeclimate.com/github/jescholl/packetman/badges/coverage.svg)](https://codeclimate.com/github/jescholl/packetman/coverage)
7
+ [![Inline docs](http://inch-ci.org/github/jescholl/packetman.svg?branch=master)](http://inch-ci.org/github/jescholl/packetman)
8
+ [![Circle CI](https://circleci.com/gh/jescholl/packetman.svg?style=svg)](https://circleci.com/gh/jescholl/packetman)
9
+ [![Code Climate](https://codeclimate.com/github/jescholl/packetman/badges/gpa.svg)](https://codeclimate.com/github/jescholl/packetman)
6
10
 
7
11
  Packetman is a packet capture filter generator modeled after [Wireshark's String-Matching Capture Filter Generator](https://www.wireshark.org/tools/string-cf.html) but with a lot more features allowing much finer control over the packets you see.
8
12
 
@@ -34,7 +38,40 @@ Or install it yourself as:
34
38
 
35
39
  ## Usage
36
40
 
37
- TODO: Write usage instructions here
41
+ $ packetman -h
42
+
43
+ Usage: packetman [OPTIONS] FILTER_STRING
44
+ -p, --protocol PROTO Transport Protocol (tcp,udp,icmp)
45
+ -t, --transport OFFSET starts at transport header instead of data payload
46
+ -r, --radix RADIX Treat FILTER_STRING as RADIX instead of String
47
+ -o, --offset OFFSET Offset in bits
48
+ -b, --byte-offset Use 8-bit bytes instead of bits for offset
49
+ -w, --wildcard [CHARACTER=?] Treat CHARACTER as single-character wildcard
50
+ -v, --version Show version
51
+
52
+ Create and use a filter string to capture all HTTP GET requests to `/foo/bar`
53
+
54
+ $ sudo tcpdump -nA `packetman GET /foo/bar`
55
+ tcpdump: data link type PKTAP
56
+ tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
57
+ listening on pktap, link-type PKTAP (Packet Tap), capture size 262144 bytes
58
+ 16:49:04.516409 IP 127.0.0.1.54662 > 127.0.0.1.80: Flags [P.], seq 1488105913:1488105994, ack 1397163988, win 4121, options [nop,nop,TS val 875380202 ecr 2751916352], length 81: HTTP: GET /foo/bar HTTP/1.1
59
+ .....b....j...E.....@.@..S..
60
+ ..:.....PX...SG......75.....
61
+ 4-=....@GET /foo/bar HTTP/1.1
62
+ Host: localhost
63
+ User-Agent: curl/7.43.0
64
+ Accept: */*
65
+
66
+ Hexadecimal string with wildcards
67
+
68
+ $ packetman -r 16 -w '?' "A8C401???C200A"
69
+ tcp[((tcp[12:1] & 0xf0) >> 2) + 0:4] & 0xffffff00 = 0xa8c40100 && tcp[((tcp[12:1] & 0xf0) >> 2) + 4:2] & 0x0fff = 0x0c20 && tcp[((tcp[12:1] & 0xf0) >> 2) + 6:1] & 0xff = 0x0a
70
+
71
+ Base 4 string with wildcards and offset beginning at start of the TCP header
72
+
73
+ $ packetman -t -o 3 -r 4 -w i 1223iiii2212
74
+ tcp[0:4] & 0x1fe01fe0 = 0x0d6014c0
38
75
 
39
76
  ## Development
40
77
 
@@ -0,0 +1,7 @@
1
+ ---
2
+ dns: # packetman -tbo 21 -p udp -w '.' 'google.com'
3
+ :offset: 21
4
+ :start_with_transport: true
5
+ :offset_type: :bytes
6
+ :wildcard: .
7
+ :transport: udp
@@ -1,35 +1,56 @@
1
1
  ---
2
- transport:
3
- tcp:
4
- table:
5
- Source Port: 16
6
- Destination Port: 16
7
- Sequence Number: 32
8
- Acknowledgement Number: 32
9
- Data Offset: 4
10
- RESERVED: 3
11
- ECN: 3
12
- Control Bits: 6
13
- Window: 16
14
- Checksum: 16
15
- Urgent Pointer: 16
16
- Options and Padding: 32
17
- payload_query: '((tcp[12:1] & 0xf0) >> 2)'
18
- udp:
19
- table:
20
- Source Port: 16
21
- Destination Port: 16
22
- Length: 16
23
- Checksum: 16
24
- payload_query: 8
25
- icmp:
26
- table:
27
- Type: 8
28
- Code: 8
29
- Checksum: 16
30
- Type Specific Options: 32
31
- application:
32
- http:
33
- transport_protocol: tcp
34
- dns:
35
- transport_protocol: tcp
2
+ tcp:
3
+ table:
4
+ - - :value: Source Port
5
+ :colspan: 16
6
+ - :value: Destination Port
7
+ :colspan: 16
8
+ - :separator
9
+ - - :value: Sequence Number
10
+ :colspan: 32
11
+ - :separator
12
+ - - :value: Acknowledgement Number
13
+ :colspan: 32
14
+ - :separator
15
+ - - :value: Data Offset
16
+ :colspan: 4
17
+ - :value: RESERVED
18
+ :colspan: 3
19
+ - :value: ECN
20
+ :colspan: 3
21
+ - :value: Control Bits
22
+ :colspan: 6
23
+ - :value: Window
24
+ :colspan: 16
25
+ - :separator
26
+ - - :value: Checksum
27
+ :colspan: 16
28
+ - :value: Urgent Pointer
29
+ :colspan: 16
30
+ - :separator
31
+ - - :value: Options and Padding
32
+ :colspan: 32
33
+ payload_query: '((tcp[12:1] & 0xf0) >> 2)'
34
+ udp:
35
+ table:
36
+ - - :value: Source Port
37
+ :colspan: 16
38
+ - :value: Destination Port
39
+ :colspan: 16
40
+ - :separator
41
+ - - :value: Length
42
+ :colspan: 16
43
+ - :value: Checksum
44
+ :colspan: 16
45
+ payload_query: 8
46
+ icmp:
47
+ table:
48
+ - - :value: Type
49
+ :colspan: 8
50
+ - :value: Code
51
+ :colspan: 8
52
+ - :value: Checksum
53
+ :colspan: 16
54
+ - :separator
55
+ - - :value: Type Specific Options
56
+ :colspan: 32
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'packetman'
3
3
 
4
- search_str = Packetman.config.parse_opts
4
+ catch :exit do
5
+ search_str = Packetman.config.parse_opts
5
6
 
6
- puts Packetman::Compose.new(search_str)
7
+ puts Packetman::Filter.new(search_str)
8
+ end
@@ -3,7 +3,8 @@ require "packetman/version"
3
3
  require "packetman/config"
4
4
  require "packetman/config_methods"
5
5
  require "packetman/table"
6
- require "packetman/compose"
6
+ require "packetman/filter"
7
+ require "packetman/clause"
7
8
 
8
9
  module Packetman
9
10
  class << self
@@ -16,9 +17,5 @@ module Packetman
16
17
  @config = Config.new
17
18
  end
18
19
 
19
- def user_input(prompt)
20
- puts prompt
21
- gets
22
- end
23
20
  end
24
21
  end
@@ -0,0 +1,33 @@
1
+ module Packetman
2
+ class Clause
3
+ include ConfigMethods
4
+
5
+ attr_accessor :search, :mask, :start_bit
6
+
7
+ def initialize(search, mask, start_bit)
8
+ self.search = search
9
+ self.mask = mask
10
+ self.start_bit = start_bit
11
+ end
12
+
13
+ # Address of first byte
14
+ def start_byte
15
+ [config.payload_query, (config.offset_bits + start_bit)/8].compact.join(' + ')
16
+ end
17
+
18
+ def num_bytes
19
+ Filter.bit_length(search)/8
20
+ end
21
+
22
+ # Full address of the query data (eg. `tcp[0:4]`)
23
+ def data_address
24
+ "#{config.transport}[#{start_byte}:#{num_bytes}]"
25
+ end
26
+
27
+ # The whole filter clause fully assembled
28
+ def to_s
29
+ "#{data_address} & #{mask} = #{search}"
30
+ end
31
+
32
+ end
33
+ end
@@ -2,69 +2,63 @@ require 'optparse'
2
2
 
3
3
  module Packetman
4
4
  class Config
5
- attr_accessor :transport, :application, :offset_units, :offset_type, :allow_wildcards, :radix
6
- attr_writer :offset
5
+ attr_accessor :transport, :application, :offset_type, :radix, :start_with_transport, :offset, :wildcard
7
6
 
8
7
  def initialize
9
- @transport = "tcp"
10
- @application = "http"
11
- @offset = 0
12
- @offset_units = "bits"
13
- @offset_type = "application"
14
- @allow_wildcards = true
8
+ self.transport = "tcp"
9
+ self.offset = 0
10
+ self.offset_type = :bits
15
11
  end
16
12
 
17
13
  def protocols
18
14
  @protocols ||= YAML.load(File.read(File.expand_path('../../../config/protocols.yml', __FILE__)))
19
15
  end
20
16
 
17
+ def applications
18
+ @applications ||= YAML.load(File.read(File.expand_path('../../../config/applications.yml', __FILE__)))
19
+ end
20
+
21
21
  def payload_query
22
- case offset_type
23
- when "application"
24
- protocols['transport'][transport.to_s]['payload_query']
22
+ protocols[transport]['payload_query'] unless start_with_transport
23
+ end
24
+
25
+ def offset_bits
26
+ if offset_type == :bytes
27
+ offset*8
25
28
  else
26
- "0"
29
+ offset
27
30
  end
28
31
  end
29
32
 
30
- def offset
31
- case offset_units
32
- when "bytes"
33
- @offset*8
34
- else
35
- @offset
33
+ # FIXME figure out a way to do defaults so this can just set defaults
34
+ def application_override(app_name)
35
+ applications[app_name].each do |key, value|
36
+ __send__("#{key}=", value)
36
37
  end
37
38
  end
38
39
 
39
- def parse_opts
40
+ def opts
40
41
  @opts ||= OptionParser.new do |opt|
41
42
  opt.banner = "Usage: #{File.basename($PROGRAM_NAME)} [OPTIONS] FILTER_STRING"
42
- opt.on("-t", "--transport [PROTO]", protocols['transport'].keys, "Transport Protocol (#{protocols['transport'].keys.join(',')})") {|v| self.transport = v }
43
- opt.on("-a", "--application [PROTO]", protocols['application'].keys, "Application protocol (#{protocols['application'].keys.join(',')})") { |v| self.application = v }
44
- #opt.on("-a", "--application [PROTO]", String, "Application Protocol (http|dns|icmp)") {|v| self.application = v }
45
- opt.on("-r", "--radix [RADIX]", Integer, "Treat FILTER_STRING as RADIX instead of String") {|v| self.radix = v; }
46
- opt.on("-o", "--offset [OFFSET]", Integer, "Offset in bits") {|v| self.offset = v; }
47
- opt.on("-b", "--byte-offset", "Use 8-bit bytes instead of bits for offset") { |v| self.offset_units = "bytes" }
48
- opt.on("-O", "--offset-type [TYPE]", ["application", "transport"], "Begin offset at the application/transport header.") { |v| self.offset_type = v }
49
- opt.on("--[no-]wildcards", "Allow '?' wildcards") { |v| self.allow_wildcards = v }
43
+ opt.on("-p", "--protocol PROTO", protocols.keys, "Transport Protocol (#{protocols.keys.join(',')})") { |v| self.transport = v }
44
+ opt.on("-a", "--application APPLICATION", applications.keys, "Application Protocol (#{applications.keys.join(',')}) OVERRIDES ALL OTHER SETTINGS") { |v| application_override(v) }
45
+ opt.on("-t", "--transport", "OFFSET starts at transport header instead of data payload") { |v| self.start_with_transport = v }
46
+ opt.on("-r", "--radix RADIX", Integer, "Treat FILTER_STRING as RADIX instead of String") { |v| self.radix = v }
47
+ opt.on("-o", "--offset OFFSET", Integer, "Offset in bits") { |v| self.offset = v }
48
+ opt.on("-b", "--byte-offset", "Use 8-bit bytes instead of bits for offset") { |v| self.offset_type = :bytes if v }
49
+ opt.on("-w", "--wildcard CHARACTER", "Treat CHARACTER as single-character wildcard") { |v| raise "invalid wildcard" if v.to_s.length > 1; self.wildcard = v }
50
+ opt.on("--table", "Show transport header table") { puts Packetman::Table.new; throw :exit }
51
+ opt.on("-v", "--version", "Show version") { puts Packetman::VERSION; throw :exit }
50
52
  end
53
+ end
51
54
 
52
- @opts.parse!
53
-
54
- raise "Invalid command line arguments" if ARGV.size != 1
55
-
56
- ARGV.pop
57
-
58
-
59
- # if transport !~ /tcp|udp/i ||
60
- # application !~ /http|dns|icmp/i
61
- # raise "invalid options"
62
- # end
63
- #
64
- # if offset_units == :bits
65
- # offset /= 8.to_f
66
- # offset_units = :octets
67
- # end
55
+ def parse_opts
56
+ unparsed_opts = opts.parse!
57
+ if unparsed_opts.length < 1
58
+ puts opts
59
+ throw :exit
60
+ end
61
+ unparsed_opts.join(" ")
68
62
  end
69
63
 
70
64
  end
@@ -7,5 +7,9 @@ module Packetman
7
7
  def config
8
8
  Packetman.config
9
9
  end
10
+
11
+ def protocols
12
+ config.protocols
13
+ end
10
14
  end
11
15
  end
@@ -0,0 +1,101 @@
1
+ module Packetman
2
+ class Filter
3
+ include ConfigMethods
4
+
5
+ attr_accessor :input
6
+
7
+ def initialize(input)
8
+ self.input = input
9
+ yield config if block_given?
10
+ end
11
+
12
+ def self.bit_length(num)
13
+ case num
14
+ when /^0x/
15
+ $'.length * bit_density(16)
16
+ else
17
+ nil
18
+ end
19
+ end
20
+
21
+ def self.bit_density(radix=config.radix)
22
+ (radix.nil?) ? 8 : Math.log2(radix).ceil
23
+ end
24
+
25
+ def map_chr
26
+ shift_and_pad(input.scan(/./).map{ |chr| yield chr }.join)
27
+ end
28
+
29
+ def shift_and_pad(bin_str)
30
+ #shift
31
+ bin_str.ljust(target_bit_length, '0').
32
+ #pad
33
+ rjust(target_bit_length + config.offset_bits % 8, '0')
34
+ end
35
+
36
+ def target_bit_length
37
+ ((input.length*self.class.bit_density + config.offset_bits)/8.to_f).ceil*8 - config.offset_bits
38
+ end
39
+
40
+ # Mask for 1 character of current radix
41
+ def radix_mask
42
+ ("1"*self.class.bit_density).to_i(2)
43
+ end
44
+
45
+ # Mask string for _chr_ substituting wildcards as necessary
46
+ def mask_chr(chr)
47
+ if chr == config.wildcard
48
+ 0
49
+ else
50
+ radix_mask
51
+ end.to_s(2).rjust(self.class.bit_density, '0')
52
+ end
53
+
54
+ # Converts the `chr` from `config.radix` to binary, substituting wildcards as necessary
55
+ #
56
+ # @param chr [String] character to convert to binary
57
+ # @return [String] binary string
58
+ def bin_chr(chr)
59
+ chr = '0' if chr == config.wildcard
60
+
61
+ if config.radix
62
+ raise "invalid character '#{chr}' for radix=#{config.radix}" if chr.downcase != chr.to_i(config.radix).to_s(config.radix).downcase
63
+ chr.to_i(config.radix)
64
+ else
65
+ chr.ord
66
+ end.to_s(2).rjust(self.class.bit_density, '0')
67
+ end
68
+
69
+ def mask_hex
70
+ hex_encode(map_chr{ |c| mask_chr(c) })
71
+ end
72
+
73
+ def search_hex
74
+ hex_encode(map_chr{ |c| bin_chr(c) })
75
+ end
76
+
77
+ # Transform _bin_str_ to array of 32, 16, and 8 bit hex encoded strings
78
+ def hex_encode(bin_str)
79
+ bin_str.reverse.scan(/.{1,4}/).map{ |chunk|
80
+ chunk.reverse.to_i(2).to_s(16)
81
+ }.reverse.join.scan(/.{8}|.{4}|.{2}/).map{ |hex|
82
+ hex.prepend('0x')
83
+ }
84
+ end
85
+
86
+ def clauses
87
+ start_bit = 0
88
+ [].tap do |filter|
89
+ search_hex.zip(mask_hex).each do |search, mask|
90
+ filter << Packetman::Clause.new(search, mask, start_bit)
91
+ start_bit += self.class.bit_length(search)
92
+ end
93
+ end
94
+ end
95
+
96
+ def to_s
97
+ clauses.map{ |clause| clause.to_s }.join(' && ')
98
+ end
99
+
100
+ end
101
+ end
@@ -1,57 +1,28 @@
1
+ require 'terminal-table'
2
+
1
3
  module Packetman
2
4
  class Table
3
5
  include ConfigMethods
4
6
 
5
- attr_reader :line_h, :line_v
6
- attr_accessor :columns
7
-
8
- def initialize(cols = 32)
9
- @line_v = '|'
10
- @line_h = '-'
11
- @columns = cols
12
- end
13
-
14
- def line_h=(value)
15
- raise "Invalid character" if value.length != 1
16
- @line_h = value
17
- end
18
-
19
- def line_v=(value)
20
- raise "Invalid character" if value.length != 1
21
- @line_v = value
22
- end
23
-
24
- def column_width
25
- (columns-1).to_s.length
26
- end
7
+ def initialize
27
8
 
28
- def horizontal_bar
29
- line_v + line_h*(table_width - 2) + line_v + "\n"
9
+ @term_table = Terminal::Table.new(headings: headings, rows: rows, style: style)
30
10
  end
31
11
 
32
- def table_width
33
- columns*(column_width + 1) + 1
12
+ def headings
13
+ [*0..31].map{ |c| "%02d" % c }
34
14
  end
35
15
 
36
- def cell_size(field_size)
37
- field_size*(column_width + 1) - 1
16
+ def rows
17
+ protocols[config.transport]['table']
38
18
  end
39
19
 
40
- def header_row
41
- line_v + columns.times.map{ |n| sprintf "%0#{column_width}d", n }.join(line_v) + line_v + "\n"
20
+ def style
21
+ { alignment: :center, padding_left: 0, padding_right: 0}
42
22
  end
43
23
 
44
24
  def to_s
45
- output = horizontal_bar + header_row + horizontal_bar
46
-
47
- config.protocols['transport'][config.transport]['table'].each do |label, size|
48
- output += sprintf "%s%.#{cell_size(size)}s", line_v, label.center(cell_size(size))
49
- if output.split("\n").last.length == (table_width - 1)
50
- output += line_v + "\n"
51
- output += horizontal_bar
52
- end
53
- end
54
- output
25
+ @term_table.to_s
55
26
  end
56
27
 
57
28
  end
@@ -1,3 +1,3 @@
1
1
  module Packetman
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.5"
3
3
  end
@@ -19,9 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_development_dependency "bundler", "~> 1"
23
- spec.add_development_dependency "rake", "~> 10.0"
24
- spec.add_development_dependency "rspec", "~> 3.3"
25
- spec.add_development_dependency "pry", "~> 0.10"
26
- spec.add_development_dependency "codeclimate-test-reporter"
22
+ spec.add_dependency "terminal-table", "~> 1.8"
23
+ spec.add_development_dependency "bundler", "~> 2.0"
24
+ spec.add_development_dependency "rake", "~> 13.0"
25
+ spec.add_development_dependency "rspec", "~> 3.9"
26
+ spec.add_development_dependency "simplecov", "~> 0.17"
27
+ spec.add_development_dependency "pry", "~> 0.12"
27
28
  end
metadata CHANGED
@@ -1,85 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: packetman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Scholl
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-09-20 00:00:00.000000000 Z
11
+ date: 2020-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: terminal-table
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - "~>"
18
32
  - !ruby/object:Gem::Version
19
- version: '1'
33
+ version: '2.0'
20
34
  type: :development
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
38
  - - "~>"
25
39
  - !ruby/object:Gem::Version
26
- version: '1'
40
+ version: '2.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '10.0'
47
+ version: '13.0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '10.0'
54
+ version: '13.0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rspec
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '3.3'
61
+ version: '3.9'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '3.3'
68
+ version: '3.9'
55
69
  - !ruby/object:Gem::Dependency
56
- name: pry
70
+ name: simplecov
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: '0.10'
75
+ version: '0.17'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: '0.10'
82
+ version: '0.17'
69
83
  - !ruby/object:Gem::Dependency
70
- name: codeclimate-test-reporter
84
+ name: pry
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - ">="
87
+ - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: '0'
89
+ version: '0.12'
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - ">="
94
+ - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '0'
96
+ version: '0.12'
83
97
  description: Simple tool for creating advanced tcpdump queries, because manually writing
84
98
  `tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420` is no fun.
85
99
  email:
@@ -89,9 +103,10 @@ executables:
89
103
  extensions: []
90
104
  extra_rdoc_files: []
91
105
  files:
106
+ - ".bundle/config"
107
+ - ".circleci/config.yml"
92
108
  - ".gitignore"
93
109
  - ".rspec"
94
- - ".travis.yml"
95
110
  - Gemfile
96
111
  - LICENSE.txt
97
112
  - NOTES.md
@@ -100,17 +115,17 @@ files:
100
115
  - TODO.md
101
116
  - bin/console
102
117
  - bin/setup
103
- - circle.yml
118
+ - config/applications.yml
104
119
  - config/protocols.yml
105
120
  - exe/packetman
106
121
  - lib/packetman.rb
107
- - lib/packetman/compose.rb
122
+ - lib/packetman/clause.rb
108
123
  - lib/packetman/config.rb
109
124
  - lib/packetman/config_methods.rb
125
+ - lib/packetman/filter.rb
110
126
  - lib/packetman/table.rb
111
127
  - lib/packetman/version.rb
112
128
  - packetman.gemspec
113
- - packetman_notes
114
129
  homepage: https://github.com/jescholl/packetman
115
130
  licenses:
116
131
  - MIT
@@ -131,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
146
  version: '0'
132
147
  requirements: []
133
148
  rubyforge_project:
134
- rubygems_version: 2.4.5
149
+ rubygems_version: 2.7.6.2
135
150
  signing_key:
136
151
  specification_version: 4
137
152
  summary: Advanced tcpdump and Wiresharp filter generator.
@@ -1,4 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.2
4
- before_install: gem install bundler -v 1.10.5
data/circle.yml DELETED
@@ -1,3 +0,0 @@
1
- machine:
2
- ruby:
3
- version: 2.1.6
@@ -1,97 +0,0 @@
1
- module Packetman
2
- class Compose
3
- include ConfigMethods
4
-
5
- def initialize(input, radix=config.radix)
6
- @input = input
7
- @radix = radix
8
- end
9
-
10
- def desired_length
11
- ((@input.length + config.offset)/8.to_f).ceil*8 - config.offset
12
- end
13
-
14
- def bit_density(radix=@radix)
15
- (radix.nil?) ? 8 : Math.log2(radix).to_i
16
- end
17
-
18
- def mask
19
- shift(@input.scan(/./).map{ |c| mask_chr(c) }.join)
20
- end
21
-
22
- def radix_mask
23
- ("1"*bit_density).to_i(2)
24
- end
25
-
26
- def shift(input)
27
- input.ljust(desired_length, '0')
28
- end
29
-
30
- def search
31
- shift(@input.scan(/./).map{ |c| bin_chr(c) }.join)
32
- end
33
-
34
- def mask_chr(chr)
35
- if chr == '?'
36
- raise "wildcards not allowed" unless config.allow_wildcards
37
- 0
38
- else
39
- radix_mask
40
- end.to_s(2).rjust(bit_density, '0')
41
- end
42
-
43
- def bin_chr(chr)
44
- if chr == '?'
45
- raise "wildcards not allowed" unless config.allow_wildcards
46
- chr = '0'
47
- end
48
-
49
- if @radix
50
- chr.to_i(@radix)
51
- else
52
- chr.ord
53
- end.to_s(2).rjust(bit_density, '0')
54
- end
55
-
56
- def mask_hex
57
- hex_encode(mask)
58
- end
59
-
60
- def search_hex
61
- hex_encode(search)
62
- end
63
-
64
- def hex_encode(bin_str)
65
- bin_str.reverse.scan(/.{1,4}/).map{ |chunk| chunk.reverse.to_i(2).to_s(16) }.reverse.join.scan(/.{1,8}/).map{ |hex| hex.prepend('0x') }
66
- end
67
-
68
- def full_bit_length(num, radix=nil)
69
- return num.to_i(radix).to_s(radix).length * bit_density(radix) if radix
70
-
71
- case num
72
- when /^0x/
73
- $'.length * bit_density(16)
74
- when /^0b/
75
- $'.length * bit_density(2)
76
- else
77
- nil
78
- end
79
- end
80
-
81
- def start_byte(position)
82
- "#{config.payload_query} + #{(config.offset + position)/8}"
83
- end
84
-
85
- def to_s
86
- clauses = []
87
- position = 0
88
- search_hex.zip(mask_hex).each_with_index do |(hex_search, hex_mask),i|
89
- search_bit_length = full_bit_length(hex_search)
90
- clauses << "#{config.transport}[#{start_byte(position)}:#{search_bit_length/8}] & #{hex_mask} = #{hex_search}"
91
- position += search_bit_length
92
- end
93
-
94
- clauses.join(' && ')
95
- end
96
- end
97
- end
@@ -1,156 +0,0 @@
1
- 7a
2
- 1111010
3
-
4
- #NOTE: I think this is wrong
5
- # also, maybe I don't need to worry about the bits bing aligned on the byte, just on the bit, as long as it's what the user asked for
6
-
7
- hex and string need to be 0 padded on the left
8
- binary needs to fill to the right to hex encode, starting at the offset bit
9
-
10
- Packetman.compose3("0b1111", 3)
11
- 1111
12
- (fill right to ((4+3)/8.to_f).ceil*8 = 8 )
13
- 11110000
14
- (shift mask and search >> 3)
15
- 00011110
16
- 00011110
17
- (sample byte sequence)
18
- 00011111
19
-
20
- Packetman.compose3("0b1111010", 3)
21
- 1111010
22
- (fill right to ((7+3)/8.to_f).ceil*8 = 16)
23
- 11110100 00000000
24
- (shift mask and search >> 3)
25
- 00011111 11000000
26
- 00011110 10000000
27
- (sample byte sequence)
28
- 00011110 10101010
29
-
30
-
31
- Packetman.compose3("0b1111010????????", 3)
32
- 1111010? ???????
33
- (fill right to ((15+3)/8.to_f).ceil*8 = 24)
34
- 1111010? ???????0 00000000
35
- (shift mask and search >> 3)
36
- 00011111 11000000 00000000
37
- 00011110 10000000 00000000
38
- (sample byte sequence)
39
- 00011110 10101010 10101010
40
-
41
- Packetman.compose3("0x123",3)
42
- 100100011
43
- (left fill to 12 first, or manual right fill with 16-12)
44
- (fill right to ((3*4)+3)/8.to_f).ceil*8 = 16)
45
- 00010010 00110000
46
- (shift mask and search >> 3)
47
- 00000011 11111110
48
- 00000010 01000110
49
- (sample byte sequence)
50
- 00000010 01000111
51
-
52
-
53
-
54
-
55
- Packetman.compose3("0xa",3)
56
- "((tcp[0:1] & 0xa) >> 1) = 0xa"
57
- 1010
58
- (generate shifted mask)
59
- 00011110
60
- (generate shifted search)
61
- 00010100
62
- (sample byte sequence)
63
- 10010101
64
-
65
- 0b00011110 & 0b10010101 == 0x14
66
-
67
-
68
-
69
-
70
-
71
-
72
- Packetman.compose3("0b001010010", 3)
73
- "((tcp[0:2] & 0x1ff0) >> 4 = 0x52"
74
- 001010010
75
- (generate shifted mask)
76
- (turn preceding 0s to 1s)
77
- (left shift (((binlen)+3)/8.to_f).ceil*8 - binlen - 3)
78
- 1111111110000
79
- (generate shifted search - same as above)
80
- 0010100100000
81
- (sample byte sequence)
82
- 0010100101010
83
-
84
- 0b0010100101010 & 1111111110000 == 0b0010100100000
85
-
86
-
87
- Packetman.compose3("0b00??1010?111?00010101??10101010?????1010111110???111110101001111010101??????????10100101010100011???????", 5)
88
- 00??1010?111?00010101??10101010?????1010111110???111110101001111010101??????????10100101010100011???????
89
- (generate search str - replace ?s with 0s)
90
- 00001010011100001010100101010100000010101111100001111101010011110101010000000000101001010101000110000000
91
- (generate shifted mask)
92
- (turn preceding 0s to 1s)
93
- (left shift (((binlen)+5)/8.to_f).ceil*8 - binlen - 5)
94
- 11001111011101111111100111111110000011111111110001111111111111111111110000000000111111111111111110000000000
95
- 11001111011101111111100111111110000011111111110001111111111111111111110000000000111111111111111110000000
96
- (generate shifted search - same as above)
97
- 00001010011100001010100101010100000010101111100001111101010011110101010000000000101001010101000110000000000
98
- (sample byte sequence)
99
- 00011010011100001010111101010101100110101111100101111101010011110101010010101001101001010101000110110100101
100
-
101
- 00011010011100001010111101010101100110101111100101111101010011110101010010101001101001010101000110110100101 &
102
- 11001111011101111111100111111110000011111111110001111111111111111111110000000000111111111111111110000000000 ==
103
- 00001010011100001010100101010100000010101111100001111101010011110101010000000000101001010101000110000000000
104
-
105
-
106
- # NOTE
107
-
108
- instead of shifting the product of (mask & input), shift search
109
- this makes the form
110
- "tcp[0:1] & mask = shifted_search"
111
-
112
-
113
- Packetman.compose3("0xa",1)
114
- "((tcp[0:1] & 0x78) >> 3) = 0xa"
115
- 1010
116
- (generate shifted mask)
117
- (left shift (((hexlen*4)+1)/8.to_f).ceil*8 - hexlen*4 - 1)
118
- (use length of mask to determine how much data to take)
119
- 01111000
120
- (sample byte sequence)
121
- 11010101
122
-
123
- (0b01111000 & 0b11010101) >> 3 == 0xa
124
-
125
-
126
- Packetman.compose3("0x123", 3)
127
- "((tcp[0:2] & 0x1ffe) >> 1) = 0x123"
128
- 100100011
129
- (hex: left fill with 1s to hexlen*4)
130
- 111100100011
131
- (generate shifted mask)
132
- (left shift (((hexlen*4)+3)/8.to_f).ceil*8 - hexlen*4 - 3)
133
- (use length of mask to determine how much data to take)
134
- 0001111111111110
135
- (sample byte sequence)
136
- 1000001001000111
137
-
138
- (0b1000001001000111 & 0b0001111111111110) >> 1 == 0x123
139
-
140
- Packetman.compose3("abc", 3)
141
- "((tcp[0:4] & 0x1fffffe0 >> 5) = 0x616263"
142
- 11000010110001001100011
143
- (str: left fill with 1s to len*8)
144
- 111000010110001001100011
145
- (generate shifted mask)
146
- (left shift (((strlen*8)+3)/8.to_f).ceil*8 - strlen*8 - 3)
147
- (use length of mask to determine how much data to take)
148
- 00011111 11111111 11111111 11100000
149
- (sample byte sequence)
150
- 10101100 00101100 01001100 01110101
151
-
152
- (0b10101100001011000100110001110101 & 0b00011111111111111111111111100000) >> 5 == 0b11000010110001001100011
153
-
154
-
155
-
156
-