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
Constants included from Constraints
Constraints::ADDRESS_TYPES, Constraints::CLOBBERED, Constraints::NULLABLE_REQUIREMENTS, Constraints::POINTER_REQUIREMENTS
Constants included from Conditional
Conditional::COMPARE_OPS, Conditional::NEGATE, Conditional::RELATION, Conditional::ZERO
Instance Attribute Summary
Attributes inherited from Processor
#bp, #pc, #refused_line, #registers, #sp
Class Method Summary collapse
-
.bits ⇒ Integer
ARM (32-bit) is 32-bit.
-
.file_data(file) ⇒ String
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) ⇒ void
Settle Thumb vs A32 from a whole candidate, before any of it is emulated.
-
#process!(cmd) ⇒ Boolean
If successfully processed.
Methods inherited from Processor
instruction_table, line_memo, #parse, #process, #reach_terminal_call, #terminal_call?
Methods included from TrackedMemory
#bp_based_stack, #get_corresponding_stack, #resolve_address, #setup_frame_pointer, #sp_based_stack, #writes_through
Methods included from Constraints
#address_deref0?, #closed_fds, #constraint_key, #constraints, #drop_implied_nonzero, #drop_restated_null, #render_constraint
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 ⇒ Integer
ARM (32-bit) is 32-bit.
309 310 311 |
# File 'lib/one_gadget/emulators/arm.rb', line 309 def bits 32 end |
.file_data(file) ⇒ String
Memoized bytes of file (the target libc), shared across emulator instances.
33 34 35 |
# File 'lib/one_gadget/emulators/arm.rb', line 33 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.
101 102 103 104 105 |
# File 'lib/one_gadget/emulators/arm.rb', line 101 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).
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/one_gadget/emulators/arm.rb', line 65 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) ⇒ void
This method returns an undefined value.
Settle Thumb vs A32 from a whole candidate, before any of it is emulated.
#track_mode learns only from lines already seen, so the first instruction
would otherwise be judged on no evidence -- and when it reads pc, that
decides an address the constraints go on to name.
118 119 120 121 122 123 124 |
# File 'lib/one_gadget/emulators/arm.rb', line 118 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) ⇒ Boolean
Returns If successfully processed.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/one_gadget/emulators/arm.rb', line 40 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.handler, *args) != :fail end |