angr 9.2.140__py3-none-manylinux2014_x86_64.whl → 9.2.142__py3-none-manylinux2014_x86_64.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 +105 -35
- angr/analyses/calling_convention/fact_collector.py +44 -18
- angr/analyses/calling_convention/utils.py +3 -1
- angr/analyses/cfg/cfg_base.py +38 -4
- angr/analyses/cfg/cfg_fast.py +23 -7
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +13 -8
- angr/analyses/class_identifier.py +8 -7
- angr/analyses/complete_calling_conventions.py +1 -1
- angr/analyses/decompiler/ail_simplifier.py +105 -62
- angr/analyses/decompiler/callsite_maker.py +24 -11
- angr/analyses/decompiler/clinic.py +83 -5
- angr/analyses/decompiler/condition_processor.py +7 -7
- angr/analyses/decompiler/decompilation_cache.py +2 -1
- angr/analyses/decompiler/decompiler.py +11 -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 +63 -34
- angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +85 -16
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +78 -1
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +29 -7
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +51 -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/eager_eval.py +44 -7
- angr/analyses/decompiler/region_identifier.py +76 -51
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +32 -18
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -1
- angr/analyses/decompiler/ssailification/rewriting.py +70 -32
- angr/analyses/decompiler/ssailification/rewriting_engine.py +118 -24
- angr/analyses/decompiler/ssailification/ssailification.py +22 -14
- angr/analyses/decompiler/stack_item.py +36 -0
- angr/analyses/decompiler/structured_codegen/c.py +86 -145
- angr/analyses/decompiler/structuring/dream.py +1 -1
- angr/analyses/decompiler/structuring/phoenix.py +9 -4
- angr/analyses/decompiler/structuring/structurer_base.py +2 -1
- angr/analyses/decompiler/utils.py +46 -20
- 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_reaching_definitions/s_rda_model.py +4 -2
- angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
- angr/analyses/typehoon/simple_solver.py +35 -8
- angr/analyses/typehoon/typehoon.py +3 -1
- angr/analyses/variable_recovery/engine_ail.py +1 -1
- angr/analyses/variable_recovery/engine_vex.py +20 -4
- angr/calling_conventions.py +17 -12
- angr/factory.py +8 -3
- angr/knowledge_plugins/functions/function.py +5 -10
- angr/knowledge_plugins/variables/variable_manager.py +34 -5
- angr/procedures/definitions/__init__.py +3 -10
- 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 +17 -11
- angr/simos/windows.py +42 -1
- angr/utils/ail.py +41 -1
- angr/utils/cpp.py +17 -0
- angr/utils/doms.py +142 -0
- angr/utils/library.py +1 -1
- angr/utils/types.py +59 -0
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/METADATA +7 -7
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/RECORD +75 -70
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/LICENSE +0 -0
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/WHEEL +0 -0
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/entry_points.txt +0 -0
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/top_level.txt +0 -0
|
@@ -314,7 +314,7 @@ class DreamStructurer(StructurerBase):
|
|
|
314
314
|
loop_head, loop_region_graph, successors=None, graph_with_successors=None, cyclic=False, full_graph=None
|
|
315
315
|
)
|
|
316
316
|
structurer = self.project.analyses[DreamStructurer].prep()(
|
|
317
|
-
region, condition_processor=self.cond_proc, func=self.function
|
|
317
|
+
region, condition_processor=self.cond_proc, func=self.function, jump_tables=self.jump_tables
|
|
318
318
|
)
|
|
319
319
|
seq = structurer.result
|
|
320
320
|
|
|
@@ -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"
|
|
@@ -1723,7 +1723,12 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1723
1723
|
for case_node in to_remove:
|
|
1724
1724
|
if case_node is not node_default and case_node is not node_a and case_node is not head:
|
|
1725
1725
|
for succ in graph.successors(case_node):
|
|
1726
|
-
if
|
|
1726
|
+
if (
|
|
1727
|
+
succ is not case_node
|
|
1728
|
+
and succ is not head
|
|
1729
|
+
and succ is not self._region.head
|
|
1730
|
+
and graph.in_degree[succ] == 1
|
|
1731
|
+
):
|
|
1727
1732
|
# succ will be dangling - not ready to be structured yet - do it later
|
|
1728
1733
|
return False
|
|
1729
1734
|
|
|
@@ -371,8 +371,8 @@ class StructurerBase(Analysis):
|
|
|
371
371
|
jump_stmt = this_node.nodes[-1].statements[-1]
|
|
372
372
|
this_node = this_node.nodes[-1]
|
|
373
373
|
|
|
374
|
-
assert isinstance(this_node, ailment.Block)
|
|
375
374
|
if isinstance(jump_stmt, ailment.Stmt.Jump):
|
|
375
|
+
assert isinstance(this_node, ailment.Block)
|
|
376
376
|
next_node = node.nodes[i + 1]
|
|
377
377
|
if (
|
|
378
378
|
isinstance(jump_stmt.target, ailment.Expr.Const)
|
|
@@ -381,6 +381,7 @@ class StructurerBase(Analysis):
|
|
|
381
381
|
# this goto is useless
|
|
382
382
|
this_node.statements = this_node.statements[:-1]
|
|
383
383
|
elif isinstance(jump_stmt, ailment.Stmt.ConditionalJump):
|
|
384
|
+
assert isinstance(this_node, ailment.Block)
|
|
384
385
|
next_node = node.nodes[i + 1]
|
|
385
386
|
if (
|
|
386
387
|
isinstance(jump_stmt.true_target, ailment.Expr.Const)
|
|
@@ -625,6 +625,48 @@ def update_labels(graph: networkx.DiGraph):
|
|
|
625
625
|
return add_labels(remove_labels(graph))
|
|
626
626
|
|
|
627
627
|
|
|
628
|
+
def _flatten_structured_node(packed_node: SequenceNode | MultiNode) -> list[ailment.Block]:
|
|
629
|
+
if not packed_node or not packed_node.nodes:
|
|
630
|
+
return []
|
|
631
|
+
|
|
632
|
+
blocks = []
|
|
633
|
+
if packed_node.nodes is not None:
|
|
634
|
+
for _node in packed_node.nodes:
|
|
635
|
+
if isinstance(_node, (SequenceNode, MultiNode)):
|
|
636
|
+
blocks += _flatten_structured_node(_node)
|
|
637
|
+
else:
|
|
638
|
+
blocks.append(_node)
|
|
639
|
+
|
|
640
|
+
return blocks
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def _find_node_in_graph(node: ailment.Block, graph: networkx.DiGraph) -> ailment.Block | None:
|
|
644
|
+
for bb in graph:
|
|
645
|
+
if bb.addr == node.addr and bb.idx == node.idx:
|
|
646
|
+
return bb
|
|
647
|
+
return None
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def structured_node_has_multi_predecessors(node: SequenceNode | MultiNode, graph: networkx.DiGraph) -> bool:
|
|
651
|
+
if graph is None:
|
|
652
|
+
return False
|
|
653
|
+
|
|
654
|
+
first_block = None
|
|
655
|
+
if isinstance(node, (SequenceNode, MultiNode)) and node.nodes:
|
|
656
|
+
flat_blocks = _flatten_structured_node(node)
|
|
657
|
+
node = flat_blocks[0]
|
|
658
|
+
|
|
659
|
+
if isinstance(node, ailment.Block):
|
|
660
|
+
first_block = node
|
|
661
|
+
|
|
662
|
+
if first_block is not None:
|
|
663
|
+
graph_node = _find_node_in_graph(first_block, graph)
|
|
664
|
+
if graph_node is not None:
|
|
665
|
+
return len(list(graph.predecessors(graph_node))) > 1
|
|
666
|
+
|
|
667
|
+
return False
|
|
668
|
+
|
|
669
|
+
|
|
628
670
|
def structured_node_is_simple_return(
|
|
629
671
|
node: SequenceNode | MultiNode, graph: networkx.DiGraph, use_packed_successors=False
|
|
630
672
|
) -> bool:
|
|
@@ -639,21 +681,6 @@ def structured_node_is_simple_return(
|
|
|
639
681
|
|
|
640
682
|
Returns true on any block ending in linear statements and a return.
|
|
641
683
|
"""
|
|
642
|
-
|
|
643
|
-
def _flatten_structured_node(packed_node: SequenceNode | MultiNode) -> list[ailment.Block]:
|
|
644
|
-
if not packed_node or not packed_node.nodes:
|
|
645
|
-
return []
|
|
646
|
-
|
|
647
|
-
blocks = []
|
|
648
|
-
if packed_node.nodes is not None:
|
|
649
|
-
for _node in packed_node.nodes:
|
|
650
|
-
if isinstance(_node, (SequenceNode, MultiNode)):
|
|
651
|
-
blocks += _flatten_structured_node(_node)
|
|
652
|
-
else:
|
|
653
|
-
blocks.append(_node)
|
|
654
|
-
|
|
655
|
-
return blocks
|
|
656
|
-
|
|
657
684
|
# sanity check: we need a graph to understand returning blocks
|
|
658
685
|
if graph is None:
|
|
659
686
|
return False
|
|
@@ -676,11 +703,10 @@ def structured_node_is_simple_return(
|
|
|
676
703
|
if valid_last_stmt:
|
|
677
704
|
# note that the block may not be the same block in the AIL graph post dephication. we must find the block again
|
|
678
705
|
# in the graph.
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
return not succs or succs == [bb]
|
|
706
|
+
last_graph_block = _find_node_in_graph(last_block, graph)
|
|
707
|
+
if last_graph_block is not None:
|
|
708
|
+
succs = list(graph.successors(last_graph_block))
|
|
709
|
+
return not succs or succs == [last_graph_block]
|
|
684
710
|
return False
|
|
685
711
|
|
|
686
712
|
|
|
@@ -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
|
|