seccomp-tools 0.1.0 → 1.0.0
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 +42 -4
- data/lib/seccomp-tools/bpf.rb +21 -5
- data/lib/seccomp-tools/cli/base.rb +27 -6
- data/lib/seccomp-tools/cli/cli.rb +3 -1
- data/lib/seccomp-tools/cli/disasm.rb +4 -9
- data/lib/seccomp-tools/cli/dump.rb +4 -4
- data/lib/seccomp-tools/cli/emu.rb +79 -0
- data/lib/seccomp-tools/const.rb +7 -0
- data/lib/seccomp-tools/disasm/context.rb +80 -0
- data/lib/seccomp-tools/disasm/disasm.rb +46 -0
- data/lib/seccomp-tools/dumper.rb +3 -2
- data/lib/seccomp-tools/emulator.rb +155 -0
- data/lib/seccomp-tools/instruction/alu.rb +23 -2
- data/lib/seccomp-tools/instruction/base.rb +25 -0
- data/lib/seccomp-tools/instruction/jmp.rb +21 -6
- data/lib/seccomp-tools/instruction/ld.rb +14 -4
- data/lib/seccomp-tools/instruction/misc.rb +19 -0
- data/lib/seccomp-tools/instruction/ret.rb +10 -2
- data/lib/seccomp-tools/instruction/st.rb +7 -1
- data/lib/seccomp-tools/syscall.rb +12 -1
- data/lib/seccomp-tools/util.rb +13 -4
- data/lib/seccomp-tools/version.rb +1 -1
- metadata +9 -5
- data/lib/seccomp-tools/context.rb +0 -31
- data/lib/seccomp-tools/disasm.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd34a83058ba2aee13d7ddc08a6547fc70eac825
|
4
|
+
data.tar.gz: 0e07f97dfc60b2a21a26a584eadaae86055c3578
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1415f2f48d7a626f3390f0a6705871da51448c92a086c6321bd201e7d5666be1f1691b561d0fbbf91265419905b44052fa1a89d7f59140ff922cba15c737549e
|
7
|
+
data.tar.gz: c5496c8708fb0f88e8dca0b967bcedd5221bc66ccdc0fd2f93f45d3b76ed8e8446677f5694b1c34d2ce2b4bfa18a12fb56e433187c8ece9d2dc7f9f719e21c5b
|
data/README.md
CHANGED
@@ -16,14 +16,16 @@ Some features might be CTF-specific, but still useful for analysis of seccomp in
|
|
16
16
|
* Disasm - Convert bpf to human readable format.
|
17
17
|
- Simple decompile.
|
18
18
|
- Show syscall names.
|
19
|
-
|
19
|
+
* Emu - Emulate seccomp rules.
|
20
20
|
* (TODO) Solve constraints for executing syscalls (e.g. `execve/open/read/write`).
|
21
21
|
* Support multi-architectures.
|
22
22
|
|
23
23
|
## Installation
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
Available on RubyGems.org!
|
26
|
+
```
|
27
|
+
$ gem install seccomp-tools
|
28
|
+
```
|
27
29
|
|
28
30
|
## Command Line Interface
|
29
31
|
|
@@ -36,7 +38,8 @@ $ seccomp-tools --help
|
|
36
38
|
# List of commands:
|
37
39
|
#
|
38
40
|
# dump Automatically dump seccomp bpf from execution file.
|
39
|
-
# disasm
|
41
|
+
# disasm Disassemble seccomp bpf.
|
42
|
+
# emu Emulate seccomp rules.
|
40
43
|
#
|
41
44
|
# See 'seccomp-tools --help <command>' to read about a specific subcommand.
|
42
45
|
|
@@ -139,11 +142,46 @@ $ seccomp-tools disasm spec/data/twctf-2016-diary.bpf
|
|
139
142
|
|
140
143
|
```
|
141
144
|
|
145
|
+
### Emu
|
146
|
+
|
147
|
+
Emulate seccomp given `sys_nr`, `arg0`, `arg1`, etc.
|
148
|
+
```bash
|
149
|
+
$ seccomp-tools emu --help
|
150
|
+
# emu - Emulate seccomp rules.
|
151
|
+
#
|
152
|
+
# Usage: seccomp-tools emu [options] BPF_FILE [sys_nr [arg0 [arg1 ... arg5]]]
|
153
|
+
# -a, --arch ARCH Specify architecture.
|
154
|
+
# Supported architectures are <amd64|i386>.
|
155
|
+
# -q, --[no-]quiet Run quietly, only show emulation result.
|
156
|
+
|
157
|
+
$ seccomp-tools emu spec/data/libseccomp.bpf 0x3
|
158
|
+
# line CODE JT JF K
|
159
|
+
# =================================
|
160
|
+
# 0000: 0x20 0x00 0x00 0x00000004 A = arch
|
161
|
+
# 0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
|
162
|
+
# 0002: 0x20 0x00 0x00 0x00000000 A = sys_number
|
163
|
+
# 0003: 0x35 0x06 0x00 0x40000000 if (A >= 0x40000000) goto 0010
|
164
|
+
# 0004: 0x15 0x04 0x00 0x00000001 if (A == write) goto 0009
|
165
|
+
# 0005: 0x15 0x03 0x00 0x00000003 if (A == close) goto 0009
|
166
|
+
# 0006: 0x15 0x02 0x00 0x00000020 if (A == dup) goto 0009
|
167
|
+
# 0007: 0x15 0x01 0x00 0x0000003c if (A == exit) goto 0009
|
168
|
+
# 0008: 0x06 0x00 0x00 0x00050005 return ERRNO
|
169
|
+
# 0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW
|
170
|
+
# 0010: 0x06 0x00 0x00 0x00000000 return KILL
|
171
|
+
#
|
172
|
+
# return ALLOW at line 0009
|
173
|
+
|
174
|
+
```
|
175
|
+
|
142
176
|
## Screenshots
|
143
177
|
|
144
178
|
### Dump
|
145
179
|

|
146
180
|
|
181
|
+
### Emu
|
182
|
+

|
183
|
+
|
184
|
+

|
147
185
|
|
148
186
|
## I Need You
|
149
187
|
Any suggestion or feature request is welcome!
|
data/lib/seccomp-tools/bpf.rb
CHANGED
@@ -4,10 +4,22 @@ require 'seccomp-tools/instruction/instruction'
|
|
4
4
|
module SeccompTools
|
5
5
|
# Define the +struct sock_filter+, while more powerful.
|
6
6
|
class BPF
|
7
|
-
|
8
|
-
|
7
|
+
# @return [Integer] Line number.
|
8
|
+
attr_reader :line
|
9
|
+
# @return [Integer] BPF code.
|
10
|
+
attr_reader :code
|
11
|
+
# @return [Integer] BPF JT.
|
12
|
+
attr_reader :jt
|
13
|
+
# @return [Integer] BPF JF.
|
14
|
+
attr_reader :jf
|
15
|
+
# @return [Integer] BPF K.
|
16
|
+
attr_reader :k
|
17
|
+
# @return [Symbol] Architecture.
|
18
|
+
attr_reader :arch
|
19
|
+
# @return [Set<Context>] Possible contexts before this instruction.
|
9
20
|
attr_accessor :contexts
|
10
21
|
|
22
|
+
# Instantiate a {BPF} object.
|
11
23
|
# @param [String] raw
|
12
24
|
# One +struct sock_filter+ in bytes, should exactly 8 bytes.
|
13
25
|
# @param [Symbol] arch
|
@@ -22,6 +34,7 @@ module SeccompTools
|
|
22
34
|
@k = io.read(4).unpack('L').first
|
23
35
|
@arch = arch
|
24
36
|
@line = line
|
37
|
+
@contexts = Set.new
|
25
38
|
end
|
26
39
|
|
27
40
|
# Pretty display the disassemble result.
|
@@ -31,12 +44,16 @@ module SeccompTools
|
|
31
44
|
line, code, jt, jf, k, decompile)
|
32
45
|
end
|
33
46
|
|
47
|
+
# Command according to +code+.
|
34
48
|
# @return [Symbol]
|
49
|
+
# See {Const::BPF::COMMAND} for list of commands.
|
35
50
|
def command
|
36
51
|
Const::BPF::COMMAND.invert[code & 7]
|
37
52
|
end
|
38
53
|
|
54
|
+
# Decompile.
|
39
55
|
# @return [String]
|
56
|
+
# Decompile string.
|
40
57
|
def decompile
|
41
58
|
inst.decompile
|
42
59
|
end
|
@@ -49,12 +66,11 @@ module SeccompTools
|
|
49
66
|
# Context after this instruction.
|
50
67
|
# @return [void]
|
51
68
|
def branch(context, &block)
|
52
|
-
# TODO: consider alu
|
53
69
|
inst.branch(context).each(&block)
|
54
70
|
end
|
55
71
|
|
56
|
-
|
57
|
-
|
72
|
+
# Corresponding instruction object.
|
73
|
+
# @return [SeccompTools::Instruction::Base]
|
58
74
|
def inst
|
59
75
|
@inst ||= case command
|
60
76
|
when :alu then SeccompTools::Instruction::ALU
|
@@ -1,10 +1,19 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
|
3
|
+
require 'seccomp-tools/util'
|
4
|
+
|
3
5
|
module SeccompTools
|
4
6
|
module CLI
|
5
7
|
# Base class for handlers.
|
6
8
|
class Base
|
7
|
-
|
9
|
+
# @return [Hash{Symbol => Object}] Options.
|
10
|
+
attr_reader :option
|
11
|
+
# @return [Array<String>] Arguments array.
|
12
|
+
attr_reader :argv
|
13
|
+
|
14
|
+
# Instantiate a {Base} object.
|
15
|
+
# @param [Array<String>] argv
|
16
|
+
# Arguments array.
|
8
17
|
def initialize(argv)
|
9
18
|
@option = {}
|
10
19
|
@argv = argv
|
@@ -22,15 +31,19 @@ module SeccompTools
|
|
22
31
|
end
|
23
32
|
|
24
33
|
# Write data to stdout or file(s).
|
25
|
-
# @
|
26
|
-
#
|
34
|
+
# @yieldreturn [String]
|
35
|
+
# The data to be written.
|
27
36
|
# @return [void]
|
28
|
-
def output
|
37
|
+
def output
|
29
38
|
# if file name not present, just output to stdout.
|
30
|
-
return $stdout.write(
|
39
|
+
return $stdout.write(yield) if option[:ofile].nil?
|
31
40
|
# times of calling output
|
32
41
|
@serial ||= 0
|
33
|
-
|
42
|
+
# Write to file, we should disable colorize
|
43
|
+
enabled = Util.colorize_enabled?
|
44
|
+
Util.disable_color! if enabled
|
45
|
+
IO.binwrite(file_of(option[:ofile], @serial), yield)
|
46
|
+
Util.enable_color! if enabled
|
34
47
|
@serial += 1
|
35
48
|
end
|
36
49
|
|
@@ -62,6 +75,14 @@ module SeccompTools
|
|
62
75
|
def usage
|
63
76
|
self.class.const_get(:USAGE)
|
64
77
|
end
|
78
|
+
|
79
|
+
def option_arch(opt)
|
80
|
+
supported = Util.supported_archs
|
81
|
+
opt.on('-a', '--arch ARCH', supported, 'Specify architecture.',
|
82
|
+
"Supported architectures are <#{supported.join('|')}>.") do |a|
|
83
|
+
option[:arch] = a
|
84
|
+
end
|
85
|
+
end
|
65
86
|
end
|
66
87
|
end
|
67
88
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'seccomp-tools/cli/disasm'
|
2
2
|
require 'seccomp-tools/cli/dump'
|
3
|
+
require 'seccomp-tools/cli/emu'
|
3
4
|
require 'seccomp-tools/version'
|
4
5
|
|
5
6
|
module SeccompTools
|
@@ -8,7 +9,8 @@ module SeccompTools
|
|
8
9
|
# Handled commands
|
9
10
|
COMMANDS = {
|
10
11
|
'dump' => SeccompTools::CLI::Dump,
|
11
|
-
'disasm' => SeccompTools::CLI::Disasm
|
12
|
+
'disasm' => SeccompTools::CLI::Disasm,
|
13
|
+
'emu' => SeccompTools::CLI::Emu
|
12
14
|
}.freeze
|
13
15
|
|
14
16
|
# Main usage message.
|
@@ -1,13 +1,12 @@
|
|
1
1
|
require 'seccomp-tools/cli/base'
|
2
|
-
require 'seccomp-tools/disasm'
|
3
|
-
require 'seccomp-tools/util'
|
2
|
+
require 'seccomp-tools/disasm/disasm'
|
4
3
|
|
5
4
|
module SeccompTools
|
6
5
|
module CLI
|
7
6
|
# Handle 'dump' command.
|
8
7
|
class Disasm < Base
|
9
8
|
# Summary of this command.
|
10
|
-
SUMMARY = '
|
9
|
+
SUMMARY = 'Disassemble seccomp bpf.'.freeze
|
11
10
|
# Usage of this command.
|
12
11
|
USAGE = ('disasm - ' + SUMMARY + "\n\n" + 'Usage: seccomp-tools disasm BPF_FILE [options]').freeze
|
13
12
|
|
@@ -20,11 +19,7 @@ module SeccompTools
|
|
20
19
|
option[:ofile] = o
|
21
20
|
end
|
22
21
|
|
23
|
-
|
24
|
-
opt.on('-a', '--arch ARCH', supported, 'Specify architecture.',
|
25
|
-
"Supported architectures are <#{supported.join('|')}>.") do |a|
|
26
|
-
option[:arch] = a
|
27
|
-
end
|
22
|
+
option_arch(opt)
|
28
23
|
end
|
29
24
|
end
|
30
25
|
|
@@ -34,7 +29,7 @@ module SeccompTools
|
|
34
29
|
return unless super
|
35
30
|
option[:ifile] = argv.shift
|
36
31
|
return CLI.show(parser.help) if option[:ifile].nil?
|
37
|
-
output
|
32
|
+
output { SeccompTools::Disasm.disasm(IO.binread(option[:ifile]), arch: option[:arch]) }
|
38
33
|
end
|
39
34
|
end
|
40
35
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'seccomp-tools/cli/base'
|
2
|
-
require 'seccomp-tools/disasm'
|
2
|
+
require 'seccomp-tools/disasm/disasm'
|
3
3
|
require 'seccomp-tools/dumper'
|
4
4
|
|
5
5
|
module SeccompTools
|
@@ -55,9 +55,9 @@ module SeccompTools
|
|
55
55
|
option[:command] = argv.shift unless argv.empty?
|
56
56
|
SeccompTools::Dumper.dump('/bin/sh', '-c', option[:command], limit: option[:limit]) do |bpf, arch|
|
57
57
|
case option[:format]
|
58
|
-
when :inspect then output
|
59
|
-
when :raw then output
|
60
|
-
when :disasm then output
|
58
|
+
when :inspect then output { '"' + bpf.bytes.map { |b| format('\\x%02X', b) }.join + "\"\n" }
|
59
|
+
when :raw then output { bpf }
|
60
|
+
when :disasm then output { SeccompTools::Disasm.disasm(bpf, arch: arch) }
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'seccomp-tools/cli/base'
|
4
|
+
require 'seccomp-tools/disasm/disasm'
|
5
|
+
require 'seccomp-tools/emulator'
|
6
|
+
require 'seccomp-tools/util'
|
7
|
+
|
8
|
+
module SeccompTools
|
9
|
+
module CLI
|
10
|
+
# Handle 'emu' command.
|
11
|
+
class Emu < Base
|
12
|
+
# Summary of this command.
|
13
|
+
SUMMARY = 'Emulate seccomp rules.'.freeze
|
14
|
+
# Usage of this command.
|
15
|
+
USAGE = ('emu - ' +
|
16
|
+
SUMMARY +
|
17
|
+
"\n\n" \
|
18
|
+
'Usage: seccomp-tools emu [options] BPF_FILE [sys_nr [arg0 [arg1 ... arg5]]]').freeze
|
19
|
+
|
20
|
+
def initialize(*)
|
21
|
+
super
|
22
|
+
option[:verbose] = 1
|
23
|
+
end
|
24
|
+
|
25
|
+
# Define option parser.
|
26
|
+
# @return [OptionParser]
|
27
|
+
def parser
|
28
|
+
@parser ||= OptionParser.new do |opt|
|
29
|
+
opt.banner = usage
|
30
|
+
|
31
|
+
option_arch(opt)
|
32
|
+
|
33
|
+
opt.on('-q', '--[no-]quiet', 'Run quietly, only show emulation result.') do |v|
|
34
|
+
option[:verbose] = 0 if v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Handle options.
|
40
|
+
# @return [void]
|
41
|
+
def handle
|
42
|
+
return unless super
|
43
|
+
option[:ifile] = argv.shift
|
44
|
+
return CLI.show(parser.help) if option[:ifile].nil?
|
45
|
+
raw = IO.binread(option[:ifile])
|
46
|
+
insts = SeccompTools::Disasm.to_bpf(raw, option[:arch]).map(&:inst)
|
47
|
+
disasm = SeccompTools::Disasm.disasm(raw, arch: option[:arch])
|
48
|
+
sys, *args = argv
|
49
|
+
sys = Integer(sys) if sys
|
50
|
+
args.map! { |v| Integer(v) }
|
51
|
+
trace = Set.new
|
52
|
+
res = SeccompTools::Emulator.new(insts, sys_nr: sys, args: args, arch: option[:arch]).run do |ctx|
|
53
|
+
trace << ctx[:pc]
|
54
|
+
end
|
55
|
+
|
56
|
+
if option[:verbose] >= 1
|
57
|
+
disasm = disasm.lines
|
58
|
+
output { disasm.shift }
|
59
|
+
output { disasm.shift }
|
60
|
+
disasm.each_with_index do |line, idx|
|
61
|
+
output do
|
62
|
+
next line if trace.member?(idx)
|
63
|
+
Util.colorize(line, t: :gray)
|
64
|
+
end
|
65
|
+
# Too much remain, omit them.
|
66
|
+
rem = disasm.size - idx - 1
|
67
|
+
break output { Util.colorize("... (omitting #{rem} lines)\n", t: :gray) } if rem > 3 && idx > res[:pc] + 4
|
68
|
+
end
|
69
|
+
output { "\n" }
|
70
|
+
end
|
71
|
+
output do
|
72
|
+
ret_type = Const::BPF::ACTION.invert[res[:ret] & 0x7fff0000]
|
73
|
+
errno = ret_type == :ERRNO ? "(#{res[:ret] & 0xffff})" : ''
|
74
|
+
format("return %s%s at line %04d\n", ret_type, errno, res[:pc])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/seccomp-tools/const.rb
CHANGED
@@ -87,11 +87,18 @@ module SeccompTools
|
|
87
87
|
module_function
|
88
88
|
|
89
89
|
# To dynamically fetch constants from files.
|
90
|
+
# @param [Symbol] cons
|
91
|
+
# Name of const.
|
92
|
+
# @return [Object]
|
93
|
+
# Value of that +cons+.
|
90
94
|
def const_missing(cons)
|
91
95
|
load_const(cons) || super
|
92
96
|
end
|
93
97
|
|
94
98
|
# Load from file and define const value.
|
99
|
+
# @param [Symbol] cons
|
100
|
+
# Name of const.
|
101
|
+
# @return [Object]
|
95
102
|
def load_const(cons)
|
96
103
|
arch = cons.to_s.downcase
|
97
104
|
filename = File.join(__dir__, 'consts', "#{arch}.rb")
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module SeccompTools
|
2
|
+
module Disasm
|
3
|
+
# Context for disassembler to analyze.
|
4
|
+
#
|
5
|
+
# This context only care if +reg/mem+ can be one of +data[*]+.
|
6
|
+
class Context
|
7
|
+
# @return [Hash{Integer, Symbol => Integer?}] Records reg and mem values.
|
8
|
+
attr_reader :values
|
9
|
+
|
10
|
+
# Instantiate a {Context} object.
|
11
|
+
# @param [Integer?] a
|
12
|
+
# Value to be set to +A+ register.
|
13
|
+
# @param [Integer?] x
|
14
|
+
# Value to be set to +X+ register.
|
15
|
+
# @param [Hash{Integer => Integer?}] mem
|
16
|
+
# Value to be set to +mem+.
|
17
|
+
def initialize(a: nil, x: nil, mem: {})
|
18
|
+
@values = mem
|
19
|
+
16.times { |i| @values[i] ||= nil } # make @values always has all keys
|
20
|
+
@values[:a] = a
|
21
|
+
@values[:x] = x
|
22
|
+
end
|
23
|
+
|
24
|
+
# Implement a deep dup.
|
25
|
+
# @return [Context]
|
26
|
+
def dup
|
27
|
+
Context.new(a: a, x: x, mem: values.dup)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Register A.
|
31
|
+
# @return [Integer?]
|
32
|
+
def a
|
33
|
+
values[:a]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Register X.
|
37
|
+
# @return [Integer?]
|
38
|
+
def x
|
39
|
+
values[:x]
|
40
|
+
end
|
41
|
+
|
42
|
+
# For conveniently get instance variable.
|
43
|
+
# @param [String, Symbol, Integer] key
|
44
|
+
# @return [Integer?]
|
45
|
+
def [](key)
|
46
|
+
return values[key] if key.is_a?(Integer) # mem
|
47
|
+
values[key.downcase.to_sym]
|
48
|
+
end
|
49
|
+
|
50
|
+
# For conveniently set instance variable.
|
51
|
+
# @param [#downcase, Integer] key
|
52
|
+
# Can be +'A', 'a', :a, 'X', 'x', :x+ or an integer.
|
53
|
+
# @param [Integer?] val
|
54
|
+
# Value to set.
|
55
|
+
# @return [void]
|
56
|
+
def []=(key, val)
|
57
|
+
if key.is_a?(Integer)
|
58
|
+
raise RangeError, "Expect 0 <= key < 16, got #{key}." unless key.between?(0, 15)
|
59
|
+
raise RangeError, "Expect 0 <= val < 64, got #{val}." unless val.nil? || val.between?(0, 63)
|
60
|
+
values[key] = val
|
61
|
+
else
|
62
|
+
values[key.downcase.to_sym] = val
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# For +Set+ to compare two {Context} object.
|
67
|
+
# @param [Context] other
|
68
|
+
# @return [Boolean]
|
69
|
+
def eql?(other)
|
70
|
+
values.eql?(other.values)
|
71
|
+
end
|
72
|
+
|
73
|
+
# For +Set+ to get hash key.
|
74
|
+
# @return [Integer]
|
75
|
+
def hash
|
76
|
+
values.hash
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'seccomp-tools/bpf'
|
4
|
+
require 'seccomp-tools/disasm/context'
|
5
|
+
require 'seccomp-tools/util'
|
6
|
+
|
7
|
+
module SeccompTools
|
8
|
+
# Disassembler of seccomp bpf.
|
9
|
+
module Disasm
|
10
|
+
module_function
|
11
|
+
|
12
|
+
# Disassemble bpf codes.
|
13
|
+
# @param [String] raw
|
14
|
+
# The raw bpf bytes.
|
15
|
+
# @param [Symbol] arch
|
16
|
+
# Architecture.
|
17
|
+
def disasm(raw, arch: nil)
|
18
|
+
codes = to_bpf(raw, arch)
|
19
|
+
contexts = Array.new(codes.size) { Set.new }
|
20
|
+
contexts[0].add(Context.new)
|
21
|
+
# all we care is if A is exactly one of data[*]
|
22
|
+
dis = codes.zip(contexts).map do |code, ctxs|
|
23
|
+
ctxs.each do |ctx|
|
24
|
+
code.branch(ctx) do |pc, c|
|
25
|
+
contexts[pc].add(c) unless pc >= contexts.size
|
26
|
+
end
|
27
|
+
end
|
28
|
+
code.contexts = ctxs
|
29
|
+
code.disasm
|
30
|
+
end.join("\n")
|
31
|
+
<<EOS + dis + "\n"
|
32
|
+
line CODE JT JF K
|
33
|
+
=================================
|
34
|
+
EOS
|
35
|
+
end
|
36
|
+
|
37
|
+
# Convert raw bpf string to array of {BPF}.
|
38
|
+
# @param [String] raw
|
39
|
+
# @param [Symbol] arch
|
40
|
+
# @return [Array<BPF>]
|
41
|
+
def to_bpf(raw, arch)
|
42
|
+
arch ||= Util.system_arch
|
43
|
+
raw.scan(/.{8}/m).map.with_index { |b, i| BPF.new(b, arch, i) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/seccomp-tools/dumper.rb
CHANGED
@@ -30,8 +30,6 @@ module SeccompTools
|
|
30
30
|
# dump('spec/binary/twctf-2016-diary') { |c| c[0, 10] }
|
31
31
|
# #=> [" \x00\x00\x00\x00\x00\x00\x00\x15\x00"]
|
32
32
|
# @todo
|
33
|
-
# Detect execution file architecture to know which syscall number should be traced.
|
34
|
-
# @todo
|
35
33
|
# +timeout+ option.
|
36
34
|
def dump(*args, limit: 1, &block)
|
37
35
|
pid = fork { handle_child(*args) }
|
@@ -40,6 +38,9 @@ module SeccompTools
|
|
40
38
|
|
41
39
|
# Do the tracer things.
|
42
40
|
class Handler
|
41
|
+
# Instantiate a {Handler} object.
|
42
|
+
# @param [Integer] pid
|
43
|
+
# The process id after fork.
|
43
44
|
def initialize(pid)
|
44
45
|
Process.waitpid(pid)
|
45
46
|
opt = Ptrace::O_TRACESYSGOOD | Ptrace::O_TRACECLONE | Ptrace::O_TRACEFORK | Ptrace::O_TRACEVFORK
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'seccomp-tools/const'
|
2
|
+
|
3
|
+
module SeccompTools
|
4
|
+
# For emulation seccomp.
|
5
|
+
class Emulator
|
6
|
+
# Instantiate a {Emulator} object.
|
7
|
+
#
|
8
|
+
# All parameters except +instructions+ are optional, while a warning will be shown if unset data being accessed.
|
9
|
+
# @param [Array<Instruction::Base>] instructions
|
10
|
+
# @param [Integer] sys_nr
|
11
|
+
# Syscall number.
|
12
|
+
# @param [Array<Integer>] args
|
13
|
+
# Syscall arguments
|
14
|
+
# @param [Integer] instruction_pointer
|
15
|
+
# Program counter address when this syscall invoked.
|
16
|
+
# @param [Symbol] arch
|
17
|
+
# If not given, use system architecture as default.
|
18
|
+
#
|
19
|
+
# See {SeccompTools::Util.supported_archs} for list of supported architectures.
|
20
|
+
def initialize(instructions, sys_nr: nil, args: [], instruction_pointer: nil, arch: nil)
|
21
|
+
@instructions = instructions
|
22
|
+
@sys_nr = sys_nr
|
23
|
+
@args = args
|
24
|
+
@ip = instruction_pointer
|
25
|
+
@arch = audit(arch || Util.system_arch)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Run emulation!
|
29
|
+
# @return [Hash{Symbol, Integer => Integer}]
|
30
|
+
def run
|
31
|
+
@values = { pc: 0 }
|
32
|
+
loop do
|
33
|
+
break if @values[:ret] # break when returned
|
34
|
+
yield(@values) if block_given?
|
35
|
+
inst = @instructions[pc]
|
36
|
+
op, *args = inst.symbolize
|
37
|
+
case op
|
38
|
+
when :ret then ret(args.first) # ret
|
39
|
+
when :ld then ld(args[0], args[1]) # ld/ldx
|
40
|
+
when :st then st(args[0], args[1]) # st/stx
|
41
|
+
when :jmp then jmp(args[0]) # directly jmp
|
42
|
+
when :cmp then cmp(*args[0, 4]) # jmp with comparsion
|
43
|
+
when :alu then alu(args[0], args[1]) # alu
|
44
|
+
when :misc then misc(args[0]) # misc: txa/tax
|
45
|
+
end
|
46
|
+
set(:pc, get(:pc) + 1) if %i[ld st alu misc].include?(op)
|
47
|
+
end
|
48
|
+
@values
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def pc
|
54
|
+
@values[:pc]
|
55
|
+
end
|
56
|
+
|
57
|
+
def audit(arch)
|
58
|
+
type = case arch
|
59
|
+
when :amd64 then 'ARCH_X86_64'
|
60
|
+
when :i386 then 'ARCH_I386'
|
61
|
+
end
|
62
|
+
Const::Audit::ARCH[type]
|
63
|
+
end
|
64
|
+
|
65
|
+
def ret(num)
|
66
|
+
set(:ret, num == :a ? get(:a) : num)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param [:a, :x] dst
|
70
|
+
# @param [{rel: <:mem, :immi, :data>, val: Integer}] src
|
71
|
+
def ld(dst, src)
|
72
|
+
val = case src[:rel]
|
73
|
+
when :immi then src[:val]
|
74
|
+
when :mem then get(:mem, src[:val])
|
75
|
+
when :data then get(:data, src[:val])
|
76
|
+
end
|
77
|
+
set(dst, val)
|
78
|
+
end
|
79
|
+
|
80
|
+
def st(reg, index)
|
81
|
+
raise IndexError, "Expect 0 <= index < 16, got: #{index}" unless index.between?(0, 15)
|
82
|
+
set(:mem, index, get(reg))
|
83
|
+
end
|
84
|
+
|
85
|
+
def jmp(k)
|
86
|
+
set(:pc, get(:pc) + k + 1)
|
87
|
+
end
|
88
|
+
|
89
|
+
def cmp(op, src, jt, jf)
|
90
|
+
src = get(:x) if src == :x
|
91
|
+
a = get(:a)
|
92
|
+
val = a.send(op, src)
|
93
|
+
val = (val != 0) if val.is_a?(Integer) # handle & operator
|
94
|
+
j = val ? jt : jf
|
95
|
+
set(:pc, get(:pc) + j + 1)
|
96
|
+
end
|
97
|
+
|
98
|
+
def alu(op, src)
|
99
|
+
if op == :neg
|
100
|
+
set(:a, 2**32 - get(:a))
|
101
|
+
else
|
102
|
+
src = get(:x) if src == :x
|
103
|
+
set(:a, get(:a).send(op, src))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def misc(op)
|
108
|
+
case op
|
109
|
+
when :txa then set(:a, get(:x))
|
110
|
+
when :tax then set(:x, get(:a))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def set(*arg, val)
|
115
|
+
if arg.size == 1
|
116
|
+
arg = arg.first
|
117
|
+
raise ArgumentError, "Invalid #{arg}" unless %i[a x pc ret].include?(arg)
|
118
|
+
@values[arg] = val & 0xffffffff
|
119
|
+
else
|
120
|
+
raise ArgumentError, arg.to_s unless arg.first == :mem
|
121
|
+
raise IndexError, "Invalid index: #{arg[1]}" unless arg[1].between?(0, 15)
|
122
|
+
@values[arg[1]] = val & 0xffffffff
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def get(*arg)
|
127
|
+
if arg.size == 1
|
128
|
+
arg = arg.first
|
129
|
+
raise ArgumentError, "Invalid #{arg}" unless %i[a x pc ret].include?(arg)
|
130
|
+
undefined(arg.upcase) if @values[arg].nil?
|
131
|
+
return @values[arg]
|
132
|
+
end
|
133
|
+
return @values[arg[1]] if arg.first == :mem
|
134
|
+
data_of(arg[1])
|
135
|
+
end
|
136
|
+
|
137
|
+
def data_of(index)
|
138
|
+
raise IndexError, "Invalid index: #{index}" unless (index & 3).zero? && index.between?(0, 63)
|
139
|
+
index /= 4
|
140
|
+
case index
|
141
|
+
when 0 then @sys_nr || undefined('sys_number')
|
142
|
+
when 1 then @arch || undefined('arch')
|
143
|
+
when 2 then @ip & 0xffffffff || undefined('instruction_pointer')
|
144
|
+
when 3 then @ip >> 32 || undefined('instruction_pointer')
|
145
|
+
else
|
146
|
+
val = @args[(index - 4) / 2] || undefined("args[#{(index - 4) / 2}]")
|
147
|
+
(val >> (index.even? ? 0 : 32)) & 0xffffffff
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def undefined(var)
|
152
|
+
raise format("Undefined Variable\n\t%04d: %s <- `%s` is undefined", pc, @instructions[pc].decompile, var)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -7,7 +7,24 @@ module SeccompTools
|
|
7
7
|
# Decompile instruction.
|
8
8
|
def decompile
|
9
9
|
return 'A = -A' if op == :neg
|
10
|
-
"A #{op_sym}= #{
|
10
|
+
"A #{op_sym}= #{src_str}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# See {Instruction::Base#symbolize}.
|
14
|
+
# @return [[:alu, Symbol, (:x, Integer, nil)]]
|
15
|
+
def symbolize
|
16
|
+
return [:alu, :neg, nil] if op == :neg
|
17
|
+
[:alu, op_sym, src]
|
18
|
+
end
|
19
|
+
|
20
|
+
# See {Base#branch}.
|
21
|
+
# @param [Context] context
|
22
|
+
# Current context.
|
23
|
+
# @return [Array<(Integer, Context)>]
|
24
|
+
def branch(context)
|
25
|
+
ctx = context.dup
|
26
|
+
ctx[:a] = nil
|
27
|
+
[[line + 1, ctx]]
|
11
28
|
end
|
12
29
|
|
13
30
|
private
|
@@ -34,8 +51,12 @@ module SeccompTools
|
|
34
51
|
end
|
35
52
|
end
|
36
53
|
|
54
|
+
def src_str
|
55
|
+
src == :x ? 'X' : src.to_s
|
56
|
+
end
|
57
|
+
|
37
58
|
def src
|
38
|
-
SRC.invert[code & 0x8] == :k ? k :
|
59
|
+
SRC.invert[code & 0x8] == :k ? k : :x
|
39
60
|
end
|
40
61
|
end
|
41
62
|
end
|
@@ -7,17 +7,42 @@ module SeccompTools
|
|
7
7
|
class Base
|
8
8
|
include SeccompTools::Const::BPF
|
9
9
|
|
10
|
+
# Instantiate a {Base} object.
|
10
11
|
# @param [SeccompTools::BPF] bpf
|
11
12
|
# An instruction.
|
12
13
|
def initialize(bpf)
|
13
14
|
@bpf = bpf
|
14
15
|
end
|
15
16
|
|
17
|
+
# Helper to raise exception with message.
|
18
|
+
# @param [String] msg
|
19
|
+
# Error message.
|
16
20
|
# @raise [ArgumentError]
|
17
21
|
def invalid(msg = 'unknown')
|
18
22
|
raise ArgumentError, "Line #{line} is invalid: #{msg}"
|
19
23
|
end
|
20
24
|
|
25
|
+
# Return the possible branches after executing this instruction.
|
26
|
+
# @param [Context] _context
|
27
|
+
# Current context.
|
28
|
+
# @return [Array<(Integer, Context)>]
|
29
|
+
# @example
|
30
|
+
# # For ALU, LD, LDX, ST, STX
|
31
|
+
# inst.line #=> 10
|
32
|
+
# inst.branch(ctx)
|
33
|
+
# #=> [[11, ctx]]
|
34
|
+
def branch(_context); raise NotImplmentedError
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return tokens stand for this instruction.
|
38
|
+
# @return [Array<Symbol, Integer>]
|
39
|
+
# @example
|
40
|
+
# ret_a.symbolize #=> [:ret, :a]
|
41
|
+
# ret_k.symbolize #=> [:ret, 0x7fff0000]
|
42
|
+
# jeq.symbolize #=> [:cmp, :==, 0, 0, 1]
|
43
|
+
def symbolize; raise NotImplmentedError
|
44
|
+
end
|
45
|
+
|
21
46
|
private
|
22
47
|
|
23
48
|
%i(code jt jf k arch line contexts).each do |sym|
|
@@ -19,6 +19,16 @@ module SeccompTools
|
|
19
19
|
if_str(true) + goto(jf)
|
20
20
|
end
|
21
21
|
|
22
|
+
# See {Instruction::Base#symbolize}.
|
23
|
+
# @return [[:cmp, Symbol, (:x, Integer), Integer, Integer], [:jmp, Integer]]
|
24
|
+
def symbolize
|
25
|
+
return [:jmp, k] if jop == :none
|
26
|
+
[:cmp, jop, src, jt, jf]
|
27
|
+
end
|
28
|
+
|
29
|
+
# See {Base#branch}.
|
30
|
+
# @param [Context] context
|
31
|
+
# Current context.
|
22
32
|
# @return [Array<(Integer, Context)>]
|
23
33
|
def branch(context)
|
24
34
|
return [[at(k), context]] if jop == :none
|
@@ -40,19 +50,24 @@ module SeccompTools
|
|
40
50
|
end
|
41
51
|
|
42
52
|
def src_str
|
43
|
-
return 'X' if
|
53
|
+
return 'X' if src == :x
|
44
54
|
# if A in all contexts are same
|
45
55
|
a = contexts.map(&:a).uniq
|
46
56
|
return k.to_s if a.size != 1
|
47
57
|
a = a[0]
|
48
|
-
return k.to_s
|
49
|
-
|
50
|
-
|
51
|
-
when
|
52
|
-
|
58
|
+
return k.to_s if a.nil?
|
59
|
+
hex = '0x' + k.to_s(16)
|
60
|
+
case a
|
61
|
+
when 0 then Util.colorize(Const::Syscall.const_get(arch.upcase.to_sym).invert[k] || hex, t: :syscall)
|
62
|
+
when 4 then Util.colorize(Const::Audit::ARCH.invert[k] || hex, t: :arch)
|
63
|
+
else hex
|
53
64
|
end
|
54
65
|
end
|
55
66
|
|
67
|
+
def src
|
68
|
+
SRC.invert[code & 8] == :x ? :x : k
|
69
|
+
end
|
70
|
+
|
56
71
|
def goto(off)
|
57
72
|
format('goto %04d', at(off))
|
58
73
|
end
|
@@ -7,26 +7,35 @@ module SeccompTools
|
|
7
7
|
# Decompile instruction.
|
8
8
|
def decompile
|
9
9
|
ret = reg + ' = '
|
10
|
-
type =
|
10
|
+
_, _reg, type = symbolize
|
11
11
|
return ret + type[:val].to_s if type[:rel] == :immi
|
12
12
|
return ret + "mem[#{type[:val]}]" if type[:rel] == :mem
|
13
13
|
ret + seccomp_data_str
|
14
14
|
end
|
15
15
|
|
16
|
+
# @return [void]
|
17
|
+
def symbolize
|
18
|
+
type = load_val
|
19
|
+
[:ld, reg.downcase.to_sym, type]
|
20
|
+
end
|
21
|
+
|
16
22
|
# Accumulator register.
|
17
23
|
# @return ['A']
|
18
24
|
def reg
|
19
25
|
'A'
|
20
26
|
end
|
21
27
|
|
28
|
+
# See {Base#branch}.
|
29
|
+
# @param [Context] context
|
30
|
+
# Current context.
|
22
31
|
# @return [Array<(Integer, Context)>]
|
23
32
|
def branch(context)
|
24
33
|
nctx = context.dup
|
25
34
|
type = load_val
|
26
35
|
nctx[reg] = case type[:rel]
|
27
|
-
when :immi then
|
28
|
-
when :mem then context
|
29
|
-
when :data then
|
36
|
+
when :immi then nil
|
37
|
+
when :mem then context[type[:val]]
|
38
|
+
when :data then type[:val]
|
30
39
|
end
|
31
40
|
[[line + 1, nctx]]
|
32
41
|
end
|
@@ -58,6 +67,7 @@ module SeccompTools
|
|
58
67
|
when 0 then 'sys_number'
|
59
68
|
when 4 then 'arch'
|
60
69
|
when 8 then 'instruction_pointer'
|
70
|
+
when 12 then 'instruction_pointer >> 32'
|
61
71
|
else
|
62
72
|
idx = Array.new(12) { |i| i * 4 + 16 }.index(k)
|
63
73
|
return 'INVALID' if idx.nil?
|
@@ -12,6 +12,25 @@ module SeccompTools
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
# See {Instruction::Base#symbolize}.
|
16
|
+
# @return [[:misc, (:tax, :txa)]]
|
17
|
+
def symbolize
|
18
|
+
[:misc, op]
|
19
|
+
end
|
20
|
+
|
21
|
+
# See {Base#branch}.
|
22
|
+
# @param [Context] context
|
23
|
+
# Current context.
|
24
|
+
# @return [Array<(Integer, Context)>]
|
25
|
+
def branch(context)
|
26
|
+
ctx = context.dup
|
27
|
+
case op
|
28
|
+
when :txa then ctx['A'] = ctx['X']
|
29
|
+
when :tax then ctx['X'] = ctx['A']
|
30
|
+
end
|
31
|
+
[[line + 1, ctx]]
|
32
|
+
end
|
33
|
+
|
15
34
|
private
|
16
35
|
|
17
36
|
def op
|
@@ -6,11 +6,19 @@ module SeccompTools
|
|
6
6
|
class RET < Base
|
7
7
|
# Decompile instruction.
|
8
8
|
def decompile
|
9
|
-
|
10
|
-
"return #{ACTION.invert[
|
9
|
+
_, type = symbolize
|
10
|
+
"return #{type == :a ? 'A' : ACTION.invert[type & 0x7fff0000]}"
|
11
11
|
end
|
12
12
|
|
13
|
+
# See {Instruction::Base#symbolize}.
|
14
|
+
# @return [[:ret, (:a, Integer)]]
|
15
|
+
def symbolize
|
16
|
+
[:ret, code & 0x18 == SRC[:a] ? :a : k]
|
17
|
+
end
|
18
|
+
|
19
|
+
# See {Base#branch}.
|
13
20
|
# @return [[]]
|
21
|
+
# Always return an empty array.
|
14
22
|
def branch(*)
|
15
23
|
[]
|
16
24
|
end
|
@@ -9,10 +9,16 @@ module SeccompTools
|
|
9
9
|
"mem[#{k}] = #{reg}"
|
10
10
|
end
|
11
11
|
|
12
|
+
# See {Instruction::Base#symbolize}.
|
13
|
+
# @return [[:misc, (:a, :x), Integer]]
|
14
|
+
def symbolize
|
15
|
+
[:st, reg.downcase.to_sym, k]
|
16
|
+
end
|
17
|
+
|
12
18
|
# @return [Array<(Integer, Context)>]
|
13
19
|
def branch(context)
|
14
20
|
ctx = context.dup
|
15
|
-
ctx
|
21
|
+
ctx[k] = ctx[reg]
|
16
22
|
[[line + 1, ctx]]
|
17
23
|
end
|
18
24
|
end
|
@@ -10,7 +10,18 @@ module SeccompTools
|
|
10
10
|
i386: { number: 120, args: [40, 88, 96, 104, 112, 32], ret: 80, SYS_prctl: 172 }
|
11
11
|
}.freeze
|
12
12
|
|
13
|
-
|
13
|
+
# @return [Integer] Process id.
|
14
|
+
attr_reader :pid
|
15
|
+
# @return [Hash{Symbol => Integer, Array<Integer>}] See {ABI}.
|
16
|
+
attr_reader :abi
|
17
|
+
# @return [Integer] Syscall number.
|
18
|
+
attr_reader :number
|
19
|
+
# @return [Integer] Syscall arguments.
|
20
|
+
attr_reader :args
|
21
|
+
# @return [Integer] Syscall return value.
|
22
|
+
attr_reader :ret
|
23
|
+
|
24
|
+
# Instantiate a {Syscall} object.
|
14
25
|
# @param [String] pid
|
15
26
|
# Process-id.
|
16
27
|
def initialize(pid)
|
data/lib/seccomp-tools/util.rb
CHANGED
@@ -20,6 +20,14 @@ module SeccompTools
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
# Enable colorize.
|
24
|
+
# @return [void]
|
25
|
+
def enable_color!
|
26
|
+
@disable_color = false
|
27
|
+
end
|
28
|
+
|
29
|
+
# Disable colorize.
|
30
|
+
# @return [void]
|
23
31
|
def disable_color!
|
24
32
|
@disable_color = true
|
25
33
|
end
|
@@ -34,13 +42,14 @@ module SeccompTools
|
|
34
42
|
COLOR_CODE = {
|
35
43
|
esc_m: "\e[0m",
|
36
44
|
syscall: "\e[38;5;120m", # light green
|
37
|
-
arch: "\e[38;5;230m" # light yellow
|
45
|
+
arch: "\e[38;5;230m", # light yellow
|
46
|
+
gray: "\e[2m"
|
38
47
|
}.freeze
|
39
48
|
# Wrapper color codes.
|
40
49
|
# @param [String] s
|
41
50
|
# Contents to wrapper.
|
42
|
-
# @param [Symbol?]
|
43
|
-
# Specific which kind of color to use, valid symbols are defined in
|
51
|
+
# @param [Symbol?] t
|
52
|
+
# Specific which kind of color to use, valid symbols are defined in {Util.COLOR_CODE}.
|
44
53
|
# @return [String]
|
45
54
|
# Wrapper with color codes.
|
46
55
|
def colorize(s, t: nil)
|
@@ -48,7 +57,7 @@ module SeccompTools
|
|
48
57
|
return s unless colorize_enabled?
|
49
58
|
cc = COLOR_CODE
|
50
59
|
color = cc[t]
|
51
|
-
"#{color}#{s.sub(cc[:esc_m], color)}#{cc[:esc_m]}"
|
60
|
+
"#{color}#{s.sub(cc[:esc_m], cc[:esc_m] + color)}#{cc[:esc_m]}"
|
52
61
|
end
|
53
62
|
end
|
54
63
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seccomp-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- david942j
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: codeclimate-test-reporter
|
@@ -108,7 +108,9 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0.9'
|
111
|
-
description:
|
111
|
+
description: |
|
112
|
+
Provide useful tools to analyze seccomp rules.
|
113
|
+
Visit https://github.com/david942j/seccomp-tools for more details.
|
112
114
|
email:
|
113
115
|
- david942j@gmail.com
|
114
116
|
executables:
|
@@ -127,12 +129,14 @@ files:
|
|
127
129
|
- lib/seccomp-tools/cli/cli.rb
|
128
130
|
- lib/seccomp-tools/cli/disasm.rb
|
129
131
|
- lib/seccomp-tools/cli/dump.rb
|
132
|
+
- lib/seccomp-tools/cli/emu.rb
|
130
133
|
- lib/seccomp-tools/const.rb
|
131
134
|
- lib/seccomp-tools/consts/amd64.rb
|
132
135
|
- lib/seccomp-tools/consts/i386.rb
|
133
|
-
- lib/seccomp-tools/context.rb
|
134
|
-
- lib/seccomp-tools/disasm.rb
|
136
|
+
- lib/seccomp-tools/disasm/context.rb
|
137
|
+
- lib/seccomp-tools/disasm/disasm.rb
|
135
138
|
- lib/seccomp-tools/dumper.rb
|
139
|
+
- lib/seccomp-tools/emulator.rb
|
136
140
|
- lib/seccomp-tools/instruction/alu.rb
|
137
141
|
- lib/seccomp-tools/instruction/base.rb
|
138
142
|
- lib/seccomp-tools/instruction/instruction.rb
|
@@ -1,31 +0,0 @@
|
|
1
|
-
module SeccompTools
|
2
|
-
# The context when emulating.
|
3
|
-
#
|
4
|
-
# @todo
|
5
|
-
# No lambda value, not support ALU instructions.
|
6
|
-
class Context
|
7
|
-
attr_accessor :a, :x, :mem
|
8
|
-
def initialize(a: nil, x: nil, mem: {})
|
9
|
-
@a = a
|
10
|
-
@x = x
|
11
|
-
@mem = mem
|
12
|
-
end
|
13
|
-
|
14
|
-
# Implement a deep dup.
|
15
|
-
# @return [Context]
|
16
|
-
def dup
|
17
|
-
Context.new(a: a, x: x, mem: mem.dup)
|
18
|
-
end
|
19
|
-
|
20
|
-
# For conveniently get instance variable.
|
21
|
-
# @param [String, Symbol] key
|
22
|
-
def [](key)
|
23
|
-
instance_variable_get(('@' + key.downcase).to_sym)
|
24
|
-
end
|
25
|
-
|
26
|
-
# For conveniently set instance variable.
|
27
|
-
def []=(key, val)
|
28
|
-
instance_variable_set(('@' + key.downcase).to_sym, val)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
data/lib/seccomp-tools/disasm.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'seccomp-tools/bpf'
|
2
|
-
require 'seccomp-tools/context'
|
3
|
-
require 'seccomp-tools/util'
|
4
|
-
|
5
|
-
module SeccompTools
|
6
|
-
# Disassembler of seccomp bpf.
|
7
|
-
module Disasm
|
8
|
-
module_function
|
9
|
-
|
10
|
-
# Disassemble bpf codes.
|
11
|
-
# @param [String] bpf
|
12
|
-
# The bpf codes.
|
13
|
-
# @param [Symbol] arch
|
14
|
-
# Architecture.
|
15
|
-
# @todo
|
16
|
-
# Detect system architecture as default.
|
17
|
-
def disasm(bpf, arch: nil)
|
18
|
-
arch ||= Util.system_arch
|
19
|
-
codes = bpf.scan(/.{8}/m).map.with_index { |b, i| BPF.new(b, arch, i) }
|
20
|
-
contexts = Array.new(codes.size) { [] }
|
21
|
-
contexts[0].push(Context.new)
|
22
|
-
dis = codes.zip(contexts).map do |code, ctxs|
|
23
|
-
ctxs.each do |ctx|
|
24
|
-
code.branch(ctx) do |pc, c|
|
25
|
-
contexts[pc].push(c) unless c.nil? || pc >= contexts.size
|
26
|
-
end
|
27
|
-
end
|
28
|
-
code.contexts = ctxs
|
29
|
-
code.disasm
|
30
|
-
end.join("\n")
|
31
|
-
<<EOS + dis + "\n"
|
32
|
-
line CODE JT JF K
|
33
|
-
=================================
|
34
|
-
EOS
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|