one_gadget 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +197 -65
  3. data/README.md +96 -22
  4. data/lib/one_gadget/abi.rb +41 -5
  5. data/lib/one_gadget/builds/libc-2.31-93b46e0027747153e336c3fd9431ce9a5d82ad00.rb +563 -0
  6. data/lib/one_gadget/builds/libc-2.35-891c1403437a4e30e684e0c8e34b87a09e4298e5.rb +434 -0
  7. data/lib/one_gadget/builds/libc-2.39-a1d1cf4badf1f5dfe57ff1d17bd692ccc6fcd5c5.rb +531 -0
  8. data/lib/one_gadget/builds/libc-2.39-cd8f5a207dd67aea370d2b471a54c3e56f44ab18.rb +531 -0
  9. data/lib/one_gadget/builds/libc-2.43-b50ceafbd17dc6bceee344a66671c7eaa152bef4.rb +15 -0
  10. data/lib/one_gadget/emulators/aarch64.rb +5 -2
  11. data/lib/one_gadget/emulators/amd64.rb +1 -0
  12. data/lib/one_gadget/emulators/arm.rb +27 -18
  13. data/lib/one_gadget/emulators/arm_family.rb +35 -160
  14. data/lib/one_gadget/emulators/conditional.rb +28 -22
  15. data/lib/one_gadget/emulators/constraints.rb +269 -0
  16. data/lib/one_gadget/emulators/data_processing.rb +167 -0
  17. data/lib/one_gadget/emulators/i386.rb +4 -6
  18. data/lib/one_gadget/emulators/instruction.rb +24 -1
  19. data/lib/one_gadget/emulators/lambda.rb +3 -1
  20. data/lib/one_gadget/emulators/mips.rb +289 -0
  21. data/lib/one_gadget/emulators/processor.rb +33 -455
  22. data/lib/one_gadget/emulators/register_file.rb +19 -10
  23. data/lib/one_gadget/emulators/riscv64.rb +265 -0
  24. data/lib/one_gadget/emulators/safe_calls.rb +9 -3
  25. data/lib/one_gadget/emulators/tracked_memory.rb +209 -0
  26. data/lib/one_gadget/emulators/x86.rb +32 -22
  27. data/lib/one_gadget/fetchers/aarch64.rb +0 -27
  28. data/lib/one_gadget/fetchers/amd64.rb +0 -24
  29. data/lib/one_gadget/fetchers/argument_resolution.rb +340 -0
  30. data/lib/one_gadget/fetchers/arm.rb +99 -44
  31. data/lib/one_gadget/fetchers/base.rb +142 -640
  32. data/lib/one_gadget/fetchers/candidate_walk.rb +150 -0
  33. data/lib/one_gadget/fetchers/disassembly.rb +252 -0
  34. data/lib/one_gadget/fetchers/dynamic_symbols.rb +104 -0
  35. data/lib/one_gadget/fetchers/i386.rb +5 -8
  36. data/lib/one_gadget/fetchers/mips.rb +478 -0
  37. data/lib/one_gadget/fetchers/objdump.rb +24 -2
  38. data/lib/one_gadget/fetchers/riscv64.rb +78 -0
  39. data/lib/one_gadget/fetchers/x86.rb +19 -0
  40. data/lib/one_gadget/fetchers.rb +22 -6
  41. data/lib/one_gadget/gadget.rb +25 -34
  42. data/lib/one_gadget/helper.rb +19 -5
  43. data/lib/one_gadget/one_gadget.rb +6 -0
  44. data/lib/one_gadget/version.rb +1 -1
  45. data/lib/one_gadget.rb +1 -1
  46. metadata +18 -6
  47. data/lib/one_gadget/builds/libc-2.26-2104f3d4ad5cf68603afbe7ba1a17f5ac99c5988.rb +0 -227
  48. data/lib/one_gadget/builds/libc-2.26-ddcc13122ddbfe5e5ef77d4ebe66d124ae5762c2.rb +0 -300
  49. data/lib/one_gadget/builds/libc-2.26-f65648a832414f2144ce795d75b6045a1ec2e252.rb +0 -199
