angr 9.2.141__py3-none-manylinux2014_aarch64.whl → 9.2.143__py3-none-manylinux2014_aarch64.whl
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.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/calling_convention.py +26 -12
- angr/analyses/calling_convention/fact_collector.py +31 -9
- angr/analyses/cfg/cfg_base.py +38 -4
- angr/analyses/cfg/cfg_fast.py +23 -7
- angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +12 -1
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +8 -1
- angr/analyses/class_identifier.py +8 -7
- angr/analyses/complete_calling_conventions.py +19 -6
- angr/analyses/decompiler/ail_simplifier.py +138 -98
- angr/analyses/decompiler/clinic.py +73 -5
- angr/analyses/decompiler/condition_processor.py +7 -7
- angr/analyses/decompiler/decompilation_cache.py +2 -1
- angr/analyses/decompiler/decompiler.py +10 -2
- angr/analyses/decompiler/dephication/graph_vvar_mapping.py +4 -6
- angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +8 -2
- angr/analyses/decompiler/optimization_passes/condition_constprop.py +110 -46
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +8 -0
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +2 -0
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +29 -7
- angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +6 -0
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +9 -1
- angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +15 -1
- angr/analyses/decompiler/region_identifier.py +70 -47
- angr/analyses/decompiler/sequence_walker.py +8 -0
- angr/analyses/decompiler/ssailification/rewriting.py +47 -17
- angr/analyses/decompiler/ssailification/rewriting_engine.py +13 -0
- angr/analyses/decompiler/stack_item.py +36 -0
- angr/analyses/decompiler/structured_codegen/c.py +14 -9
- angr/analyses/decompiler/structuring/phoenix.py +3 -3
- angr/analyses/decompiler/utils.py +13 -0
- angr/analyses/find_objects_static.py +2 -1
- angr/analyses/reaching_definitions/engine_vex.py +13 -0
- angr/analyses/reaching_definitions/function_handler.py +24 -10
- angr/analyses/reaching_definitions/function_handler_library/stdio.py +1 -0
- angr/analyses/reaching_definitions/function_handler_library/stdlib.py +45 -12
- angr/analyses/reaching_definitions/function_handler_library/string.py +77 -21
- angr/analyses/reaching_definitions/function_handler_library/unistd.py +21 -1
- angr/analyses/reaching_definitions/rd_state.py +11 -7
- angr/analyses/s_liveness.py +44 -6
- angr/analyses/s_propagator.py +40 -29
- angr/analyses/s_reaching_definitions/s_rda_model.py +48 -37
- angr/analyses/s_reaching_definitions/s_rda_view.py +6 -3
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +21 -21
- angr/analyses/typehoon/simple_solver.py +35 -8
- angr/analyses/typehoon/typehoon.py +3 -1
- angr/analyses/variable_recovery/engine_ail.py +6 -6
- angr/calling_conventions.py +20 -10
- angr/knowledge_plugins/functions/function.py +5 -10
- angr/knowledge_plugins/variables/variable_manager.py +27 -0
- angr/procedures/definitions/__init__.py +3 -10
- angr/procedures/definitions/linux_kernel.py +5 -0
- angr/procedures/definitions/wdk_ntoskrnl.py +2 -0
- angr/procedures/win32_kernel/__fastfail.py +15 -0
- angr/sim_procedure.py +2 -2
- angr/simos/simos.py +14 -10
- angr/simos/windows.py +42 -1
- angr/utils/ail.py +41 -1
- angr/utils/cpp.py +17 -0
- angr/utils/doms.py +149 -0
- angr/utils/library.py +1 -1
- angr/utils/ssa/__init__.py +21 -14
- angr/utils/ssa/vvar_uses_collector.py +2 -2
- angr/utils/types.py +12 -1
- {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/METADATA +7 -7
- {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/RECORD +71 -67
- {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/LICENSE +0 -0
- {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/WHEEL +0 -0
- {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/entry_points.txt +0 -0
- {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,7 @@ from ailment.statement import Statement, ConditionalJump, Jump, Label, Return
|
|
|
14
14
|
from ailment.expression import Const, UnaryOp, MultiStatementExpression
|
|
15
15
|
|
|
16
16
|
from angr.utils.graph import GraphUtils
|
|
17
|
-
from angr.utils.ail import is_phi_assignment
|
|
17
|
+
from angr.utils.ail import is_phi_assignment, is_head_controlled_loop_block
|
|
18
18
|
from angr.knowledge_plugins.cfg import IndirectJump, IndirectJumpType
|
|
19
19
|
from angr.utils.constants import SWITCH_MISSING_DEFAULT_NODE_ADDR
|
|
20
20
|
from angr.utils.graph import dominates, to_acyclic_graph, dfs_back_edges
|
|
@@ -312,11 +312,11 @@ class PhoenixStructurer(StructurerBase):
|
|
|
312
312
|
and head_block.nodes
|
|
313
313
|
and isinstance(head_block.nodes[0], Block)
|
|
314
314
|
and head_block.nodes[0].statements
|
|
315
|
-
and
|
|
315
|
+
and is_head_controlled_loop_block(head_block.nodes[0])
|
|
316
316
|
) or (
|
|
317
317
|
isinstance(head_block, Block)
|
|
318
318
|
and head_block.statements
|
|
319
|
-
and
|
|
319
|
+
and is_head_controlled_loop_block(head_block)
|
|
320
320
|
):
|
|
321
321
|
# it's a while loop if the conditional jump (or the head block) is at the beginning of node
|
|
322
322
|
loop_type = "while" if head_block_idx == 0 else "do-while"
|
|
@@ -214,6 +214,19 @@ def switch_extract_switch_expr_from_jump_target(target: ailment.Expr.Expression)
|
|
|
214
214
|
target = target.operands[0]
|
|
215
215
|
else:
|
|
216
216
|
return None
|
|
217
|
+
elif target.op == "And":
|
|
218
|
+
# it must be and-ing the target expr with a constant
|
|
219
|
+
if (
|
|
220
|
+
isinstance(target.operands[1], ailment.Expr.VirtualVariable)
|
|
221
|
+
and isinstance(target.operands[0], ailment.Expr.Const)
|
|
222
|
+
) or (
|
|
223
|
+
isinstance(target.operands[0], ailment.Expr.VirtualVariable)
|
|
224
|
+
and isinstance(target.operands[1], ailment.Expr.Const)
|
|
225
|
+
):
|
|
226
|
+
break
|
|
227
|
+
return None
|
|
228
|
+
else:
|
|
229
|
+
return None
|
|
217
230
|
elif isinstance(target, ailment.Expr.Load):
|
|
218
231
|
# we want the address!
|
|
219
232
|
found_load = True
|
|
@@ -9,6 +9,7 @@ from angr.analyses.reaching_definitions.function_handler import FunctionHandler
|
|
|
9
9
|
from angr.knowledge_plugins.key_definitions.atoms import Register, MemoryLocation
|
|
10
10
|
from angr.storage.memory_mixins.paged_memory.pages.multi_values import MultiValues
|
|
11
11
|
from angr.knowledge_plugins.key_definitions.constants import OP_BEFORE, OP_AFTER
|
|
12
|
+
from angr.utils.cpp import is_cpp_funcname_ctor
|
|
12
13
|
from . import Analysis, VtableFinder, CFGFast, ReachingDefinitionsAnalysis
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
@@ -109,7 +110,7 @@ class NewFunctionHandler(FunctionHandler):
|
|
|
109
110
|
else:
|
|
110
111
|
if self.project.kb.functions.contains_addr(function_address):
|
|
111
112
|
func = self.project.kb.functions.get_by_addr(function_address)
|
|
112
|
-
if func is not None and
|
|
113
|
+
if func is not None and is_cpp_funcname_ctor(func.demangled_name):
|
|
113
114
|
# check if rdi has a possible this pointer/ object address, if so then we can assign this object
|
|
114
115
|
# this class
|
|
115
116
|
# also if the func is a constructor(not stripped binaries)
|
|
@@ -77,12 +77,15 @@ class SimEngineRDVEX(
|
|
|
77
77
|
def _process_block_end(self, stmt_result, whitelist):
|
|
78
78
|
self.stmt_idx = DEFAULT_STATEMENT
|
|
79
79
|
self._set_codeloc()
|
|
80
|
+
|
|
81
|
+
function_handled = False
|
|
80
82
|
if self.block.vex.jumpkind == "Ijk_Call":
|
|
81
83
|
# it has to be a function
|
|
82
84
|
block_next = self.block.vex.next
|
|
83
85
|
assert isinstance(block_next, pyvex.expr.IRExpr)
|
|
84
86
|
addr = self._expr_bv(block_next)
|
|
85
87
|
self._handle_function(addr)
|
|
88
|
+
function_handled = True
|
|
86
89
|
elif self.block.vex.jumpkind == "Ijk_Boring":
|
|
87
90
|
# test if the target addr is a function or not
|
|
88
91
|
block_next = self.block.vex.next
|
|
@@ -94,6 +97,16 @@ class SimEngineRDVEX(
|
|
|
94
97
|
if addr_int in self.functions:
|
|
95
98
|
# yes it's a jump to a function
|
|
96
99
|
self._handle_function(addr)
|
|
100
|
+
function_handled = True
|
|
101
|
+
|
|
102
|
+
# take care of OP_AFTER during statement processing for function calls in a block
|
|
103
|
+
if self.state.analysis and function_handled:
|
|
104
|
+
self.state.analysis.stmt_observe(
|
|
105
|
+
self.stmt_idx, self.block.vex.statements[-1], self.block, self.state, OP_AFTER
|
|
106
|
+
)
|
|
107
|
+
self.state.analysis.insn_observe(
|
|
108
|
+
self.ins_addr, self.block.vex.statements[-1], self.block, self.state, OP_AFTER
|
|
109
|
+
)
|
|
97
110
|
|
|
98
111
|
return self.state
|
|
99
112
|
|
|
@@ -121,9 +121,9 @@ class FunctionCallData:
|
|
|
121
121
|
return False
|
|
122
122
|
if isinstance(dest, MemoryLocation) and isinstance(dest.addr, SpOffset):
|
|
123
123
|
for effect in self.effects:
|
|
124
|
-
if not isinstance(effect.dest, MemoryLocation) or not isinstance(effect.dest.addr, SpOffset):
|
|
125
|
-
continue
|
|
126
124
|
stkarg = effect.dest
|
|
125
|
+
if not isinstance(stkarg, MemoryLocation) or not isinstance(stkarg.addr, SpOffset):
|
|
126
|
+
continue
|
|
127
127
|
if (
|
|
128
128
|
dest.addr.offset + dest.size <= stkarg.addr.offset
|
|
129
129
|
or stkarg.addr.offset + stkarg.size <= dest.addr.offset
|
|
@@ -282,12 +282,20 @@ class FunctionHandler:
|
|
|
282
282
|
A mechanism for summarizing a function call's effect on a program for ReachingDefinitionsAnalysis.
|
|
283
283
|
"""
|
|
284
284
|
|
|
285
|
-
def __init__(self, interfunction_level: int = 0, extra_impls: Iterable[FunctionHandler] | None = None):
|
|
285
|
+
def __init__(self, interfunction_level: int = 0, extra_impls: Iterable[type[FunctionHandler]] | None = None):
|
|
286
|
+
"""
|
|
287
|
+
:param interfunction_level: Maximum depth in to continue local function exploration
|
|
288
|
+
:param extra_impls: FunctionHandler classes to implement beyond what's implemented in function_handler_library
|
|
289
|
+
"""
|
|
290
|
+
|
|
286
291
|
self.interfunction_level: int = interfunction_level
|
|
287
292
|
|
|
288
|
-
if extra_impls is
|
|
289
|
-
|
|
290
|
-
|
|
293
|
+
if extra_impls is None:
|
|
294
|
+
return
|
|
295
|
+
|
|
296
|
+
for extra_handler in extra_impls:
|
|
297
|
+
for cls in extra_handler.__mro__:
|
|
298
|
+
for name, func in vars(cls).items():
|
|
291
299
|
if name.startswith("handle_impl_"):
|
|
292
300
|
setattr(self, name, _mk_wrapper(func, self))
|
|
293
301
|
|
|
@@ -398,9 +406,13 @@ class FunctionHandler:
|
|
|
398
406
|
for typelib_name in prototype_lib.type_collection_names:
|
|
399
407
|
type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
|
|
400
408
|
if type_collections:
|
|
401
|
-
|
|
409
|
+
prototype = dereference_simtype(data.prototype, type_collections).with_arch(state.arch)
|
|
410
|
+
data.prototype = cast(SimTypeFunction, prototype)
|
|
402
411
|
|
|
403
|
-
|
|
412
|
+
if isinstance(data.prototype, SimTypeFunction):
|
|
413
|
+
args_atoms_from_values = data.reset_prototype(data.prototype, state, soft_reset=True)
|
|
414
|
+
else:
|
|
415
|
+
args_atoms_from_values = set()
|
|
404
416
|
|
|
405
417
|
# PROCESS
|
|
406
418
|
state.move_codelocs(data.function_codeloc)
|
|
@@ -506,7 +518,9 @@ class FunctionHandler:
|
|
|
506
518
|
assert data.prototype is not None
|
|
507
519
|
if data.prototype.returnty is not None:
|
|
508
520
|
if not isinstance(data.prototype.returnty, SimTypeBottom):
|
|
509
|
-
data.ret_values = MultiValues(
|
|
521
|
+
data.ret_values = MultiValues(
|
|
522
|
+
state.top(data.prototype.returnty.with_arch(state.arch).size or state.arch.bits)
|
|
523
|
+
)
|
|
510
524
|
else:
|
|
511
525
|
data.ret_values = MultiValues(state.top(state.arch.bits))
|
|
512
526
|
if data.guessed_prototype:
|
|
@@ -567,7 +581,7 @@ class FunctionHandler:
|
|
|
567
581
|
sub_rda = state.analysis.project.analyses.ReachingDefinitions(
|
|
568
582
|
data.function,
|
|
569
583
|
observe_all=state.analysis._observe_all,
|
|
570
|
-
observation_points=(state.analysis._observation_points or [])
|
|
584
|
+
observation_points=list(state.analysis._observation_points or []).extend(return_observation_points),
|
|
571
585
|
observe_callback=state.analysis._observe_callback,
|
|
572
586
|
dep_graph=state.dep_graph,
|
|
573
587
|
function_handler=self,
|
|
@@ -202,6 +202,7 @@ def handle_printf(
|
|
|
202
202
|
for defn in state.get_definitions(atom):
|
|
203
203
|
top_val = state.annotate_with_def(top_val, defn)
|
|
204
204
|
buf_data = MultiValues(top_val)
|
|
205
|
+
buf_atoms = atom
|
|
205
206
|
elif fmt == "%u":
|
|
206
207
|
buf_atoms = atom
|
|
207
208
|
buf_data = state.get_concrete_value(buf_atoms)
|
|
@@ -7,7 +7,7 @@ import claripy
|
|
|
7
7
|
from angr.analyses.reaching_definitions.function_handler import FunctionCallDataUnwrapped, FunctionHandler
|
|
8
8
|
from angr.knowledge_plugins.key_definitions.atoms import Atom
|
|
9
9
|
from angr.knowledge_plugins.key_definitions.live_definitions import DerefSize
|
|
10
|
-
|
|
10
|
+
from angr.knowledge_plugins.key_definitions.definition import Definition
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from angr.analyses.reaching_definitions.rd_state import ReachingDefinitionsState
|
|
@@ -75,7 +75,7 @@ class LibcStdlibHandlers(FunctionHandler):
|
|
|
75
75
|
@FunctionCallDataUnwrapped.decorate
|
|
76
76
|
def handle_impl_calloc(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
77
77
|
nmemb = state.get_concrete_value(data.args_atoms[0]) or 48
|
|
78
|
-
size = state.get_concrete_value(data.args_atoms[
|
|
78
|
+
size = state.get_concrete_value(data.args_atoms[1]) or 1
|
|
79
79
|
heap_ptr = state.heap_address(state.heap_allocator.allocate(nmemb * size))
|
|
80
80
|
data.depends(state.deref(heap_ptr, nmemb * size), value=0)
|
|
81
81
|
data.depends(data.ret_atoms, value=heap_ptr)
|
|
@@ -84,18 +84,51 @@ class LibcStdlibHandlers(FunctionHandler):
|
|
|
84
84
|
def handle_impl_getenv(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
85
85
|
name_atom = state.deref(data.args_atoms[0], DerefSize.NULL_TERMINATE)
|
|
86
86
|
name_value = state.get_concrete_value(name_atom, cast_to=bytes)
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
length = 2
|
|
88
|
+
heap_value = None
|
|
89
|
+
|
|
89
90
|
data.depends(None, name_atom)
|
|
90
91
|
|
|
91
92
|
# store a buffer, registering it as an output of this function
|
|
92
93
|
# we store this two-byte mixed value because we don't want the value to be picked up by get_concrete_value()
|
|
93
94
|
# but also it should be able to be picked up by NULL_TERMINATE reads
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
heap_atom = None
|
|
96
|
+
env_atom = None
|
|
97
|
+
heap_ptr = None
|
|
98
|
+
sources = []
|
|
99
|
+
if name_value is not None:
|
|
100
|
+
name_value = name_value.strip(b"\0").decode()
|
|
101
|
+
for env_atom, env_value in state.others.items():
|
|
102
|
+
if not isinstance(env_atom, EnvironAtom) or env_atom.name != name_value:
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
# There exists an environment variable with this name
|
|
106
|
+
heap_value = env_value
|
|
107
|
+
length = env_atom.size
|
|
108
|
+
heap_ptr = state.heap_allocator.allocate(length)
|
|
109
|
+
heap_atom = state.deref(heap_ptr, length)
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
else:
|
|
113
|
+
heap_value = None
|
|
114
|
+
|
|
115
|
+
if name_value is None or heap_value is None or heap_atom is None or env_atom is None:
|
|
116
|
+
heap_ptr = state.heap_allocator.allocate(length)
|
|
117
|
+
heap_atom = state.deref(heap_ptr, length)
|
|
118
|
+
heap_value = claripy.BVS("weh", 8)
|
|
119
|
+
env_atom = EnvironAtom(length, name_value)
|
|
120
|
+
if heap_atom is not None:
|
|
121
|
+
heap_value = state.annotate_with_def(heap_value, Definition(heap_atom, state.codeloc))
|
|
122
|
+
heap_value = heap_value.concat(claripy.BVV(0, 8))
|
|
123
|
+
data.depends(env_atom, value=heap_value) # Puts the env_atom in the others dict
|
|
124
|
+
|
|
125
|
+
data.depends(heap_atom, env_atom, value=heap_value)
|
|
126
|
+
sources = [heap_atom, env_atom]
|
|
127
|
+
if name_atom is not None:
|
|
128
|
+
sources.append(name_atom)
|
|
129
|
+
|
|
130
|
+
value = state.heap_address(heap_ptr) if heap_ptr is not None else state.top(state.arch.bits)
|
|
131
|
+
data.depends(data.ret_atoms, *sources, value=value)
|
|
99
132
|
|
|
100
133
|
@FunctionCallDataUnwrapped.decorate
|
|
101
134
|
def handle_impl_setenv(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
@@ -107,9 +140,9 @@ class LibcStdlibHandlers(FunctionHandler):
|
|
|
107
140
|
|
|
108
141
|
src_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE)
|
|
109
142
|
src_value = state.get_values(src_atom)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
)
|
|
143
|
+
|
|
144
|
+
env_atom = EnvironAtom(len(src_value) // 8 if src_value is not None else 1, name_value)
|
|
145
|
+
data.depends(env_atom, src_atom, value=src_value)
|
|
113
146
|
|
|
114
147
|
@FunctionCallDataUnwrapped.decorate
|
|
115
148
|
def handle_impl_system(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import archinfo
|
|
3
|
+
import claripy
|
|
3
4
|
from angr.analyses.reaching_definitions.function_handler import FunctionCallDataUnwrapped, FunctionHandler
|
|
4
5
|
from angr.analyses.reaching_definitions.rd_state import ReachingDefinitionsState
|
|
5
6
|
from angr.knowledge_plugins.key_definitions.live_definitions import DerefSize
|
|
7
|
+
from angr.knowledge_plugins.key_definitions.live_definitions import MultiValues
|
|
6
8
|
|
|
7
9
|
# pylint: disable=no-self-use,missing-class-docstring,unused-argument
|
|
8
10
|
|
|
@@ -12,16 +14,26 @@ class LibcStringHandlers(FunctionHandler):
|
|
|
12
14
|
def handle_impl_strcat(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
13
15
|
src0_atom = state.deref(data.args_atoms[0], DerefSize.NULL_TERMINATE)
|
|
14
16
|
src1_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE)
|
|
15
|
-
src0_value = state.get_values(src0_atom)
|
|
16
|
-
src1_value = state.get_values(src1_atom)
|
|
17
|
+
src0_value = state.get_values(src0_atom) if src0_atom is not None else None
|
|
18
|
+
src1_value = state.get_values(src1_atom) if src1_atom is not None else None
|
|
19
|
+
|
|
17
20
|
if src0_value is not None and src1_value is not None:
|
|
18
21
|
src0_value = src0_value.extract(0, len(src0_value) // 8 - 1, archinfo.Endness.BE)
|
|
19
22
|
dest_value = src0_value.concat(src1_value)
|
|
20
23
|
dest_atom = state.deref(data.args_atoms[0], len(dest_value) // 8, endness=archinfo.Endness.BE)
|
|
24
|
+
elif src0_value is not None:
|
|
25
|
+
src0_value = src0_value.extract(0, len(src0_value) // 8 - 1, archinfo.Endness.BE)
|
|
26
|
+
top_val = state.top(state.arch.bits)
|
|
27
|
+
if src1_atom is not None:
|
|
28
|
+
for defn in state.get_definitions(src1_atom):
|
|
29
|
+
top_val = state.annotate_with_def(top_val, defn)
|
|
30
|
+
dest_value = src0_value.concat(MultiValues(top_val))
|
|
31
|
+
dest_atom = state.deref(data.args_atoms[0], len(dest_value) // 8, endness=archinfo.Endness.BE)
|
|
21
32
|
else:
|
|
22
33
|
dest_value = None
|
|
23
34
|
dest_atom = src0_atom
|
|
24
|
-
|
|
35
|
+
if src0_atom is not None and src1_atom is not None:
|
|
36
|
+
data.depends(dest_atom, src0_atom, src1_atom, value=dest_value)
|
|
25
37
|
data.depends(data.ret_atoms, data.args_atoms[0], value=src0_value)
|
|
26
38
|
|
|
27
39
|
handle_impl_strncat = handle_impl_strcat
|
|
@@ -29,39 +41,76 @@ class LibcStringHandlers(FunctionHandler):
|
|
|
29
41
|
@FunctionCallDataUnwrapped.decorate
|
|
30
42
|
def handle_impl_strlen(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
31
43
|
src_atom = state.deref(data.args_atoms[0], DerefSize.NULL_TERMINATE)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
if src_atom is not None:
|
|
45
|
+
src_str = state.get_values(src_atom) if src_atom is not None else None
|
|
46
|
+
if src_str is not None:
|
|
47
|
+
data.depends(data.ret_atoms, src_atom, value=len(src_str) // 8 - 1)
|
|
48
|
+
else:
|
|
49
|
+
data.depends(data.ret_atoms, src_atom)
|
|
35
50
|
else:
|
|
36
|
-
data.depends(data.ret_atoms,
|
|
51
|
+
data.depends(data.ret_atoms, data.args_atoms[0])
|
|
37
52
|
|
|
38
53
|
@FunctionCallDataUnwrapped.decorate
|
|
39
54
|
def handle_impl_strcpy(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
40
55
|
src_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE)
|
|
41
|
-
src_str = state.get_values(src_atom)
|
|
42
|
-
if src_str is
|
|
43
|
-
|
|
56
|
+
src_str = state.get_values(src_atom) if src_atom is not None else None
|
|
57
|
+
if src_str is None:
|
|
58
|
+
src_str = state.top(state.arch.bits)
|
|
59
|
+
if src_atom is not None:
|
|
60
|
+
for defn in state.get_definitions(src_atom):
|
|
61
|
+
src_str = state.annotate_with_def(src_str, defn)
|
|
62
|
+
src_str = MultiValues(src_str)
|
|
63
|
+
|
|
64
|
+
dst_atom = state.deref(data.args_atoms[0], len(src_str) // 8)
|
|
65
|
+
if src_atom is not None:
|
|
44
66
|
data.depends(dst_atom, src_atom, value=src_str)
|
|
45
67
|
data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
|
|
46
68
|
|
|
47
69
|
@FunctionCallDataUnwrapped.decorate
|
|
48
70
|
def handle_impl_strncpy(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
49
71
|
n = state.get_concrete_value(data.args_atoms[2])
|
|
50
|
-
src_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE
|
|
51
|
-
src_str = state.get_values(src_atom)
|
|
52
|
-
if src_str is not None:
|
|
72
|
+
src_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE)
|
|
73
|
+
src_str = state.get_values(src_atom) if src_atom is not None else None
|
|
74
|
+
if src_str is None and src_atom is not None:
|
|
75
|
+
tmp_atom = state.deref(data.args_atoms[1], 1)
|
|
76
|
+
if tmp_atom is not None:
|
|
77
|
+
tmp_str = state.get_values(tmp_atom)
|
|
78
|
+
val_defns = None if tmp_str is None else state.get_definitions(tmp_str)
|
|
79
|
+
if tmp_str is None or not val_defns: # There's no data at all or no valid definitions
|
|
80
|
+
src_str = state.top(state.arch.bits if n is None or n > state.arch.bytes else n * 8)
|
|
81
|
+
defns = state.get_definitions(src_atom) if src_atom is not None else []
|
|
82
|
+
for defn in defns:
|
|
83
|
+
src_str = state.annotate_with_def(src_str, defn)
|
|
84
|
+
src_str = MultiValues(src_str)
|
|
85
|
+
else: # We found some data, but it's not NULL_TERIMINATED or of size n
|
|
86
|
+
src_atoms = set()
|
|
87
|
+
for defn in val_defns:
|
|
88
|
+
a = defn.atom
|
|
89
|
+
a.size = a.size if n is None or a.size < n else n
|
|
90
|
+
src_atoms.add(a)
|
|
91
|
+
src_str = state.get_values(src_atoms)
|
|
92
|
+
|
|
93
|
+
elif n is not None and src_str is not None and n < len(src_str) // 8:
|
|
94
|
+
# We have a src_str, but need to truncate it if n is not None and less than the size of src_str
|
|
95
|
+
src_atom = state.deref(data.args_atoms[1], n)
|
|
96
|
+
if src_atom is not None:
|
|
97
|
+
src_str = state.get_values(src_atom)
|
|
98
|
+
|
|
99
|
+
if src_str is not None and src_atom is not None:
|
|
53
100
|
dst_atom = state.deref(data.args_atoms[0], len(src_str) // 8)
|
|
54
101
|
data.depends(dst_atom, src_atom, value=src_str)
|
|
102
|
+
|
|
55
103
|
data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
|
|
56
104
|
|
|
57
105
|
@FunctionCallDataUnwrapped.decorate
|
|
58
106
|
def handle_impl_strdup(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
59
|
-
src_atom = state.deref(data.args_atoms[
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
107
|
+
src_atom = state.deref(data.args_atoms[0], DerefSize.NULL_TERMINATE)
|
|
108
|
+
if src_atom is not None:
|
|
109
|
+
src_str = state.get_values(src_atom)
|
|
110
|
+
malloc_size = len(src_str) // 8 if src_str is not None else 1
|
|
111
|
+
heap_ptr = state.heap_allocator.allocate(malloc_size)
|
|
112
|
+
dst_atom = state.deref(heap_ptr, malloc_size)
|
|
113
|
+
data.depends(dst_atom, src_atom, value=src_str)
|
|
65
114
|
data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
|
|
66
115
|
|
|
67
116
|
@FunctionCallDataUnwrapped.decorate
|
|
@@ -70,15 +119,22 @@ class LibcStringHandlers(FunctionHandler):
|
|
|
70
119
|
if size is not None:
|
|
71
120
|
src_atom = state.deref(data.args_atoms[1], size)
|
|
72
121
|
dst_atom = state.deref(data.args_atoms[0], size)
|
|
73
|
-
|
|
122
|
+
if src_atom is not None:
|
|
123
|
+
data.depends(dst_atom, src_atom, value=state.get_values(src_atom))
|
|
74
124
|
data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
|
|
75
125
|
|
|
76
126
|
@FunctionCallDataUnwrapped.decorate
|
|
77
127
|
def handle_impl_memset(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
78
128
|
size = state.get_concrete_value(data.args_atoms[2])
|
|
129
|
+
c = state.get_concrete_value(data.args_atoms[1])
|
|
79
130
|
if size is not None:
|
|
80
131
|
dst_atom = state.deref(data.args_atoms[0], size)
|
|
81
|
-
|
|
132
|
+
if c is not None:
|
|
133
|
+
value = MultiValues(claripy.BVV(chr(c) * size, size * 8))
|
|
134
|
+
data.depends(dst_atom, data.args_atoms[1], value=value)
|
|
135
|
+
else:
|
|
136
|
+
data.depends(dst_atom, data.args_atoms[1], value=state.get_values(data.args_atoms[1]))
|
|
137
|
+
|
|
82
138
|
data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
|
|
83
139
|
|
|
84
140
|
@FunctionCallDataUnwrapped.decorate
|
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
import random
|
|
2
3
|
from angr.analyses.reaching_definitions.function_handler import FunctionCallDataUnwrapped, FunctionHandler
|
|
3
4
|
from angr.analyses.reaching_definitions.function_handler_library.stdio import StdinAtom, StdoutAtom
|
|
4
5
|
from angr.analyses.reaching_definitions.rd_state import ReachingDefinitionsState
|
|
6
|
+
from angr.knowledge_plugins.key_definitions.atoms import Atom
|
|
5
7
|
|
|
6
8
|
# pylint: disable=no-self-use,missing-class-docstring,unused-argument
|
|
7
9
|
|
|
8
10
|
|
|
11
|
+
class FDAtom(Atom):
|
|
12
|
+
def __init__(self, fd: int | None, source: str, size: int = 1):
|
|
13
|
+
self.source = source
|
|
14
|
+
self.fd = fd
|
|
15
|
+
self.nonce = random.randint(0, 999999999999)
|
|
16
|
+
super().__init__(size)
|
|
17
|
+
|
|
18
|
+
def _identity(self):
|
|
19
|
+
if self.fd is not None:
|
|
20
|
+
return (self.fd,)
|
|
21
|
+
return (self.nonce,)
|
|
22
|
+
|
|
23
|
+
|
|
9
24
|
class LibcUnistdHandlers(FunctionHandler):
|
|
10
25
|
@FunctionCallDataUnwrapped.decorate
|
|
11
26
|
def handle_impl_read(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
12
27
|
size = state.get_concrete_value(data.args_atoms[2]) or 1
|
|
13
28
|
dst_atom = state.deref(data.args_atoms[1], size)
|
|
14
|
-
|
|
29
|
+
real_fd = state.get_concrete_value(data.args_atoms[0])
|
|
30
|
+
|
|
31
|
+
fd_atom = StdinAtom(data.function.name, size) if real_fd == 0 else FDAtom(real_fd, data.function.name, size)
|
|
32
|
+
buf_data = state.top(size * 8) if size is not None else state.top(state.arch.bits)
|
|
33
|
+
|
|
34
|
+
data.depends(dst_atom, fd_atom, value=buf_data)
|
|
15
35
|
|
|
16
36
|
handle_impl_recv = handle_impl_recvfrom = handle_impl_read
|
|
17
37
|
|
|
@@ -215,14 +215,14 @@ class ReachingDefinitionsState:
|
|
|
215
215
|
def tmp_uses(self):
|
|
216
216
|
return self.live_definitions.tmp_uses
|
|
217
217
|
|
|
218
|
-
@property
|
|
219
|
-
def register_uses(self):
|
|
220
|
-
return self.live_definitions.register_uses
|
|
221
|
-
|
|
222
218
|
@property
|
|
223
219
|
def registers(self) -> MultiValuedMemory:
|
|
224
220
|
return self.live_definitions.registers
|
|
225
221
|
|
|
222
|
+
@property
|
|
223
|
+
def register_uses(self):
|
|
224
|
+
return self.live_definitions.register_uses
|
|
225
|
+
|
|
226
226
|
@property
|
|
227
227
|
def stack(self) -> MultiValuedMemory:
|
|
228
228
|
return self.live_definitions.stack
|
|
@@ -239,13 +239,17 @@ class ReachingDefinitionsState:
|
|
|
239
239
|
def heap_uses(self):
|
|
240
240
|
return self.live_definitions.heap_uses
|
|
241
241
|
|
|
242
|
+
@property
|
|
243
|
+
def memory(self) -> MultiValuedMemory:
|
|
244
|
+
return self.live_definitions.memory
|
|
245
|
+
|
|
242
246
|
@property
|
|
243
247
|
def memory_uses(self):
|
|
244
248
|
return self.live_definitions.memory_uses
|
|
245
249
|
|
|
246
250
|
@property
|
|
247
|
-
def
|
|
248
|
-
return self.live_definitions.
|
|
251
|
+
def others(self) -> dict[Atom, MultiValues]:
|
|
252
|
+
return self.live_definitions.others
|
|
249
253
|
|
|
250
254
|
@property
|
|
251
255
|
def uses_by_codeloc(self):
|
|
@@ -493,7 +497,7 @@ class ReachingDefinitionsState:
|
|
|
493
497
|
self.live_definitions.add_memory_use_by_def(definition, self.codeloc, expr=expr)
|
|
494
498
|
|
|
495
499
|
def get_definitions(
|
|
496
|
-
self, atom: Atom | Definition[Atom] | Iterable[Atom] | Iterable[Definition[Atom]]
|
|
500
|
+
self, atom: Atom | Definition[Atom] | Iterable[Atom] | Iterable[Definition[Atom]] | MultiValues
|
|
497
501
|
) -> set[Definition[Atom]]:
|
|
498
502
|
return self.live_definitions.get_definitions(atom)
|
|
499
503
|
|
angr/analyses/s_liveness.py
CHANGED
|
@@ -2,9 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import networkx
|
|
4
4
|
from ailment.expression import VirtualVariable
|
|
5
|
-
from ailment.statement import Assignment, Call
|
|
5
|
+
from ailment.statement import Assignment, Call, ConditionalJump
|
|
6
6
|
|
|
7
7
|
from angr.analyses import Analysis, register_analysis
|
|
8
|
+
from angr.utils.ail import is_head_controlled_loop_block, is_phi_assignment
|
|
8
9
|
from angr.utils.ssa import VVarUsesCollector, phi_assignment_get_src
|
|
9
10
|
|
|
10
11
|
|
|
@@ -69,8 +70,14 @@ class SLivenessAnalysis(Analysis):
|
|
|
69
70
|
block_key = block.addr, block.idx
|
|
70
71
|
changed = False
|
|
71
72
|
|
|
73
|
+
head_controlled_loop = is_head_controlled_loop_block(block)
|
|
74
|
+
|
|
72
75
|
live = set()
|
|
73
76
|
for succ in graph.successors(block):
|
|
77
|
+
if head_controlled_loop and (block.addr, block.idx) == (succ.addr, succ.idx):
|
|
78
|
+
# this is a head-controlled loop block; we ignore the self-loop edge because all variables defined
|
|
79
|
+
# in the block after the conditional jump will be dead after leaving the current block
|
|
80
|
+
continue
|
|
74
81
|
edge = (block.addr, block.idx), (succ.addr, succ.idx)
|
|
75
82
|
if edge in live_on_edges:
|
|
76
83
|
live |= live_on_edges[edge]
|
|
@@ -81,8 +88,18 @@ class SLivenessAnalysis(Analysis):
|
|
|
81
88
|
changed = True
|
|
82
89
|
live_outs[block_key] = live.copy()
|
|
83
90
|
|
|
91
|
+
if head_controlled_loop:
|
|
92
|
+
# this is a head-controlled loop block; we start scanning from the first condition jump backwards
|
|
93
|
+
condjump_idx = next(
|
|
94
|
+
iter(i for i, stmt in enumerate(block.statements) if isinstance(stmt, ConditionalJump)), None
|
|
95
|
+
)
|
|
96
|
+
assert condjump_idx is not None
|
|
97
|
+
stmts = block.statements[: condjump_idx + 1]
|
|
98
|
+
else:
|
|
99
|
+
stmts = block.statements
|
|
100
|
+
|
|
84
101
|
live_in_by_pred = {}
|
|
85
|
-
for stmt in reversed(
|
|
102
|
+
for stmt in reversed(stmts):
|
|
86
103
|
# handle assignments: a defined vvar is not live before the assignment
|
|
87
104
|
if isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable):
|
|
88
105
|
live.discard(stmt.dst.varid)
|
|
@@ -92,6 +109,10 @@ class SLivenessAnalysis(Analysis):
|
|
|
92
109
|
phi_expr = phi_assignment_get_src(stmt)
|
|
93
110
|
if phi_expr is not None:
|
|
94
111
|
for src, vvar in phi_expr.src_and_vvars:
|
|
112
|
+
if head_controlled_loop and src == (block.addr, block.idx):
|
|
113
|
+
# this is a head-controlled loop block; we ignore the self-loop edge
|
|
114
|
+
continue
|
|
115
|
+
|
|
95
116
|
if src not in live_in_by_pred:
|
|
96
117
|
live_in_by_pred[src] = live.copy()
|
|
97
118
|
if vvar is not None:
|
|
@@ -99,9 +120,15 @@ class SLivenessAnalysis(Analysis):
|
|
|
99
120
|
live_in_by_pred[src].discard(stmt.dst.varid)
|
|
100
121
|
|
|
101
122
|
# handle the statement: add used vvars to the live set
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
123
|
+
if head_controlled_loop and is_phi_assignment(stmt):
|
|
124
|
+
for src, vvar in stmt.src.src_and_vvars:
|
|
125
|
+
# this is a head-controlled loop block; we ignore the self-loop edge
|
|
126
|
+
if src != (block.addr, block.idx) and vvar is not None:
|
|
127
|
+
live |= {vvar.varid}
|
|
128
|
+
else:
|
|
129
|
+
vvar_use_collector = VVarUsesCollector()
|
|
130
|
+
vvar_use_collector.walk_statement(stmt)
|
|
131
|
+
live |= vvar_use_collector.vvars
|
|
105
132
|
|
|
106
133
|
if live_ins[block_key] != live:
|
|
107
134
|
live_ins[block_key] = live
|
|
@@ -135,7 +162,18 @@ class SLivenessAnalysis(Analysis):
|
|
|
135
162
|
|
|
136
163
|
for block in self.func_graph.nodes():
|
|
137
164
|
live = self.model.live_outs[(block.addr, block.idx)].copy()
|
|
138
|
-
|
|
165
|
+
|
|
166
|
+
if is_head_controlled_loop_block(block):
|
|
167
|
+
# this is a head-controlled loop block; we start scanning from the first condition jump backwards
|
|
168
|
+
condjump_idx = next(
|
|
169
|
+
iter(i for i, stmt in enumerate(block.statements) if isinstance(stmt, ConditionalJump)), None
|
|
170
|
+
)
|
|
171
|
+
assert condjump_idx is not None
|
|
172
|
+
stmts = block.statements[: condjump_idx + 1]
|
|
173
|
+
else:
|
|
174
|
+
stmts = block.statements
|
|
175
|
+
|
|
176
|
+
for stmt in reversed(stmts):
|
|
139
177
|
if isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable):
|
|
140
178
|
def_vvar = stmt.dst.varid
|
|
141
179
|
elif isinstance(stmt, Call) and isinstance(stmt.ret_expr, VirtualVariable):
|