Class: OneGadget::Emulators::Arm
- Defined in:
- lib/one_gadget/emulators/arm.rb
Overview
Emulator of 32-bit ARM (both A32 and Thumb-2 encodings).
Constant Summary collapse
- FLAG_SETTING =
The flag-setting spelling of an instruction we model, which differs from the base mnemonic only by a trailing
s(+movs+,ands, ...). The flags it sets are not modelled, so a branch reading them aborts the path anyway; what matters here is the value it also writes. Conditional variants (+moveq+,addne, ...) are deliberately absent and stay unsupported. /\A(mov|add|sub|and|orr|eor|bic|mvn|lsl|lsr)s\z/
Constants inherited from ArmFamily
OneGadget::Emulators::ArmFamily::COMPARES, OneGadget::Emulators::ArmFamily::COND
Constants inherited from Processor
Processor::ADDRESS_TYPES, Processor::CLOBBERED, Processor::NULLABLE_REQUIREMENTS, Processor::POINTER_REQUIREMENTS, Processor::TERMINAL_CALL_RE
Constants included from Conditional
Conditional::COMPARE_OPS, Conditional::NEGATE, Conditional::RELATION
Instance Attribute Summary
Attributes inherited from Processor
#bp, #pc, #refused_line, #registers, #sp
Class Method Summary collapse
-
.bits ⇒ Object
ARM (32-bit) is 32-bit.
-
.file_data(file) ⇒ Object
Memoized bytes of
file(the target libc), shared across emulator instances.
Instance Method Summary collapse
-
#argument(idx) ⇒ Lambda, Integer
Return the argument value of calling a function.
-
#initialize(file = nil) ⇒ Arm
constructor
Instantiate an Arm object.
-
#instructions ⇒ Array<Instruction>
Supported instruction set.
-
#note_instruction_set(lines) ⇒ Object
Settle Thumb vs A32 from a whole candidate, before any of it is emulated.
- #process!(cmd) ⇒ Object
Methods inherited from Processor
#address_deref0?, #bp_based_stack, #closed_fds, #constraint_key, #constraints, #drop_implied_nonzero, #drop_restated_null, #get_corresponding_stack, instruction_table, line_memo, #parse, #process, #reach_terminal_call, #render_constraint, #resolve_address, #setup_frame_pointer, #sp_based_stack, #terminal_call?
Methods included from Conditional
#branch_on_bit, #branch_on_compare, #branch_on_zero, #comparisons_on, #handle_compare, #mnemonic, #operand_str, #record_compare, #resolve_pending_branch, #satisfiable?, #value_str
Constructor Details
#initialize(file = nil) ⇒ Arm
Instantiate an OneGadget::Emulators::Arm object.
19 20 21 22 23 24 25 26 27 28 |
# File 'lib/one_gadget/emulators/arm.rb', line 19 def initialize(file = nil) super(OneGadget::ABI.arm, 'sp') @pc = 'pc' # find() builds a fresh emulator per candidate; cache the file's bytes so # the literal pool isn't re-read from disk thousands of times. @data = file && self.class.file_data(file) @prev_addr = nil # A32 until proven Thumb by a +.w+/+.n+ suffix or a 2-byte instruction stride. @thumb = false end |
Class Method Details
.bits ⇒ Object
ARM (32-bit) is 32-bit.
300 301 302 |
# File 'lib/one_gadget/emulators/arm.rb', line 300 def bits 32 end |
.file_data(file) ⇒ Object
Memoized bytes of file (the target libc), shared across emulator instances.
31 32 33 |
# File 'lib/one_gadget/emulators/arm.rb', line 31 def self.file_data(file) (@file_data ||= {})[file] ||= File.binread(file) end |
Instance Method Details
#argument(idx) ⇒ Lambda, Integer
Return the argument value of calling a function.
97 98 99 100 101 |
# File 'lib/one_gadget/emulators/arm.rb', line 97 def argument(idx) return registers["r#{idx}"] if idx < 4 sp_based_stack[(idx - 4) * size_t] end |
#instructions ⇒ Array<Instruction>
Supported instruction set. Any instruction not listed here aborts the current gadget candidate (mirrors the conservative aarch64 emulator).
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 |
# File 'lib/one_gadget/emulators/arm.rb', line 61 def instructions [ Instruction.new('push', 1), Instruction.new('pop', 1), Instruction.new('add', 2..3), Instruction.new('sub', 2..3), Instruction.new('mov', 2), Instruction.new('ldr', 2..3), Instruction.new('ldrb', 2..3), Instruction.new('str', 2..3), Instruction.new('bl', 1), Instruction.new('blx', 1), Instruction.new('nop', 0..1), Instruction.new('dmb', 0..1), Instruction.new('dsb', 0..1), Instruction.new('isb', 0..1), Instruction.new('and', 2..3), Instruction.new('orr', 2..3), Instruction.new('eor', 2..3), Instruction.new('bic', 2..3), Instruction.new('mvn', 2), Instruction.new('lsl', 2..3), Instruction.new('lsr', 2..3), Instruction.new('cmp', 2..3), Instruction.new('cmn', 2..3), Instruction.new('tst', 2..3), Instruction.new('svc', 1) ] end |
#note_instruction_set(lines) ⇒ Object
Settle Thumb vs A32 from a whole candidate, before any of it is emulated.
#track_mode can only learn from lines already seen, so the FIRST
instruction is judged on no evidence at all -- and when that instruction
reads pc, the bias it picks decides an address the constraints go on to
name. Applying the same evidence up front removes that dependence on
whatever happened to be processed first.
A32 is left alone: it shows neither a width suffix nor a 2-byte stride, so a genuinely A32 candidate keeps the whole-word bias. A single-instruction Thumb candidate offers no evidence either and is no better served than before.
115 116 117 118 119 120 121 |
# File 'lib/one_gadget/emulators/arm.rb', line 115 def note_instruction_set(lines) return if @thumb addrs = lines.filter_map { |l| l[/\A\s*([0-9a-f]+):/, 1]&.to_i(16) } @thumb = lines.any? { |l| l.match?(/\.[wn]\b/) } || addrs.each_cons(2).any? { |a, b| b - a == 2 } end |
#process!(cmd) ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/one_gadget/emulators/arm.rb', line 36 def process!(cmd) resolve_pending_branch(cmd) line = cmd.strip track_mode(line) body, @literal = decompose(line) mnem, rest = body.split(/\s+/, 2) return handle_compare(COMPARES[mnem], rest) if COMPARES.key?(mnem) return handle_branch(mnem, rest) != :fail if branch_mnem?(mnem) # push/pop take a {reg-list} whose commas would confuse the generic parser. return __send__(:"inst_#{mnem}", rest) != :fail if %w[push pop].include?(mnem) inst, args = parse(body) __send__(:"inst_#{inst.inst}", *args) != :fail end |