Module: SeccompTools::Disasm

Defined in:
lib/seccomp-tools/disasm/disasm.rb

Overview

Disassembler of seccomp BPF.

Class Method Summary collapse

Class Method Details

.disasm(raw, arch: nil, display_bpf: true, arg_infer: true) ⇒ String

Disassemble BPF codes.

Emulates the filter to track what each register holds, so syscall names and argument positions can be inferred and shown as comments.

Examples:

SeccompTools::Disasm.disasm(raw, arch: :amd64, display_bpf: false)
#=> "0000: A = sys_number\n0001: if (A == read) goto 0003\n0002: return KILL\n0003: return ALLOW\n"

Parameters:

  • raw (String)

    The raw BPF bytes.

  • arch (Symbol?) (defaults to: nil)

    Target architecture, must be one of Util.supported_archs. Defaults to Util.system_arch when nil.

  • display_bpf (Boolean) (defaults to: true)

    Whether to prepend each line with its raw code, jt, jf and k fields.

  • arg_infer (Boolean) (defaults to: true)

    Whether to annotate lines with the inferred syscall name and argument.

Returns:

  • (String)

    The disassembly result, ready to be printed.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/seccomp-tools/disasm/disasm.rb', line 33

def disasm(raw, arch: nil, display_bpf: true, arg_infer: true)
  codes = to_bpf(raw, arch)
  states = Array.new(codes.size) { Set.new }
  states[0].add(Symbolic::State.initial)
  # A forward pass (jumps only go forward) tracking, per line, the states that can reach it, so
  # syscall names and argument positions can be inferred from what each register holds. Unlike
  # the Executor, this over-approximates - it forks every conditional instead of folding - so
  # dead lines are still rendered.
  dis = codes.zip(states).map do |code, sts|
    sts.each do |st|
      code.branch(st) do |pc, s|
        states[pc].add(s) unless pc >= states.size
      end
    end
    code.states = sts
    code.disasm(code: display_bpf, arg_infer:)
  end.join("\n")
  if display_bpf
    <<-EOS
 line  CODE  JT   JF      K
=================================
#{dis}
    EOS
  else
    "#{dis}\n"
  end
end

.to_bpf(raw, arch) ⇒ Array<BPF>

Convert raw BPF string to array of BPF.

Parameters:

  • raw (String)

    The raw BPF bytes, each instruction being 8 bytes long.

  • arch (Symbol?)

    Target architecture, defaults to Util.system_arch when nil.

Returns:

  • (Array<BPF>)

    One BPF per instruction, in order.



68
69
70
71
# File 'lib/seccomp-tools/disasm/disasm.rb', line 68

def to_bpf(raw, arch)
  arch ||= Util.system_arch
  raw.scan(/.{8}/m).map.with_index { |b, i| BPF.new(b, arch, i) }
end