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 +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
|
-
[](http://badge.fury.io/rb/packetman)
|
6
|
+
[](https://codeclimate.com/github/jescholl/packetman/coverage)
|
7
|
+
[](http://inch-ci.org/github/jescholl/packetman)
|
8
|
+
[](https://circleci.com/gh/jescholl/packetman)
|
9
|
+
[](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
|
-
|