seccomp-tools 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
![dump](https://github.com/david942j/seccomp-tools/blob/master/examples/dump-diary.png?raw=true)
|
146
180
|
|
181
|
+
### Emu
|
182
|
+
![emu](https://github.com/david942j/seccomp-tools/blob/master/examples/emu-libseccomp.png?raw=true)
|
183
|
+
|
184
|
+
![emu](https://github.com/david942j/seccomp-tools/blob/master/examples/emu-amigo.png?raw=true)
|
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
|