Module: PWN::Plugins::GhidraHeadless
- Defined in:
- lib/pwn/plugins/ghidra_headless.rb
Overview
analyzeHeadless wrapper that exports decompiled C plus the symbol table as JSON, cached by binary SHA-256.
Class Method Summary collapse
- .analyze(opts = {}) ⇒ Object
- .authors ⇒ Object
- .decompile(opts = {}) ⇒ Object
- .help ⇒ Object
- .required_bins ⇒ Object
Class Method Details
.analyze(opts = {}) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/pwn/plugins/ghidra_headless.rb', line 20 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) path = File.realpath(path) sha = Digest::SHA256.file(path).hexdigest cache = File.join(File.(opts[:cache_dir] || '~/.pwn/ghidra_cache'), "#{sha}.json") if File.file?(cache) && opts[:backend] != 'binutils' row = JSON.parse(File.read(cache), symbolize_names: true) return select_functions(row: row.merge(cached: true), function: opts[:function]) if row[:sha256] == sha && row[:status] == 'ok' end row = if opts[:backend] != 'binutils' && BinaryAnalysis.available?(name: 'analyzeHeadless') run_headless(path: path, sha: sha, timeout: opts.fetch(:timeout, 300)) else BinaryAnalysis.analyze(opts.merge(path: path)).merge(engine: 'binutils', decompiled: { functions: [], symbols: [], types: [] }) end row = row.merge(sha256: sha, cached: false, risk_level: 'low') if row[:status] == 'ok' FileUtils.mkdir_p(File.dirname(cache), mode: 0o700) Tempfile.create(['ghidra', '.json'], File.dirname(cache)) do |file| file.write(JSON.generate(row)) file.close File.rename(file.path, cache) end end select_functions(row: row, function: opts[:function]) end |
.authors ⇒ Object
54 55 56 |
# File 'lib/pwn/plugins/ghidra_headless.rb', line 54 public_class_method def self. "AUTHOR(S):\n 0day Inc. <[email protected]>\n" end |
.decompile(opts = {}) ⇒ Object
50 51 52 |
# File 'lib/pwn/plugins/ghidra_headless.rb', line 50 public_class_method def self.decompile(opts = {}) analyze(opts) end |
.help ⇒ Object
58 59 60 61 62 63 64 65 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 91 92 93 94 95 96 |
# File 'lib/pwn/plugins/ghidra_headless.rb', line 58 public_class_method def self.help puts "USAGE: # List host binaries this module expects to be installed. #{self}.required_bins # Run analyzeHeadless and cache decompiled C + symbols as JSON. #{self}.analyze( bin: 'required - filesystem path of the binary', path: 'optional - alias for bin', function: 'optional - function name to decompile' ) # Alias of #analyze. #{self}.decompile( bin: 'required - filesystem path of the binary', path: 'optional - alias for bin', function: 'optional - function name to decompile' ) # Print the AUTHOR(S) string for this module. #{self}.authors # Invoke required_bins with the documented options; normalized path APIs report their backend. #{self}.required_bins # Invoke analyze with the documented options; normalized path APIs report their backend. #{self}.analyze( backend: 'optional - analysis backend name; binutils forces lightweight fallback', bin: 'optional - filesystem path to the binary to analyze', cache_dir: 'optional - directory for successful SHA256-keyed Ghidra JSON exports', function: 'optional - function symbol name to select from analysis', path: 'optional - filesystem path to the local artifact or binary', timeout: 'optional - positive subprocess or HTTP deadline in seconds' ) # Invoke decompile with the documented options; normalized path APIs report their backend. #{self}.decompile # Invoke authors with the documented options; normalized path APIs report their backend. #{self}.authors " constants.sort end |
.required_bins ⇒ Object
16 17 18 |
# File 'lib/pwn/plugins/ghidra_headless.rb', line 16 public_class_method def self.required_bins %w[analyzeHeadless] end |