Module: Pwnlib::RegSort
- Included in:
- Shellcraft::Generators::Helper::Runner
- Defined in:
- lib/pwnlib/reg_sort.rb
Overview
Do topological sort on register assignments.
Class Method Summary collapse
-
.regsort(in_out, all_regs, randomize: nil) ⇒ Array
Sorts register dependencies.
Class Method Details
.regsort(in_out, all_regs, randomize: nil) ⇒ Array
Sorts register dependencies.
Given a dictionary of registers to desired register contents, return the optimal order in which to set the registers to those contents.
The implementation assumes that it is possible to move from any register to any other register.
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 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/pwnlib/reg_sort.rb', line 51 def regsort(in_out, all_regs, randomize: nil) # randomize = context.randomize if randomize.nil? # TODO(david942j): stringify_keys in_out = in_out.transform_keys(&:to_s) # Drop all registers which will be set to themselves. # Ex. {eax: 'eax'} in_out.reject! { |k, v| k == v } # Check input if (in_out.keys - all_regs).any? raise ArgumentError, format('Unknown register! Know: %p. Got: %p', all_regs, in_out) end # Collapse constant values. # # Ex. {eax: 1, ebx: 1} can be collapsed to {eax: 1, ebx: 'eax'}. # +post_mov+ are collapsed registers, set their values in the end. post_mov = in_out.group_by { |_, v| v }.each_value.with_object({}) do |list, hash| list.sort! first_reg, val = list.shift # Special case for val.zero? because zeroify registers is cheaper than mov. next if list.empty? || all_regs.include?(val) || val.zero? list.each do |(reg, _)| hash[reg] = first_reg in_out.delete(reg) end end graph = in_out.dup result = [] # Let's do the topological sort. # so sad ruby 2.1 doesn't have +itself+... deg = graph.values.group_by { |i| i }.transform_values(&:size) graph.each_key { |k| deg[k] ||= 0 } until deg.empty? min_deg = deg.min_by { |_, v| v }[1] break unless min_deg.zero? # remain are all cycles min_pivs = deg.select { |_, v| v == min_deg } piv = randomize ? min_pivs.sample : min_pivs.first dst = piv.first deg.delete(dst) next unless graph.key?(dst) # Reach an end node. deg[graph[dst]] -= 1 result << ['mov', dst, graph[dst]] graph.delete(dst) end # Remain must be cycles. graph.each_key do |reg| cycle = check_cycle(reg, graph) cycle.each_cons(2) do |d, s| result << ['xchg', d, s] end cycle.each { |r| graph.delete(r) } end # Now assign those collapsed registers. post_mov.sort.each do |dreg, sreg| result << ['mov', dreg, sreg] end result end |