Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/payloads/shuffle.rb
Views: 11623
# -*- coding: binary -*-12require 'rex/arch'3##4# This module contains a helper function for generating payloads from a shuffled5# set of instructions loaded from a special file.6##7module Rex8module Payloads9class Shuffle1011FLOW_INSTRUCTIONS = {}12FLOW_INSTRUCTIONS[Rex::Arch::ARCH_X86] = %w{ call jae jb jbe jc jcxz je jecxz jg jge jl jle jmp jna jnae jnb jnbe jnc jne jng jnge jnl jnle jno jnp jns jnz jo jp jpe jpo js jz }.freeze13FLOW_INSTRUCTIONS[Rex::Arch::ARCH_X64] = (FLOW_INSTRUCTIONS[Rex::Arch::ARCH_X86] + %w{ jrcxz }).freeze14private_constant :FLOW_INSTRUCTIONS1516#17# Shuffle instructions from a GraphML data file and return the assembly source. If an architecture is specified18# and supported, labels will be added for control flow instructions such as jumps and calls. Labels are necessary19# if any post processing is performed on the source (such as for obfuscation).20#21# @param file_path [String] The file path to load the GraphML data from.22# @param name [String] An optional symbol name to apply to the assembly source.23def self.from_graphml_file(file_path, arch: nil, name: nil)24graphml = Rex::Parser::GraphML.from_file(file_path)25blocks = create_path(graphml.nodes.select { |_id,node| node.attributes['type'] == 'block' }, graphml.graphs[0].edges)26blocks.map! { |block| { node: block, instructions: process_block(block) } }2728label_prefix = Rex::Text.rand_text_alpha_lower(4)29labeler = lambda { |address| "loc_#{label_prefix}#{ address.to_s(16).rjust(4, '0') }" }3031source_lines = []32labeled = []33label_refs = []34blocks.each do |block|35if [ Rex::Arch::ARCH_X86, Rex::Arch::ARCH_X64 ].include? arch36source_lines << labeler.call(block[:node].attributes['address']) + ':'37labeled << block[:node].attributes['address']38# by default use the raw binary instruction to avoid syntax compatibility issues with metasm39instructions = block[:instructions].map { |node| 'db ' + node.attributes['instruction.hex'].strip.chars.each_slice(2).map { |hex| '0x' + hex.join }.join(', ') }40else41instructions = block[:instructions].map { |node| node.attributes['instruction.source'] }42end43unless arch.nil?44raise ArgumentError, 'Unsupported architecture' if FLOW_INSTRUCTIONS[arch].nil?4546# if a supported architecture was specified, use the original source and apply the necessary labels47block[:instructions].each_with_index do |node, index|48next unless match = /^(?<mnemonic>\S+)\s+(?<address>0x[a-f0-9]+)$/.match(node.attributes['instruction.source'])49next unless FLOW_INSTRUCTIONS[arch].include? match[:mnemonic]5051address = Integer(match[:address])52instructions[index] = "#{match[:mnemonic]} #{labeler.call(address)}"53label_refs << address54end55end5657source_lines += instructions58end5960unless label_refs.all? { |address| labeled.include? address }61# raise this here so it's closer to the source of the problem :(62raise StandardError, 'Missing label reference'63end646566source_lines = ([name + ':'] + source_lines.map { |source_line| ' ' + source_line}) unless name.nil?67source_lines.join("\n") + "\n"68end6970class << self71private7273#74# Process the specified graph element which represents a single basic block in assembly. This graph element75# contains nodes representing each of its instructions.76#77def process_block(block)78subgraph = block.subgraph79instructions = subgraph.nodes.select { |_id, node| node.attributes['type'] == 'instruction' }80create_path(instructions, subgraph.edges)81end8283def create_path(nodes, edges)84path = []8586# the initial choices are any node without a predecessor (dependency)87targets = edges.map(&:target)88choices = nodes.values.select { |node| !targets.include? node.id }89until choices.empty?90selection = choices.sample91choices.delete(selection)92path << selection9394# check each node for which the selection is a dependency95successors = selection.target_edges.map { |edge| nodes[edge.target] }96successors.each do |successor|97next if path.include? successor98next if !successor.source_edges.map { |edge| path.include? nodes[edge.source] }.all?99100choices << successor101end102end103104path105end106end107end108end109end110111112