Module: PWN::Plugins::Ghidra

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

Overview

Headless Ghidra analyzeHeadless wrapper with r2 fallback.

Class Method Summary collapse

Class Method Details

.analyze(opts = {}) ⇒ Object



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

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

  if PWN::Plugins::PreflightChecker.bin?(name: 'analyzeHeadless')
    proj = opts[:project_dir].to_s
    proj = File.join(Dir.home, '.pwn', 'ghidra') if proj.empty?
    FileUtils.mkdir_p(proj)
    stdout, stderr, status = Open3.capture3('analyzeHeadless', proj, 'pwn', '-import', path, '-deleteProject')
    return { stdout: stdout, stderr: stderr, exit: status.exitstatus, engine: 'ghidra' }
  end

  return r2_fallback(path: path) if PWN::Plugins::PreflightChecker.bin?(name: 'r2')

  { error: 'analyzeHeadless missing', hint: 'install Ghidra or r2; pwn setup --profile re' }
end

.authors ⇒ Object



62
63
64
# File 'lib/pwn/plugins/ghidra.rb', line 62

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

.decompile(opts = {}) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/pwn/plugins/ghidra.rb', line 34

public_class_method def self.decompile(opts = {})
  path = (opts[:bin] || opts[:path]).to_s
  raise 'ERROR: bin is required' if path.empty?

  sha = Digest::SHA256.file(path).hexdigest if File.file?(path)
  cache = File.join(Dir.home, '.pwn', 'cache', 'decompile', "#{sha}.json") if sha
  return JSON.parse(File.read(cache), symbolize_names: true).merge(cached: true) if cache && File.file?(cache)

  row = analyze(opts)
  fn = opts[:function].to_s
  row[:function] = fn unless fn.empty?
  FileUtils.mkdir_p(File.dirname(cache)) if cache
  File.write(cache, JSON.generate(row)) if cache
  row
end

.help ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/pwn/plugins/ghidra.rb', line 66

public_class_method def self.help
  puts "USAGE:
    # List host binaries this module expects to be installed.
    #{self}.required_bins

    # Run analyzeHeadless (or r2 pdg) against a binary.
    #{self}.analyze(
      bin: 'required - filesystem path of the binary',
      path: 'optional - alias for bin',
      project_dir: 'optional - Ghidra project directory under ~/.pwn/ghidra'
    )

    # Decompile a binary; same as #analyze when Ghidra/r2 is the engine.
    #{self}.decompile(
      bin: 'required - filesystem path of the binary',
      path: 'optional - alias for bin',
      project_dir: 'optional - Ghidra project directory',
      function: 'optional - function name to decompile instead of the whole program'
    )

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

.required_bins ⇒ Object



12
13
14
# File 'lib/pwn/plugins/ghidra.rb', line 12

public_class_method def self.required_bins
  %w[analyzeHeadless]
end