packetman 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/packetman.svg)](http://badge.fury.io/rb/packetman)
|
6
6
|
[![Test Coverage](https://codeclimate.com/github/jescholl/packetman/badges/coverage.svg)](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
|