packetman 0.1.0 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.circleci/config.yml +55 -0
- data/.gitignore +1 -0
- data/README.md +40 -3
- data/config/applications.yml +7 -0
- data/config/protocols.yml +55 -34
- data/exe/packetman +4 -2
- data/lib/packetman.rb +2 -5
- data/lib/packetman/clause.rb +33 -0
- data/lib/packetman/config.rb +37 -43
- data/lib/packetman/config_methods.rb +4 -0
- data/lib/packetman/filter.rb +101 -0
- data/lib/packetman/table.rb +11 -40
- data/lib/packetman/version.rb +1 -1
- data/packetman.gemspec +6 -5
- metadata +36 -21
- data/.travis.yml +0 -4
- data/circle.yml +0 -3
- data/lib/packetman/compose.rb +0 -97
- data/packetman_notes +0 -156
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2820eacc8a12d33ab3ce8799c4c37d350457775f741173b86766fe5e8236951e
|
4
|
+
data.tar.gz: e977ac8d6076e95671ac2ccf0539c14d38653b01395e9fe838bcfdf2c55d76b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/README.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# Packetman
|
2
2
|
|
3
|
-
Advanced tcpdump and Wireshark
|
3
|
+
Advanced tcpdump and Wireshark filter string generator.
|
4
4
|
|
5
|
-
[![
|
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
|
-
|
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
|
|
data/config/protocols.yml
CHANGED
@@ -1,35 +1,56 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
data/exe/packetman
CHANGED
data/lib/packetman.rb
CHANGED
@@ -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/
|
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
|
data/lib/packetman/config.rb
CHANGED
@@ -2,69 +2,63 @@ require 'optparse'
|
|
2
2
|
|
3
3
|
module Packetman
|
4
4
|
class Config
|
5
|
-
attr_accessor :transport, :application, :
|
6
|
-
attr_writer :offset
|
5
|
+
attr_accessor :transport, :application, :offset_type, :radix, :start_with_transport, :offset, :wildcard
|
7
6
|
|
8
7
|
def initialize
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
29
|
+
offset
|
27
30
|
end
|
28
31
|
end
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
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("-
|
43
|
-
opt.on("-a", "--application
|
44
|
-
|
45
|
-
opt.on("-r", "--radix
|
46
|
-
opt.on("-o", "--offset
|
47
|
-
opt.on("-b", "--byte-offset", "Use 8-bit bytes instead of bits for offset") { |v| self.
|
48
|
-
opt.on("-
|
49
|
-
opt.on("--
|
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
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
@@ -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
|
data/lib/packetman/table.rb
CHANGED
@@ -1,57 +1,28 @@
|
|
1
|
+
require 'terminal-table'
|
2
|
+
|
1
3
|
module Packetman
|
2
4
|
class Table
|
3
5
|
include ConfigMethods
|
4
6
|
|
5
|
-
|
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
|
-
|
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
|
33
|
-
|
12
|
+
def headings
|
13
|
+
[*0..31].map{ |c| "%02d" % c }
|
34
14
|
end
|
35
15
|
|
36
|
-
def
|
37
|
-
|
16
|
+
def rows
|
17
|
+
protocols[config.transport]['table']
|
38
18
|
end
|
39
19
|
|
40
|
-
def
|
41
|
-
|
20
|
+
def style
|
21
|
+
{ alignment: :center, padding_left: 0, padding_right: 0}
|
42
22
|
end
|
43
23
|
|
44
24
|
def to_s
|
45
|
-
|
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
|
data/lib/packetman/version.rb
CHANGED
data/packetman.gemspec
CHANGED
@@ -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.
|
23
|
-
spec.add_development_dependency "
|
24
|
-
spec.add_development_dependency "
|
25
|
-
spec.add_development_dependency "
|
26
|
-
spec.add_development_dependency "
|
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.
|
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:
|
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: '
|
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: '
|
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: '
|
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: '
|
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.
|
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.
|
68
|
+
version: '3.9'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: simplecov
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0.
|
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.
|
82
|
+
version: '0.17'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
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
|
-
-
|
118
|
+
- config/applications.yml
|
104
119
|
- config/protocols.yml
|
105
120
|
- exe/packetman
|
106
121
|
- lib/packetman.rb
|
107
|
-
- lib/packetman/
|
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.
|
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.
|
data/.travis.yml
DELETED
data/circle.yml
DELETED
data/lib/packetman/compose.rb
DELETED
@@ -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
|
data/packetman_notes
DELETED
@@ -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
|
-
|