Class: OneGadget::Fetchers::Mips
- Defined in:
- lib/one_gadget/fetchers/mips.rb
Overview
Fetcher for MIPS (32-bit, o32).
Two things about this architecture are unlike every other one supported, and both are answered here so that nothing about them reaches the engine:
- a call states no target -- it goes through a register loaded from the GOT, so the callee's name has to be resolved and written where every other arch has one already (#name_got_calls);
- a branch or call has a delay slot: the instruction after it runs before it takes effect. Emulators::Mips#process! holds the transfer back so both run in that order, which leaves the disassembly in the order objdump wrote it and every address meaning what it says. Two seams follow from it, and they are the only ones: the instruction after a call belongs to the window that ends at the call (#emulate), and the edge into a branch's target leaves from the delay slot rather than the branch (#branch_pred_map).
Constant Summary
Constants included from DynamicSymbols
DynamicSymbols::CONTROL_TARGET, DynamicSymbols::TRAILING_ADDRESS
Constants included from Disassembly
Disassembly::TERMINAL_PREFIXES, Disassembly::TERMINAL_SPAWN, Disassembly::WINDOW_BACK, Disassembly::WINDOW_FWD
Constants included from CandidateWalk
CandidateWalk::MAX_FORKS, CandidateWalk::PATH_BUDGET
Instance Attribute Summary
Attributes inherited from Base
Instance Method Summary collapse
-
#environ?(str) ⇒ Boolean
This arch reads a global through the GOT and resolves the slot to the address it holds, so what reaches a call is one dereference of the variable rather than two of the slot naming it.
-
#executed_windows(lines) {|window| ... } ⇒ void
A candidate may begin at a delay slot -- entering there runs it and falls past the transfer it belongs to -- but it may not then follow that transfer, which never executed.
-
#got_preconditions(processor, got) ⇒ Array<String>
What the caller must arrange for this window to reach the GOT.
-
#resolve(processor) ⇒ Hash?
Everything this arch reaches -- its calls and its globals alike -- goes through the GOT base in
gp, which is a precondition the caller arranges by setting that register.
Methods inherited from Base
cached, #find, #initialize, #refused_before_call?, #resolve_suffix, #terminal_call_line?
Methods included from CandidateWalk
Constructor Details
This class inherits a constructor from OneGadget::Fetchers::Base
Instance Method Details
#environ?(str) ⇒ Boolean
This arch reads a global through the GOT and resolves the slot to the address it holds, so what reaches a call is one dereference of the variable rather than two of the slot naming it. Which variable that is comes from the symbols already read for the table.
52 53 54 55 56 57 |
# File 'lib/one_gadget/fetchers/mips.rb', line 52 def environ?(str) got = mips_got or return false offset = string_file_offset(str.delete('[]')) or return false ENVIRON.match?(got[:names][offset].to_s) end |
#executed_windows(lines) {|window| ... } ⇒ void
This method returns an undefined value.
A candidate may begin at a delay slot -- entering there runs it and falls past the transfer it belongs to -- but it may not then follow that transfer, which never executed. Such a window shows it by its second line not being the next instruction along; entering one instruction earlier, at the transfer itself, is the separate and valid window that does follow it.
86 87 88 89 90 91 92 93 |
# File 'lib/one_gadget/fetchers/mips.rb', line 86 def executed_windows(lines) super do |window| next if follows_a_transfer_it_skipped?(window) || enters_at_an_unset_call?(window) next if calls_without_the_callee_in_t9?(window) yield(window) end end |
#got_preconditions(processor, got) ⇒ Array<String>
What the caller must arrange for this window to reach the GOT. Normally just the register itself -- but o32 has the caller restore it after every call, because the callee establishes its own, so a window that runs past a call reads the table through whatever it restored from. Every call it makes after that point was named on the assumption that this is the GOT, so say so rather than leaving it unsaid.
71 72 73 74 75 76 |
# File 'lib/one_gadget/fetchers/mips.rb', line 71 def got_preconditions(processor, got) held = processor.registers[GOT_BASE].to_s return [got] if held == GOT_BASE [got, "#{held} is the GOT address of libc"] end |
#resolve(processor) ⇒ Hash?
Everything this arch reaches -- its calls and its globals alike -- goes
through the GOT base in gp, which is a precondition the caller arranges
by setting that register. Say so, and drop the read/write requirements
rooted there: the GOT is a fixed, mapped libc address, so reaching through
it asks nothing further of the caller (as i386 does for its own GOT
register).
35 36 37 38 39 40 41 42 43 44 |
# File 'lib/one_gadget/fetchers/mips.rb', line 35 def resolve(processor) res = super return if res.nil? got = got_base_constraint(processor, GOT_BASE) or return nil res[:constraints].unshift(*got_preconditions(processor, got)) res[:constraints].reject! { |con| con.match?(/\A(?:writable|readable): \[*#{GOT_BASE}\b/) } res end |