Module: PWN::Plugins::REDB

Defined in:
lib/pwn/plugins/redb.rb

Overview

Persistent per-binary analysis DB under ~/.pwn/redb.

Constant Summary collapse

ROOT =
File.join(Dir.home, '.pwn', 'redb')

Class Method Summary collapse

Class Method Details

.annotate(opts = {}) ⇒ Object

Raises:

  • (ArgumentError)


70
71
72
73
74
75
76
77
78
79
# File 'lib/pwn/plugins/redb.rb', line 70

public_class_method def self.annotate(opts = {})
  note = opts[:text].to_s
  target = (opts[:addr] || opts[:func] || opts[:sym]).to_s
  raise ArgumentError, 'addr or func is required' if target.empty?

  with_db(opts) do |db|
    db.execute('INSERT INTO annotations(target, text, cached_at) VALUES(?,?,?)', [target, note, Time.now.utc.iso8601])
    { target: target, text: note }
  end
end

.authorsObject



81
82
83
# File 'lib/pwn/plugins/redb.rb', line 81

public_class_method def self.authors
  "AUTHOR(S):\n  0day Inc. <[email protected]>\n"
end

.decompile(opts = {}) ⇒ Object

Raises:

  • (ArgumentError)


56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/pwn/plugins/redb.rb', line 56

public_class_method def self.decompile(opts = {})
  func = (opts[:func] || opts[:name]).to_s
  raise ArgumentError, 'func is required' if func.empty?

  with_db(opts) do |db|
    row = db.get_first_row('SELECT * FROM decompile WHERE name = ?', [func])
    return row if row

    text = objdump_func(bin: PWN::Plugins::REDB.open(opts)[:bin], func: func)
    db.execute('INSERT OR REPLACE INTO decompile(name, text, cached_at) VALUES(?,?,?)', [func, text, Time.now.utc.iso8601])
    { 'name' => func, 'text' => text }
  end
end

.funcs(opts = {}) ⇒ Object



34
35
36
# File 'lib/pwn/plugins/redb.rb', line 34

public_class_method def self.funcs(opts = {})
  with_db(opts) { |db| db.execute('SELECT * FROM functions ORDER BY name') }
end

.helpObject



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/pwn/plugins/redb.rb', line 85

public_class_method def self.help
  puts "USAGE:
    # Open or reuse ~/.pwn/redb/<sha256> for a binary.
    #{self}.open(
      bin: 'required - filesystem path of the binary',
      path: 'optional - alias for bin'
    )

    # List cached functions.
    #{self}.funcs(
      bin: 'required - filesystem path of the binary'
    )

    # Return callers of a symbol from cache.
    #{self}.xrefs_to(
      bin: 'required - filesystem path of the binary',
      sym: 'required - destination symbol or address',
      name: 'optional - alias for sym',
      addr: 'optional - alias for sym'
    )

    # Search cached strings.
    #{self}.strings(
      bin: 'required - filesystem path of the binary',
      match: 'optional - substring filter'
    )

    # Return cached decompilation, filling the cache from objdump on miss.
    #{self}.decompile(
      bin: 'required - filesystem path of the binary',
      func: 'required - function name',
      name: 'optional - alias for func'
    )

    # Persist an analyst annotation for a later session.
    #{self}.annotate(
      bin: 'required - filesystem path of the binary',
      addr: 'optional - address to annotate',
      func: 'optional - function name to annotate',
      sym: 'optional - symbol to annotate',
      text: 'optional - annotation body'
    )

    # Print the AUTHOR(S) string for this module.
    #{self}.authors
  "
  constants.sort
end

.open(opts = {}) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/pwn/plugins/redb.rb', line 16

public_class_method def self.open(opts = {})
  bin = (opts[:bin] || opts[:path]).to_s
  raise ArgumentError, 'bin is required' if bin.empty?
  raise ArgumentError, "binary not found: #{bin}" unless File.file?(bin)

  sha = Digest::SHA256.file(bin).hexdigest
  dir = File.join(ROOT, sha)
  FileUtils.mkdir_p(dir)
  db_path = File.join(dir, 'analysis.sqlite')
  db = SQLite3::Database.new(db_path)
  db.results_as_hash = true
  migrate(db: db)
  analyze(db: db, bin: bin) if db.get_first_value("SELECT COUNT(*) FROM meta WHERE key = 'analyzed'").to_i.zero?
  { sha256: sha, dir: dir, db: db_path, bin: File.expand_path(bin) }
ensure
  db&.close
end

.strings(opts = {}) ⇒ Object



45
46
47
48
49
50
51
52
53
54
# File 'lib/pwn/plugins/redb.rb', line 45

public_class_method def self.strings(opts = {})
  needle = opts[:match].to_s
  sql = 'SELECT * FROM strings'
  args = []
  unless needle.empty?
    sql += ' WHERE value LIKE ?'
    args << "%#{needle}%"
  end
  with_db(opts) { |db| db.execute(sql, args) }
end

.xrefs_to(opts = {}) ⇒ Object

Raises:

  • (ArgumentError)


38
39
40
41
42
43
# File 'lib/pwn/plugins/redb.rb', line 38

public_class_method def self.xrefs_to(opts = {})
  sym = (opts[:sym] || opts[:name] || opts[:addr]).to_s
  raise ArgumentError, 'sym is required' if sym.empty?

  with_db(opts) { |db| db.execute('SELECT * FROM xrefs WHERE dst = ?', [sym]) }
end