Skip to content

Commit 8ce4b72

Browse files
authored
Merge pull request #70 from angr/wip/ropvalue
Wip/ropvalue
2 parents 2a8a21e + a5e3818 commit 8ce4b72

File tree

7 files changed

+208
-66
lines changed

7 files changed

+208
-66
lines changed

angrop/chain_builder/__init__.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
from .. import rop_utils
1313
from .. import common
1414
from ..errors import RopException
15-
from ..rop_chain import RopChain
1615
from ..rop_gadget import RopGadget
16+
from ..rop_value import RopValue
1717

1818
l = logging.getLogger("angrop.chain_builder")
1919

@@ -75,6 +75,11 @@ def _contain_badbyte(self, ptr):
7575
"""
7676
check if a pointer contains any bad byte
7777
"""
78+
if isinstance(ptr, RopValue):
79+
if ptr.symbolic:
80+
return False
81+
else:
82+
ptr = ptr.concreted
7883
raw_bytes = struct.pack(self.project.arch.struct_fmt(), ptr)
7984
if any(x in raw_bytes for x in self.badbytes):
8085
return True
@@ -236,15 +241,15 @@ def add_to_mem(self, addr, value, data_size=None):
236241
Example:
237242
chain = rop.add_to_mem(0x8048f124, 0x41414141)
238243
"""
244+
addr = rop_utils.cast_rop_value(addr, self.project)
245+
value = rop_utils.cast_rop_value(value, self.project)
239246
return self._mem_writer.add_to_mem(addr, value, data_size=data_size)
240247

241248
def write_to_mem(self, addr, data, fill_byte=b"\xff"):
249+
addr = rop_utils.cast_rop_value(addr, self.project)
242250
return self._mem_writer.write_to_mem(addr, data, fill_byte=fill_byte)
243251

244252
def _try_invoke_execve(self, path_addr):
245-
cc = angr.SYSCALL_CC[self.project.arch.name]["default"](self.project.arch)
246-
arg_regs = cc.ARG_REGS
247-
248253
# next, try to invoke execve(path, ptr, ptr), where ptr points is either NULL or nullptr
249254
if 0 not in self.badbytes:
250255
ptr = 0

angrop/chain_builder/mem_writer.py

+38-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .. import rop_utils
88
from ..errors import RopException
99
from ..rop_chain import RopChain
10+
from ..rop_value import RopValue
1011

1112
l = logging.getLogger("angrop.chain_builder.mem_writer")
1213

@@ -66,6 +67,11 @@ def _contain_badbyte(self, ptr):
6667
"""
6768
check if a pointer contains any bad byte
6869
"""
70+
if isinstance(ptr, RopValue):
71+
if ptr.symbolic:
72+
return False
73+
else:
74+
ptr = ptr.concreted
6975
raw_bytes = struct.pack(self.project.arch.struct_fmt(), ptr)
7076
if any(x in raw_bytes for x in self.badbytes):
7177
return True
@@ -160,7 +166,6 @@ def _write_to_mem(self, addr, string_data, fill_byte=b"\xff"):# pylint:disable=i
160166

161167
gen = self._gen_mem_write_gadgets(string_data)
162168
gadget, use_partial_controllers = next(gen, (None, None))
163-
164169
while gadget:
165170
try:
166171
return self._try_write_to_mem(gadget, use_partial_controllers, addr, string_data, fill_byte)
@@ -263,7 +268,7 @@ def write_to_mem(self, addr, data, fill_byte=b"\xff"):
263268

264269
# do the write
265270
offset = 0
266-
chain = RopChain(self.project, self, badbytes=self.badbytes)
271+
chain = RopChain(self.project, self, badbytes=self.badbytes)
267272
for elem in elems:
268273
ptr = addr + offset
269274
if self._contain_badbyte(ptr):
@@ -314,8 +319,12 @@ def add_to_mem(self, addr, value, data_size=None):
314319
chain = self._change_mem_with_gadget(best_gadget, addr, data_size, difference=value)
315320
return chain
316321

322+
def _write_to_mem_with_gadget(self, gadget, addr_val, data, use_partial_controllers=False):
323+
"""
324+
addr is a RopValue
325+
"""
326+
addr_bvs = claripy.BVS("addr", self.project.arch.bits)
317327

318-
def _write_to_mem_with_gadget(self, gadget, addr, data, use_partial_controllers=False):
319328
# sanity check for simple gadget
320329
if len(gadget.mem_writes) != 1 or len(gadget.mem_reads) + len(gadget.mem_changes) > 0:
321330
raise RopException("too many memory accesses for my lazy implementation")
@@ -355,26 +364,48 @@ def _write_to_mem_with_gadget(self, gadget, addr, data, use_partial_controllers=
355364
raise RopException("Couldn't find the matching action")
356365

357366
# constrain the addr
358-
test_state.add_constraints(the_action.addr.ast == addr)
359-
pre_gadget_state.add_constraints(the_action.addr.ast == addr)
367+
test_state.add_constraints(the_action.addr.ast == addr_bvs, addr_bvs == addr_val.data)
368+
pre_gadget_state.add_constraints(the_action.addr.ast == addr_bvs, addr_bvs = addr_val.data)
360369
pre_gadget_state.options.discard(angr.options.AVOID_MULTIVALUED_WRITES)
361370
state = rop_utils.step_to_unconstrained_successor(self.project, pre_gadget_state)
362371

363372
# constrain the data
364-
test_state.add_constraints(state.memory.load(addr, len(data)) == test_state.solver.BVV(data))
373+
test_state.add_constraints(state.memory.load(addr_val.data, len(data)) == test_state.solver.BVV(data))
365374

366375
# get the actual register values
367376
all_deps = list(mem_write.addr_dependencies) + list(mem_write.data_dependencies)
368377
reg_vals = {}
378+
name = addr_bvs._encoded_name.decode()
369379
for reg in set(all_deps):
370-
reg_vals[reg] = test_state.solver.eval(test_state.registers.load(reg))
380+
var = test_state.solver.eval(test_state.registers.load(reg))
381+
# check whether this reg will propagate to addr
382+
# if yes, propagate its rebase value
383+
for c in test_state.solver.constraints:
384+
if len(c.variables) != 2: # xx == yy
385+
continue
386+
if name not in c.variables:
387+
continue
388+
var_names = set(c.variables)
389+
var_names.remove(name)
390+
if reg in var_names.pop():
391+
var = rop_utils.cast_rop_value(var, self.project)
392+
if addr_val._rebase:
393+
var.rebase_ptr()
394+
break
395+
reg_vals[reg] = var
371396

372397
chain = self._set_regs(use_partial_controllers=use_partial_controllers, **reg_vals)
373398
chain.add_gadget(gadget)
374399

375400
bytes_per_pop = self.project.arch.bytes
376401
for _ in range(gadget.stack_change // bytes_per_pop - 1):
377402
chain.add_value(self._get_fill_val(), needs_rebase=False)
403+
404+
# verify the write actually works
405+
state = chain.exec()
406+
sim_data = state.memory.load(addr_val.data, len(data))
407+
if not state.solver.eval(sim_data == data):
408+
raise RopException("memory write fails")
378409
return chain
379410

380411
def _change_mem_with_gadget(self, gadget, addr, data_size, final_val=None, difference=None):

angrop/chain_builder/reg_setter.py

+45-13
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
from collections import defaultdict
55
from functools import cmp_to_key
66

7+
import claripy
78
from angr.errors import SimUnsatError
89

910

1011
from .. import rop_utils
1112
from ..rop_chain import RopChain
1213
from ..errors import RopException
14+
from ..rop_value import RopValue
1315

1416
l = logging.getLogger("angrop.chain_builder.reg_setter")
1517

@@ -32,6 +34,11 @@ def _contain_badbyte(self, ptr):
3234
"""
3335
check if a pointer contains any bad byte
3436
"""
37+
if isinstance(ptr, RopValue):
38+
if ptr.symbolic:
39+
return False
40+
else:
41+
ptr = ptr.concreted
3542
raw_bytes = struct.pack(self.project.arch.struct_fmt(), ptr)
3643
if any(x in raw_bytes for x in self._badbytes):
3744
return True
@@ -46,7 +53,13 @@ def verify(self, chain, registers):
4653
for reg, val in registers.items():
4754
chain_str = '\n-----\n'.join([str(self.project.factory.block(g.addr).capstone)for g in chain._gadgets])
4855
bv = getattr(state.regs, reg)
49-
if bv.symbolic or state.solver.eval(bv != val):
56+
for act in state.history.actions.hardcopy:
57+
if act.type != "mem":
58+
continue
59+
if act.addr.ast.variables:
60+
l.exception("memory access outside stackframe\n%s\n", chain_str)
61+
return False
62+
if bv.symbolic or state.solver.eval(bv != val.data):
5063
l.exception("Somehow angrop thinks \n%s\n can be used for the chain generation.", chain_str)
5164
return False
5265
return True
@@ -60,9 +73,10 @@ def run(self, modifiable_memory_range=None, use_partial_controllers=False, **re
6073
if unknown_regs:
6174
raise RopException("unknown registers: %s" % unknown_regs)
6275

63-
# load from cache
64-
#reg_tuple = tuple(sorted(registers.keys()))
65-
#chains = self._chain_cache[reg_tuple]
76+
# cast values to RopValue
77+
for x in registers:
78+
registers[x] = rop_utils.cast_rop_value(registers[x], self.project)
79+
6680
chains = []
6781

6882
gadgets = self._find_relevant_gadgets(**registers)
@@ -168,19 +182,20 @@ def _find_add_chain(self, gadgets, reg, val):
168182
"""
169183
find one chain to set one single register to a specific value using concrete values only through add/dec
170184
"""
185+
val = rop_utils.cast_rop_value(val, self.project)
171186
concrete_setter_gadgets = [ x for x in gadgets if reg in x.concrete_regs ]
172187
delta_gadgets = [ x for x in gadgets if len(x.reg_dependencies) == 1 and reg in x.reg_dependencies\
173188
and len(x.reg_dependencies[reg]) == 1 and reg in x.reg_dependencies[reg]]
174189
for g1 in concrete_setter_gadgets:
175190
for g2 in delta_gadgets:
176191
try:
177-
chain = self._build_reg_setting_chain([g1, g2], False,
178-
{reg: val}, g1.stack_change+g2.stack_change, [])
192+
chain = self._build_reg_setting_chain([g1, g2], False, # pylint:disable=too-many-function-args
193+
{reg: val}, g1.stack_change+g2.stack_change)
179194
state = chain.exec()
180195
bv = state.registers.load(reg)
181196
if bv.symbolic:
182197
continue
183-
if state.solver.eval(bv == val):
198+
if state.solver.eval(bv == val.data):
184199
return [g1, g2]
185200
except Exception:# pylint:disable=broad-except
186201
pass
@@ -193,16 +208,16 @@ def _find_all_candidate_chains(self, gadgets, **registers):
193208
TODO: handle moves
194209
"""
195210
# get the list of regs that cannot be popped (call it hard_regs)
196-
hard_regs = [reg for reg, val in registers.items() if type(val) == int and self._contain_badbyte(val)]
211+
hard_regs = [reg for reg, val in registers.items() if self._contain_badbyte(val)]
197212
if len(hard_regs) > 1:
198213
l.error("too many registers contain bad bytes! bail out! %s", registers)
199214
return []
200215