@@ -0,0 +1,289 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'one_gadget/abi'
4
+ require 'one_gadget/emulators/instruction'
5
+ require 'one_gadget/emulators/processor'
6
+
7
+ module OneGadget
8
+ module Emulators
9
+ # Emulator of MIPS (32-bit, o32).
10
+ class Mips < Processor
11
+ # Instantiate a {Mips} object.
12
+ def initialize
13
+ super(OneGadget::ABI.mips, 'sp')
14
+ @registers['zero'] = 0 # hardwired
15
+ @pc = 'pc'
16
+ end
17
+
18
+ # This arch compares and branches in one instruction -- there is no flag
19
+ # register -- so each mnemonic names the relation its operands must stand in
20
+ # for the branch to be taken. The spellings against zero name one operand;
21
+ # +zero+ is the other, and reads as the +0x0+ it holds.
22
+ # @return [Hash{String => Symbol}]
23
+ COND = {
24
+ 'beq' => :eq, 'bne' => :ne,
25
+ 'beqz' => :eq, 'bnez' => :ne,
26
+ 'blez' => :sle, 'bgtz' => :sgt, 'bltz' => :slt, 'bgez' => :sge
27
+ }.freeze
28
+
29
+ # The data-processing instructions, mapped to the Ruby operator that folds
30
+ # them. An immediate form differs from its register one only in how the right
31
+ # operand is written, which the operand reader already handles, so both name
32
+ # the same operator. Arithmetic shift right is absent: a value is held masked
33
+ # to its width, where +>>+ is the logical shift.
34
+ DATA_OPS = {
35
+ 'and' => :&, 'andi' => :&,
36
+ 'or' => :|, 'ori' => :|,
37
+ 'xor' => :^, 'xori' => :^,
38
+ 'sll' => :<<, 'srl' => :>>
39
+ }.freeze
40
+ private_constant :DATA_OPS
41
+
42
+ # The loads, mapped to how many bytes each reads.
43
+ LOADS = { 'lw' => 4, 'lh' => 2, 'lhu' => 2, 'lb' => 1, 'lbu' => 1 }.freeze
44
+ private_constant :LOADS
45
+
46
+ # The stores, mapped to how many bytes each writes.
47
+ STORES = { 'sw' => 4, 'sh' => 2, 'sb' => 1 }.freeze
48
+ private_constant :STORES
49
+
50
+ # Supported instruction set. Anything not listed aborts the candidate.
51
+ # @return [Array<Instruction>] The supported instructions.
52
+ def instructions
53
+ [
54
+ Instruction.new('addiu', 3),
55
+ Instruction.new('addu', 3),
56
+ Instruction.new('bal', 1),
57
+ Instruction.new('jal', 1),
58
+ Instruction.new('jalr', 1..2),
59
+ Instruction.new('li', 2),
60
+ Instruction.new('lui', 2),
61
+ Instruction.new('move', 2),
62
+ Instruction.new('nop', 0),
63
+ Instruction.new('subu', 3)
64
+ ] + (LOADS.keys + STORES.keys).map { |mnem| Instruction.new(mnem, 2) } +
65
+ DATA_OPS.keys.map { |mnem| Instruction.new(mnem, 3) }
66
+ end
67
+
68
+ # The value of a call's +idx+-th argument. o32 states the first four in
69
+ # registers, and reserves a stack slot for every argument including those --
70
+ # so an argument's slot is its index however it is passed, and the ones past
71
+ # the registers are read from there.
72
+ # @param [Integer] idx The 0-based index of the argument.
73
+ # @return [Lambda, Integer]
74
+ def argument(idx)
75
+ return registers["a#{idx}"] if idx < ARG_REGISTERS
76
+
77
+ top = registers['sp'].evaluate('sp' => 0)
78
+ sp_based_stack[top + (idx * size_t)]
79
+ end
80
+
81
+ # Emulate one instruction, holding back any transfer of control.
82
+ #
83
+ # This arch delays every transfer by one instruction: whatever follows a
84
+ # branch or a call runs *before* it takes effect. So a transfer is held back
85
+ # here and applied once that instruction has run, which is the order they
86
+ # really happen in -- and it leaves both addresses of the pair meaning what
87
+ # they say, since entering at the transfer runs both, while entering at the
88
+ # instruction after it runs only that one.
89
+ # @param [String] cmd One line from result of objdump.
90
+ # @return [Boolean] If successfully processed.
91
+ # @see OneGadget::Emulators::X86#process!
92
+ def process!(cmd)
93
+ resolve_pending_branch(cmd)
94
+ @cur_addr = cmd[/\A\s*([0-9a-f]+):/, 1]&.to_i(16)
95
+
96
+ @got_value = cmd[GOT_VALUE, 1]&.to_i(16)
97
+
98
+ mnem = mnemonic(cmd)
99
+ if transfer?(mnem)
100
+ @delayed = [mnem, cmd]
101
+ return true
102
+ end
103
+
104
+ dispatch(cmd) && apply_delayed
105
+ end
106
+
107
+ # How many arguments this ABI states in registers.
108
+ ARG_REGISTERS = 4
109
+ private_constant :ARG_REGISTERS
110
+
111
+ private
112
+
113
+ # What a GOT slot holds, stated beside the load by
114
+ # +OneGadget::Fetchers::Mips+ because this architecture reaches its globals
115
+ # through that table rather than through the instruction's own address.
116
+ GOT_VALUE = /#\s*([0-9a-f]+)\s*\z/
117
+ private_constant :GOT_VALUE
118
+
119
+ # The calls, which are delayed exactly as the branches are.
120
+ CALLS = %w[jal jalr bal].freeze
121
+ private_constant :CALLS
122
+
123
+ def branch_mnem?(mnem) = mnem == 'b' || COND.key?(mnem)
124
+
125
+ # Whether +mnem+ transfers control, and so takes effect one instruction late.
126
+ def transfer?(mnem) = branch_mnem?(mnem) || CALLS.include?(mnem)
127
+
128
+ def dispatch(cmd)
129
+ inst, args = parse(cmd)
130
+ __send__(inst.handler, *args) != :fail
131
+ end
132
+
133
+ # Carry out the transfer held back from the previous instruction, now that
134
+ # the instruction it delays behind has run.
135
+ # @return [Boolean]
136
+ def apply_delayed
137
+ return true if @delayed.nil?
138
+
139
+ mnem, cmd = @delayed
140
+ @delayed = nil
141
+ return handle_branch(mnem, cmd) != :fail if branch_mnem?(mnem)
142
+
143
+ dispatch(cmd)
144
+ end
145
+
146
+ # Operands of +cmd+ (mnemonic dropped), each stripped of a trailing +<symbol>+.
147
+ def operands(cmd)
148
+ cmd.sub(/\A[0-9a-f]+:\s*\S+\s*/, '').split(',').map { |o| o.strip.sub(/\s*<.*>\z/, '') }
149
+ end
150
+
151
+ # Record the comparison and decide the branch together, since one
152
+ # instruction is both (see {COND}).
153
+ def handle_branch(mnem, cmd)
154
+ return true if mnem == 'b' # unconditional: control handled by the stitched path
155
+
156
+ ops = operands(cmd)
157
+ lhs, rhs = mnem.end_with?('z') ? [ops[0], 'zero'] : ops[0..1]
158
+ record_compare(:sub, operand_str(lhs), operand_str(rhs))
159
+ branch_on_compare(COND[mnem], ops.last.to_i(16))
160
+ end
161
+
162
+ # A call: record the terminal +exec*+ target, accept a known-safe libc call,
163
+ # or +:fail+ to abort the candidate. This arch reaches most of libc through a
164
+ # register, so the name comes from what the fetcher wrote beside it
165
+ # (+OneGadget::Fetchers::Mips+); a call it could not name is one the emulator
166
+ # cannot reason about either, which {Processor#dispatch_safe_call} refuses.
167
+ # @param [String] addr The call target, as the line names it.
168
+ # @return [nil, :fail]
169
+ def call_target(addr)
170
+ return reach_terminal_call(addr) if terminal_call?(addr)
171
+
172
+ dispatch_safe_call(addr)
173
+ end
174
+
175
+ # +jalr+ takes the register holding the target, optionally preceded by the
176
+ # register the return address goes to; the target is the last either way.
177
+ def inst_jalr(*args) = call_target(args.last)
178
+
179
+ # A direct call, which states its target outright.
180
+ def inst_jal(addr) = call_target(addr)
181
+
182
+ # +bal+ is the pc-relative direct call; it names its target as +jal+ does.
183
+ def inst_bal(addr) = call_target(addr)
184
+
185
+ def inst_nop = true
186
+
187
+ def inst_move(dst, src)
188
+ check_register!(dst)
189
+
190
+ registers[dst] = arg_to_lambda(src)
191
+ end
192
+
193
+ # +li+ is the assembler's spelling of loading a constant, whatever the
194
+ # instructions it expands to.
195
+ def inst_li(dst, imm)
196
+ check_register!(dst)
197
+
198
+ registers[dst] = Integer(imm)
199
+ end
200
+
201
+ # +lui dst, imm+ loads the immediate into the upper half of the register.
202
+ def inst_lui(dst, imm)
203
+ check_register!(dst)
204
+
205
+ registers[dst] = (imm.to_i(16) << 16) & width_mask
206
+ end
207
+
208
+ # +addu dst, src, op2+; +addiu+ differs only in that its right operand is
209
+ # written as a literal. Neither traps on overflow, which is why the compiler
210
+ # uses them for address arithmetic and the trapping forms are not modelled.
211
+ def inst_addu(dst, src, op2) = arith(:+, dst, src, op2)
212
+ alias inst_addiu inst_addu
213
+
214
+ # +subu dst, src, op2+. See {#inst_addu}.
215
+ def inst_subu(dst, src, op2) = arith(:-, dst, src, op2)
216
+
217
+ # Each data-processing mnemonic handled the one way, since they differ only
218
+ # in the operator applied.
219
+ DATA_OPS.each do |mnem, op|
220
+ define_method(Instruction.handler_name(mnem)) do |dst, src, op2|
221
+ data_op(op, dst, src, op2, name: mnem)
222
+ end
223
+ end
224
+
225
+ LOADS.each do |mnem, size|
226
+ define_method(Instruction.handler_name(mnem)) { |dst, mem| load_value(dst, mem, size) }
227
+ end
228
+
229
+ STORES.each do |mnem, size|
230
+ define_method(Instruction.handler_name(mnem)) { |src, mem| store_value(src, mem, size) }
231
+ end
232
+
233
+ # A load. The address is read like any other, so what the caller has to
234
+ # arrange about it is recorded the same way. A load narrower than a register
235
+ # only takes part of the word this emulator tracks, which is not a value it
236
+ # can name, so the register then holds what a call would have left.
237
+ # @param [String] dst The destination register.
238
+ # @param [String] mem The memory operand, as written.
239
+ # @param [Integer] size How many bytes the load reads.
240
+ # @return [void]
241
+ def load_value(dst, mem, size)
242
+ check_register!(dst)
243
+ # a GOT slot holds an address, and the file says which
244
+ return registers[dst] = libc_base + @got_value if @got_value && size == size_t
245
+
246
+ value = read_value(arg_to_lambda(mem_operand(mem)))
247
+ registers[dst] = size == size_t ? value : clobbered_value
248
+ end
249
+
250
+ # A store, tracked so a later load of the same slot reads it back, and
251
+ # requiring its target writable ({Processor#track_write}). A store narrower
252
+ # than a register leaves the rest of the slot as it was, so what the slot
253
+ # then holds is named as unknown rather than as the value stored.
254
+ # @param [String] src The register holding the value stored.
255
+ # @param [String] mem The memory operand, as written.
256
+ # @param [Integer] size How many bytes the store writes.
257
+ # @return [void]
258
+ def store_value(src, mem, size)
259
+ check_register!(src)
260
+
261
+ dst_l = arg_to_lambda(mem_operand(mem)).ref!
262
+ track_write(dst_l, size == size_t ? registers[src] : clobbered_value)
263
+ end
264
+
265
+ # This arch writes a memory operand as an offset applied to one register,
266
+ # which the {Lambda} parser reads in its bracketed form. The offset is
267
+ # decimal, as objdump prints it.
268
+ # @param [String] mem The operand, as written.
269
+ # @return [String]
270
+ # @example
271
+ # mem_operand('-31652(gp)') #=> '[gp-31652]'
272
+ # mem_operand('4(s6)') #=> '[s6+4]'
273
+ def mem_operand(mem)
274
+ m = mem.match(/\A(-?\d+)\((\w+)\)\z/)
275
+ raise_unsupported('memory operand', mem) if m.nil?
276
+
277
+ "[#{m[2]}#{format('%+d', Integer(m[1]))}]"
278
+ end
279
+
280
+ class << self
281
+ # o32 is 32-bit.
282
+ # @return [Integer]
283
+ def bits
284
+ 32
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end