Class: Rex::Payloads::Shuffle

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/payloads/shuffle.rb

Constant Summary collapse

FLOW_INSTRUCTIONS =
{}

Class Method Summary collapse

Class Method Details

.from_graphml_file(file_path, arch: nil, name: nil) ⇒ Object

Shuffle instructions from a GraphML data file and return the assembly source. If an architecture is specified and supported, labels will be added for control flow instructions such as jumps and calls. Labels are necessary if any post processing is performed on the source (such as for obfuscation).

Parameters:

  • file_path (String)

    The file path to load the GraphML data from.

  • name (String) (defaults to: nil)

    An optional symbol name to apply to the assembly source.


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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/rex/payloads/shuffle.rb', line 24

def self.from_graphml_file(file_path, arch: nil, name: nil)
  graphml = Rex::Parser::GraphML.from_file(file_path)
  blocks = graphml.graphs.select { |graph| graph.attributes['type'] == 'block' }.sort_by { |graph| graph.attributes['address'] }
  blocks.map! { |block| { node: block, instructions: self.process_block(block) } }

  label_prefix = Rex::Text.rand_text_alpha_lower(4)
  labeler = lambda { |address| "loc_#{label_prefix}#{ address.to_s(16).rjust(4, '0') }" }

  source_lines = []
  labeled = []
  label_refs = []
  blocks.each do |block|
    source_lines << labeler.call(block[:node].attributes['address']) + ':'
    labeled << block[:node].attributes['address']
    # by default use the raw binary instruction to avoid syntax compatibility issues with metasm
    instructions = block[:instructions].map { |node| 'db ' + node.attributes['instruction.hex'].strip.chars.each_slice(2).map { |hex| '0x' + hex.join }.join(', ') }
    unless arch.nil?
      raise ArgumentError, 'Unsupported architecture' if FLOW_INSTRUCTIONS[arch].nil?

      # if a supported architecture was specified, use the original source and apply the necessary labels
      block[:instructions].each_with_index do |node, index|
        next unless match = /^(?<mnemonic>\S+)\s+(?<address>0x[a-f0-9]+)$/.match(node.attributes['instruction.source'])
        next unless FLOW_INSTRUCTIONS[arch].include? match[:mnemonic]

        address = Integer(match[:address])
        instructions[index] = "#{match[:mnemonic]} #{labeler.call(address)}"
        label_refs << address
      end
    end

    source_lines += instructions
  end

  unless label_refs.all? { |address| labeled.include? address  }
    # raise this here so it's closer to the source of the problem :(
    raise StandardError, 'Missing label reference'
  end


  source_lines = ([name + ':'] + source_lines.map { |source_line| '  ' + source_line}) unless name.nil?
  source_lines.join("\n") + "\n"
end