packetman 0.1.2 → 0.1.3
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 +4 -4
- data/README.md +35 -2
- data/config/protocols.yml +47 -20
- data/exe/packetman +4 -2
- data/lib/packetman/clause.rb +15 -8
- data/lib/packetman/config.rb +16 -13
- data/lib/packetman/filter.rb +22 -13
- data/lib/packetman/table.rb +11 -40
- data/lib/packetman/version.rb +1 -1
- data/packetman.gemspec +2 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ee06237cff9b2ffb77984c3d3ed1447e9fe4ba8
|
4
|
+
data.tar.gz: 58b85bd5f40726dbe58271425c6247556659dcfe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cfa7fe1df88846174e93d98003b4399da5b97162b3ccf8c9a23895d87e4d16a69da654c1da5772dd98eb0ce06afe97033b1adbb36913e3e1c470efd3a3dfe964
|
7
|
+
data.tar.gz: 48890d5a660428531b7d17f6338bcba85f142ec6b76ce09519632fddd2a7536f9079432e89065f817a5de5a1e07fc997bd24e3ee5ec9b45c5d70211314353a68
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Packetman
|
2
2
|
|
3
|
-
Advanced tcpdump and Wireshark
|
3
|
+
Advanced tcpdump and Wireshark filter string generator.
|
4
4
|
|
5
5
|
[](http://badge.fury.io/rb/packetman)
|
6
6
|
[](https://codeclimate.com/github/jescholl/packetman/coverage)
|
@@ -38,7 +38,40 @@ Or install it yourself as:
|
|
38
38
|
|
39
39
|
## Usage
|
40
40
|
|
41
|
-
|
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
|
42
75
|
|
43
76
|
## Development
|
44
77
|
|
data/config/protocols.yml
CHANGED
@@ -1,29 +1,56 @@
|
|
1
1
|
---
|
2
2
|
tcp:
|
3
3
|
table:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
16
33
|
payload_query: '((tcp[12:1] & 0xf0) >> 2)'
|
17
34
|
udp:
|
18
35
|
table:
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
23
45
|
payload_query: 8
|
24
46
|
icmp:
|
25
47
|
table:
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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/clause.rb
CHANGED
@@ -2,24 +2,31 @@ module Packetman
|
|
2
2
|
class Clause
|
3
3
|
include ConfigMethods
|
4
4
|
|
5
|
-
attr_accessor :search, :mask, :
|
5
|
+
attr_accessor :search, :mask, :start_bit
|
6
6
|
|
7
|
-
def initialize(search, mask,
|
7
|
+
def initialize(search, mask, start_bit)
|
8
8
|
self.search = search
|
9
9
|
self.mask = mask
|
10
|
-
self.
|
10
|
+
self.start_bit = start_bit
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
# Address of first byte
|
14
|
+
def start_byte
|
15
|
+
[config.payload_query, (config.offset_bits + start_bit)/8].compact.join(' + ')
|
15
16
|
end
|
16
17
|
|
17
|
-
def
|
18
|
-
|
18
|
+
def num_bytes
|
19
|
+
Filter.bit_length(search)/8
|
19
20
|
end
|
20
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
|
21
28
|
def to_s
|
22
|
-
"#{data_address
|
29
|
+
"#{data_address} & #{mask} = #{search}"
|
23
30
|
end
|
24
31
|
|
25
32
|
end
|
data/lib/packetman/config.rb
CHANGED
@@ -2,11 +2,12 @@ require 'optparse'
|
|
2
2
|
|
3
3
|
module Packetman
|
4
4
|
class Config
|
5
|
-
attr_accessor :transport, :application, :
|
5
|
+
attr_accessor :transport, :application, :offset_type, :radix, :start_with_transport, :offset, :wildcard
|
6
6
|
|
7
7
|
def initialize
|
8
|
-
|
9
|
-
|
8
|
+
self.transport = "tcp"
|
9
|
+
self.offset = 0
|
10
|
+
self.offset_type = :bits
|
10
11
|
end
|
11
12
|
|
12
13
|
def protocols
|
@@ -18,7 +19,7 @@ module Packetman
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def offset_bits
|
21
|
-
if
|
22
|
+
if offset_type == :bytes
|
22
23
|
offset*8
|
23
24
|
else
|
24
25
|
offset
|
@@ -28,22 +29,24 @@ module Packetman
|
|
28
29
|
def opts
|
29
30
|
@opts ||= OptionParser.new do |opt|
|
30
31
|
opt.banner = "Usage: #{File.basename($PROGRAM_NAME)} [OPTIONS] FILTER_STRING"
|
31
|
-
opt.on("-p", "--protocol PROTO", protocols.keys, "Transport Protocol (
|
32
|
+
opt.on("-p", "--protocol PROTO", protocols.keys, "Transport Protocol (#{protocols.keys.join(',')})") { |v| self.transport = v }
|
32
33
|
opt.on("-t", "--transport", "OFFSET starts at transport header instead of data payload") { |v| self.start_with_transport = v }
|
33
34
|
opt.on("-r", "--radix RADIX", Integer, "Treat FILTER_STRING as RADIX instead of String") { |v| self.radix = v }
|
34
35
|
opt.on("-o", "--offset OFFSET", Integer, "Offset in bits") { |v| self.offset = v }
|
35
|
-
opt.on("-b", "--byte-offset", "Use 8-bit bytes instead of bits for offset") { |v| self.
|
36
|
-
opt.on("-w", "--wildcard
|
37
|
-
opt.on("
|
36
|
+
opt.on("-b", "--byte-offset", "Use 8-bit bytes instead of bits for offset") { |v| self.offset_type = :bytes if v }
|
37
|
+
opt.on("-w", "--wildcard CHARACTER", "Treat CHARACTER as single-character wildcard") { |v| raise "invalid wildcard" if v.to_s.length > 1; self.wildcard = v }
|
38
|
+
opt.on("--table", "Show transport header table") { puts Packetman::Table.new; throw :exit }
|
39
|
+
opt.on("-v", "--version", "Show version") { puts Packetman::VERSION; throw :exit }
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
43
|
def parse_opts
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
unparsed_opts = opts.parse!
|
45
|
+
if unparsed_opts.length < 1
|
46
|
+
puts opts
|
47
|
+
throw :exit
|
48
|
+
end
|
49
|
+
unparsed_opts.join(" ")
|
47
50
|
end
|
48
51
|
|
49
52
|
end
|
data/lib/packetman/filter.rb
CHANGED
@@ -13,33 +13,36 @@ module Packetman
|
|
13
13
|
case num
|
14
14
|
when /^0x/
|
15
15
|
$'.length * bit_density(16)
|
16
|
-
when /^0b/
|
17
|
-
$'.length * bit_density(2)
|
18
16
|
else
|
19
17
|
nil
|
20
18
|
end
|
21
19
|
end
|
22
20
|
|
23
21
|
def self.bit_density(radix=config.radix)
|
24
|
-
(radix.nil?) ? 8 : Math.log2(radix).
|
22
|
+
(radix.nil?) ? 8 : Math.log2(radix).ceil
|
25
23
|
end
|
26
24
|
|
27
25
|
def map_chr
|
28
|
-
|
26
|
+
shift_and_pad(input.scan(/./).map{ |chr| yield chr }.join)
|
29
27
|
end
|
30
28
|
|
31
|
-
def
|
32
|
-
|
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')
|
33
34
|
end
|
34
35
|
|
35
|
-
def
|
36
|
-
((input.length + config.offset_bits)/8.to_f).ceil*8 - config.offset_bits
|
36
|
+
def target_bit_length
|
37
|
+
((input.length*self.class.bit_density + config.offset_bits)/8.to_f).ceil*8 - config.offset_bits
|
37
38
|
end
|
38
39
|
|
40
|
+
# Mask for 1 character of current radix
|
39
41
|
def radix_mask
|
40
42
|
("1"*self.class.bit_density).to_i(2)
|
41
43
|
end
|
42
44
|
|
45
|
+
# Mask string for _chr_ substituting wildcards as necessary
|
43
46
|
def mask_chr(chr)
|
44
47
|
if chr == config.wildcard
|
45
48
|
0
|
@@ -48,6 +51,7 @@ module Packetman
|
|
48
51
|
end.to_s(2).rjust(self.class.bit_density, '0')
|
49
52
|
end
|
50
53
|
|
54
|
+
# Binary string for _chr_ substituting wildcards as necessary
|
51
55
|
def bin_chr(chr)
|
52
56
|
chr = '0' if chr == config.wildcard
|
53
57
|
|
@@ -67,6 +71,7 @@ module Packetman
|
|
67
71
|
hex_encode(map_chr{ |c| bin_chr(c) })
|
68
72
|
end
|
69
73
|
|
74
|
+
# Transform _bin_str_ to array of 32, 16, and 8 bit hex encoded strings
|
70
75
|
def hex_encode(bin_str)
|
71
76
|
bin_str.reverse.scan(/.{1,4}/).map{ |chunk|
|
72
77
|
chunk.reverse.to_i(2).to_s(16)
|
@@ -75,14 +80,18 @@ module Packetman
|
|
75
80
|
}
|
76
81
|
end
|
77
82
|
|
78
|
-
def
|
79
|
-
|
83
|
+
def clauses
|
84
|
+
start_bit = 0
|
80
85
|
[].tap do |filter|
|
81
86
|
search_hex.zip(mask_hex).each do |search, mask|
|
82
|
-
filter << Packetman::Clause.new(search, mask,
|
83
|
-
|
87
|
+
filter << Packetman::Clause.new(search, mask, start_bit)
|
88
|
+
start_bit += self.class.bit_length(search)
|
84
89
|
end
|
85
|
-
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_s
|
94
|
+
clauses.map{ |clause| clause.to_s }.join(' && ')
|
86
95
|
end
|
87
96
|
|
88
97
|
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
|
-
protocols[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,6 +19,8 @@ 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_dependency "terminal-table", "~> 1.5.2"
|
23
|
+
|
22
24
|
spec.add_development_dependency "bundler", "~> 1"
|
23
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
26
|
spec.add_development_dependency "rspec", "~> 3.3"
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
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.3
|
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-11-
|
11
|
+
date: 2015-11-30 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.5.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.5.2
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|