201216
# if hard_regs exists, try to use concrete values to craft the value
202217
hard_chain = []
203-
if hard_regs:
218+
if hard_regs and not registers[hard_regs[0]].symbolic:
204219
reg = hard_regs[0]
205-
val = registers[reg]
220+
val = registers[reg].concreted
206221
key = (reg, val)
207222
if key in self.hard_chain_cache:
208223
hard_chain = self.hard_chain_cache[key]
@@ -294,8 +309,13 @@ def _build_reg_setting_chain(self, gadgets, modifiable_memory_range, register_di
294309

295310
# constrain the final registers
296311
rebase_state = test_symbolic_state.copy()
312+
var_dict = {}
297313
for r, v in register_dict.items():
298-
test_symbolic_state.add_constraints(state.registers.load(r) == v)
314+
var = claripy.BVS(r, self.project.arch.bits)
315+
var_name = var._encoded_name.decode()
316+
var_dict[var_name] = v
317+
test_symbolic_state.add_constraints(state.registers.load(r) == var)
318+
test_symbolic_state.add_constraints(var == v.data)
299319

300320
# constrain the "filler" values
301321
if self._roparg_filler is not None:
@@ -323,7 +343,19 @@ def _build_reg_setting_chain(self, gadgets, modifiable_memory_range, register_di
323343
chain.add_gadget(gadgets[0])
324344
gadgets = gadgets[1:]
325345
else:
326-
chain.add_value(sym_word, needs_rebase=False)
346+
# propagate the initial RopValue provided by users to preserve info like rebase
347+
var = sym_word
348+
for c in test_symbolic_state.solver.constraints:
349+
if len(c.variables) != 2: # it is always xx == yy
350+
continue
351+
if not sym_word.variables.intersection(c.variables):
352+
continue
353+
var_name = set(c.variables - sym_word.variables).pop()
354+
if var_name not in var_dict:
355+
continue
356+
var = var_dict[var_name]
357+
break
358+
chain.add_value(var, needs_rebase=False)
327359

328360
if len(gadgets) > 0:
329361
raise RopException("Didnt find all gadget addresses, something must've broke")
@@ -377,7 +409,7 @@ def _find_reg_setting_gadgets(self, modifiable_memory_range=None, use_partial_co
377409
search_regs = set(registers)
378410

379411
if modifiable_memory_range is not None and len(modifiable_memory_range) != 2:
380-
raise Exception("modifiable_memory_range should be a tuple (low, high)")
412+
raise RopException("modifiable_memory_range should be a tuple (low, high)")
381413

382414
# find gadgets with sufficient partial control
383415
partial_controllers = {}

angrop/rop_chain.py

+16-26
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
from . import rop_utils
22
from .errors import RopException
3-
from .value import ROPValue
4-
5-
from cle.address_translator import AT
3+
from .rop_value import RopValue
64

75
class RopChain:
86
"""
@@ -38,12 +36,9 @@ def __add__(self, other):
3836
return result
3937

4038
def add_value(self, value, needs_rebase=False):
41-
if type(value) is not ROPValue:
42-
value = ROPValue(value)
43-
value.set_project(self._p)
44-
value.rebase_analysis()
45-
else:
46-
value.set_project(self._p)
39+
if type(value) is not RopValue:
40+
value = RopValue(value, self._p)
41+
value.rebase_analysis(chain=self)
4742
self._values.append(value)
4843
self.payload_len += self._p.arch.bytes
4944

@@ -53,8 +48,9 @@ def add_gadget(self, gadget):
5348
value = gadget.addr
5449
if self._pie:
5550
value -= self._p.loader.main_object.mapped_base
56-
value = ROPValue(value, project=self._p)
57-
value.set_rebase(True)
51+
value = RopValue(value, self._p)
52+
if self._pie:
53+
value._rebase = True
5854
self.add_value(value)
5955

6056
def add_constraint(self, cons):
@@ -71,7 +67,6 @@ def _concretize_chain_values(self, constraints=None):
7167
:param constraints: constraints to use when concretizing values
7268
:return: a list of tuples of type (int, needs_rebase)
7369
"""
74-
7570
solver_state = self._blank_state.copy()
7671
if constraints is not None:
7772
if isinstance(constraints, (list, tuple)):
@@ -82,12 +77,8 @@ def _concretize_chain_values(self, constraints=None):
8277

8378
concrete_vals = []
8479
for value in self._values:
85-
if not value.symbolic:
86-
concrete_vals.append((value.concreted, value.rebase))
87-
continue
88-
89-
# if it is symbolic, make sure it does not have badbytes in it
90-
ast = value.ast
80+
# make sure it does not have badbytes in it
81+
ast = value.data
9182
constraints = []
9283
# for each byte, it should not be equal to any bad bytes
9384
# TODO: we should do the badbyte verification when adding values
@@ -98,6 +89,8 @@ def _concretize_chain_values(self, constraints=None):
9889
# apply the constraints
9990
for expr in constraints:
10091
solver_state.solver.add(expr)
92+
if not solver_state.solver.satisfiable():
93+
raise RopException("bad chain!")
10194
concrete_vals.append((solver_state.solver.eval(ast), value.rebase))
10295

10396
return concrete_vals
@@ -126,8 +119,8 @@ def payload_bv(self):
126119

127120
test_state = self._blank_state.copy()
128121

129-
for value, _ in reversed(self._values):
130-
test_state.stack_push(value)
122+
for value in reversed(self._values):
123+
test_state.stack_push(value.data)
131124

132125
sp = test_state.regs.sp
133126
return test_state.memory.load(sp, self.payload_len)
@@ -156,17 +149,14 @@ def payload_code(self, constraints=None, print_instructions=True):
156149

157150
instruction_code = ""
158151
if print_instructions:
159-
if needs_rebase:
160-
#dealing with pie code
161-
value_in_gadget = AT.from_lva(value, self._p.loader.main_object).to_mva()
162-
else:
163-
value_in_gadget = value
152+
value_in_gadget = value
164153
if value_in_gadget in gadget_dict:
165-
asmstring = rop_utils.gadget_to_asmstring(self._p,gadget_dict[value_in_gadget])
154+
asmstring = rop_utils.gadget_to_asmstring(self._p, gadget_dict[value_in_gadget])
166155
if asmstring != "":
167156
instruction_code = "\t# " + asmstring
168157

169158
if needs_rebase:
159+
value -= self._p.loader.main_object.mapped_base
170160
payload += "chain += " + pack_rebase % value + instruction_code
171161
else:
172162
payload += "chain += " + pack % value + instruction_code

0 commit comments

Comments
 (0)