Class: Rex::Payloads::Shuffle

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

Class Method Summary collapse

Class Method Details

.from_graphml_file(file_path, arch: nil, name: nil, random: 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.

  • random (Random) (defaults to: nil)

    Optional seeded RNG used for shuffling and label naming. When supplied, the resulting assembly source is deterministic for a given seed; when nil, the global RNG is used and output is non-deterministic.



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
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
# File 'lib/rex/payloads/shuffle.rb', line 27

def self.from_graphml_file(file_path, arch: nil, name: nil, random: nil)
  graphml = Rex::Parser::GraphML.from_file(file_path)
  blocks = create_path(graphml.nodes.select { |_id,node| %w[ data instructions-graph block ].include?(node.attributes['type']) }, graphml.graphs[0].edges, random: random)
  blocks.map! do |block|
    hash = { node: block }

    if %w[ instructions-graph block ].include?(block.attributes['type'])
      hash[:instructions] = process_instructions_graph_block(block, random: random)
    end

    hash
  end

  label_prefix = random ? Array.new(4) { ('a'.ord + random.rand(26)).chr }.join : 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|
    if [ Rex::Arch::ARCH_X86, Rex::Arch::ARCH_X64 ].include? arch
      source_lines << labeler.call(block[:node].attributes['address']) + ':'
      labeled << block[:node].attributes['address']
      case block[:node].attributes['type']
      when 'data'
        instructions = data_block_instructions(block[:node])
      when 'instructions-graph', 'block'
        # 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(', ') }
      end
    else
      case block[:node].attributes['type']
      when 'data'
        instructions = data_block_instructions(block[:node])
      when 'instructions-graph', 'block'
        instructions = block[:instructions].map { |node| node.attributes['instruction.source'] }
      end
    end

    unless arch.nil?
      raise ArgumentError, 'Unsupported architecture' if FLOW_INSTRUCTIONS[arch].nil?

      if %w[ instructions-graph block ].include?(block[:node].attributes['type'])
        # 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
    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