angr 9.2.146__py3-none-macosx_11_0_arm64.whl → 9.2.148__py3-none-macosx_11_0_arm64.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/analysis.py +3 -11
- angr/analyses/bindiff.py +343 -68
- angr/analyses/calling_convention/fact_collector.py +5 -4
- angr/analyses/calling_convention/utils.py +1 -0
- angr/analyses/cfg/cfg_arch_options.py +10 -0
- angr/analyses/cfg/cfg_base.py +42 -74
- angr/analyses/cfg/cfg_emulated.py +12 -12
- angr/analyses/cfg/cfg_fast.py +39 -20
- angr/analyses/cfg/cfg_fast_soot.py +3 -3
- angr/analyses/decompiler/callsite_maker.py +28 -18
- angr/analyses/decompiler/clinic.py +4 -4
- angr/analyses/decompiler/condition_processor.py +0 -21
- angr/analyses/decompiler/counters/call_counter.py +3 -0
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +14 -0
- angr/analyses/decompiler/structured_codegen/c.py +5 -5
- angr/analyses/decompiler/structuring/phoenix.py +11 -3
- angr/analyses/deobfuscator/api_obf_finder.py +5 -1
- angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +1 -1
- angr/analyses/flirt/__init__.py +47 -0
- angr/analyses/flirt/consts.py +160 -0
- angr/analyses/{flirt.py → flirt/flirt.py} +99 -38
- angr/analyses/flirt/flirt_function.py +20 -0
- angr/analyses/flirt/flirt_matcher.py +351 -0
- angr/analyses/flirt/flirt_module.py +32 -0
- angr/analyses/flirt/flirt_node.py +23 -0
- angr/analyses/flirt/flirt_sig.py +356 -0
- angr/analyses/flirt/flirt_utils.py +31 -0
- angr/analyses/forward_analysis/visitors/graph.py +0 -8
- angr/analyses/identifier/runner.py +1 -1
- angr/analyses/reaching_definitions/function_handler.py +4 -4
- angr/analyses/reassembler.py +1 -1
- angr/analyses/stack_pointer_tracker.py +35 -1
- angr/analyses/static_hooker.py +11 -9
- angr/analyses/variable_recovery/engine_ail.py +8 -8
- angr/analyses/variable_recovery/engine_base.py +2 -0
- angr/block.py +6 -6
- angr/calling_conventions.py +74 -23
- angr/engines/vex/heavy/concretizers.py +10 -0
- angr/exploration_techniques/director.py +1 -1
- angr/flirt/__init__.py +15 -44
- angr/knowledge_plugins/functions/function.py +42 -39
- angr/knowledge_plugins/functions/function_manager.py +9 -0
- angr/knowledge_plugins/functions/function_parser.py +9 -1
- angr/knowledge_plugins/functions/soot_function.py +1 -1
- angr/knowledge_plugins/key_definitions/key_definition_manager.py +1 -1
- angr/lib/angr_native.dylib +0 -0
- angr/procedures/definitions/__init__.py +14 -11
- angr/procedures/stubs/format_parser.py +1 -1
- angr/project.py +23 -29
- angr/protos/cfg_pb2.py +14 -25
- angr/protos/function_pb2.py +11 -22
- angr/protos/primitives_pb2.py +36 -47
- angr/protos/variables_pb2.py +28 -39
- angr/protos/xrefs_pb2.py +8 -19
- angr/sim_type.py +0 -16
- angr/simos/cgc.py +1 -1
- angr/simos/linux.py +5 -5
- angr/simos/windows.py +5 -5
- angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +1 -1
- {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/METADATA +8 -8
- {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/RECORD +67 -59
- {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/WHEEL +1 -1
- {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/entry_points.txt +0 -0
- {angr-9.2.146.dist-info → angr-9.2.148.dist-info/licenses}/LICENSE +0 -0
- {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/top_level.txt +0 -0
angr/analyses/cfg/cfg_base.py
CHANGED
|
@@ -17,7 +17,6 @@ from archinfo.arch_arm import is_arm_arch, get_real_address_if_arm
|
|
|
17
17
|
from angr.knowledge_plugins.functions.function_manager import FunctionManager
|
|
18
18
|
from angr.knowledge_plugins.functions.function import Function
|
|
19
19
|
from angr.knowledge_plugins.cfg import IndirectJump, CFGNode, CFGENode, CFGModel # pylint:disable=unused-import
|
|
20
|
-
from angr.misc.ux import deprecated
|
|
21
20
|
from angr.procedures.stubs.UnresolvableJumpTarget import UnresolvableJumpTarget
|
|
22
21
|
from angr.utils.constants import DEFAULT_STATEMENT
|
|
23
22
|
from angr.procedures.procedure_dict import SIM_PROCEDURES
|
|
@@ -354,64 +353,9 @@ class CFGBase(Analysis):
|
|
|
354
353
|
|
|
355
354
|
raise NotImplementedError("I'm too lazy to implement it right now")
|
|
356
355
|
|
|
357
|
-
@deprecated(replacement="self.model.get_predecessors()")
|
|
358
|
-
def get_predecessors(self, cfgnode, excluding_fakeret=True, jumpkind=None):
|
|
359
|
-
return self._model.get_predecessors(cfgnode, excluding_fakeret=excluding_fakeret, jumpkind=jumpkind)
|
|
360
|
-
|
|
361
|
-
@deprecated(replacement="self.model.get_successors()")
|
|
362
|
-
def get_successors(self, node, excluding_fakeret=True, jumpkind=None):
|
|
363
|
-
return self._model.get_successors(node, excluding_fakeret=excluding_fakeret, jumpkind=jumpkind)
|
|
364
|
-
|
|
365
|
-
@deprecated(replacement="self.model.get_successors_and_jumpkind")
|
|
366
|
-
def get_successors_and_jumpkind(self, node, excluding_fakeret=True):
|
|
367
|
-
return self._model.get_successors_and_jumpkind(node, excluding_fakeret=excluding_fakeret)
|
|
368
|
-
|
|
369
|
-
@deprecated(replacement="self.model.get_all_predecessors()")
|
|
370
|
-
def get_all_predecessors(self, cfgnode, depth_limit=None):
|
|
371
|
-
return self._model.get_all_predecessors(cfgnode, depth_limit)
|
|
372
|
-
|
|
373
|
-
@deprecated(replacement="self.model.get_all_successors()")
|
|
374
|
-
def get_all_successors(self, cfgnode, depth_limit=None):
|
|
375
|
-
return self._model.get_all_successors(cfgnode, depth_limit)
|
|
376
|
-
|
|
377
|
-
@deprecated(replacement="self.model.get_node()")
|
|
378
|
-
def get_node(self, block_id):
|
|
379
|
-
return self._model.get_node(block_id)
|
|
380
|
-
|
|
381
|
-
@deprecated(replacement="self.model.get_any_node()")
|
|
382
|
-
def get_any_node(self, addr, is_syscall=None, anyaddr=False, force_fastpath=False):
|
|
383
|
-
return self._model.get_any_node(addr, is_syscall=is_syscall, anyaddr=anyaddr, force_fastpath=force_fastpath)
|
|
384
|
-
|
|
385
|
-
@deprecated(replacement="self.model.get_all_nodes()")
|
|
386
|
-
def get_all_nodes(self, addr, is_syscall=None, anyaddr=False):
|
|
387
|
-
return self._model.get_all_nodes(addr, is_syscall=is_syscall, anyaddr=anyaddr)
|
|
388
|
-
|
|
389
|
-
@deprecated(replacement="self.model.nodes()")
|
|
390
|
-
def nodes(self):
|
|
391
|
-
return self._model.nodes()
|
|
392
|
-
|
|
393
|
-
@deprecated(replacement="nodes")
|
|
394
|
-
def nodes_iter(self):
|
|
395
|
-
"""
|
|
396
|
-
(Decrepated) An iterator of all nodes in the graph. Will be removed in the future.
|
|
397
|
-
|
|
398
|
-
:return: The iterator.
|
|
399
|
-
:rtype: iterator
|
|
400
|
-
"""
|
|
401
|
-
|
|
402
|
-
return self.nodes()
|
|
403
|
-
|
|
404
356
|
def get_loop_back_edges(self):
|
|
405
357
|
return self._loop_back_edges
|
|
406
358
|
|
|
407
|
-
@deprecated(replacement="self.model.get_branching_nodes()")
|
|
408
|
-
def get_branching_nodes(self):
|
|
409
|
-
return self._model.get_branching_nodes()
|
|
410
|
-
|
|
411
|
-
@deprecated(replacement="self.model.get_exit_stmt_idx")
|
|
412
|
-
def get_exit_stmt_idx(self, src_block, dst_block):
|
|
413
|
-
return self._model.get_exit_stmt_idx(src_block, dst_block)
|
|
414
|
-
|
|
415
359
|
@property
|
|
416
360
|
def graph(self) -> networkx.DiGraph[CFGNode]:
|
|
417
361
|
raise NotImplementedError
|
|
@@ -1550,10 +1494,12 @@ class CFGBase(Analysis):
|
|
|
1550
1494
|
block = next((b for b in function.blocks), None)
|
|
1551
1495
|
if block is None:
|
|
1552
1496
|
continue
|
|
1553
|
-
if
|
|
1497
|
+
if self._is_noop_block(self.project.arch, block) or all(
|
|
1498
|
+
self._is_noop_insn(insn) for insn in block.capstone.insns
|
|
1499
|
+
):
|
|
1554
1500
|
# all nops. mark this function as a function alignment
|
|
1555
1501
|
l.debug("Function chunk %#x is probably used as a function alignment (all nops).", func_addr)
|
|
1556
|
-
self.kb.functions[func_addr].
|
|
1502
|
+
self.kb.functions[func_addr].is_alignment = True
|
|
1557
1503
|
continue
|
|
1558
1504
|
node = function.get_node(block.addr)
|
|
1559
1505
|
assert node is not None
|
|
@@ -1561,7 +1507,7 @@ class CFGBase(Analysis):
|
|
|
1561
1507
|
if len(successors) == 1 and successors[0].addr == node.addr:
|
|
1562
1508
|
# self loop. mark this function as a function alignment
|
|
1563
1509
|
l.debug("Function chunk %#x is probably used as a function alignment (self-loop).", func_addr)
|
|
1564
|
-
self.kb.functions[func_addr].
|
|
1510
|
+
self.kb.functions[func_addr].is_alignment = True
|
|
1565
1511
|
continue
|
|
1566
1512
|
|
|
1567
1513
|
def make_functions(self):
|
|
@@ -2108,7 +2054,7 @@ class CFGBase(Analysis):
|
|
|
2108
2054
|
continue
|
|
2109
2055
|
if func_addr in jumptable_entries:
|
|
2110
2056
|
# is there any call edge pointing to it?
|
|
2111
|
-
func_node = self.get_any_node(func_addr, force_fastpath=True)
|
|
2057
|
+
func_node = self.model.get_any_node(func_addr, force_fastpath=True)
|
|
2112
2058
|
if func_node is not None:
|
|
2113
2059
|
in_edges = self.graph.in_edges(func_node, data=True)
|
|
2114
2060
|
has_transition_pred = None
|
|
@@ -2205,10 +2151,6 @@ class CFGBase(Analysis):
|
|
|
2205
2151
|
out_edges = [e for e in g.out_edges(node_) if g.get_edge_data(*e)["jumpkind"] != "Ijk_FakeRet"]
|
|
2206
2152
|
return len(out_edges) > 1
|
|
2207
2153
|
|
|
2208
|
-
if len(src_function.block_addrs_set) > 10:
|
|
2209
|
-
# ignore functions unless they are extremely small
|
|
2210
|
-
return False
|
|
2211
|
-
|
|
2212
2154
|
if len(all_edges) == 1 and dst_addr != src_addr:
|
|
2213
2155
|
the_edge = next(iter(all_edges))
|
|
2214
2156
|
_, dst, data = the_edge
|
|
@@ -2251,15 +2193,41 @@ class CFGBase(Analysis):
|
|
|
2251
2193
|
candidate = True
|
|
2252
2194
|
|
|
2253
2195
|
if candidate:
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2196
|
+
# we have two strategies; for small functions, we run SPTracker on the entire function and see if the
|
|
2197
|
+
# stack pointer changes or not; for large functions, we simply detect how far away we jump as well as
|
|
2198
|
+
# if there are any other functions identified between the source and the destination.
|
|
2199
|
+
if len(src_function.block_addrs_set) <= 10:
|
|
2200
|
+
regs = {self.project.arch.sp_offset}
|
|
2201
|
+
if hasattr(self.project.arch, "bp_offset") and self.project.arch.bp_offset is not None:
|
|
2202
|
+
regs.add(self.project.arch.bp_offset)
|
|
2203
|
+
sptracker = self.project.analyses[StackPointerTracker].prep()(
|
|
2204
|
+
src_function, regs, track_memory=self._sp_tracking_track_memory
|
|
2205
|
+
)
|
|
2206
|
+
sp_delta = sptracker.offset_after_block(src_addr, self.project.arch.sp_offset)
|
|
2207
|
+
if sp_delta == 0:
|
|
2208
|
+
return True
|
|
2209
|
+
else:
|
|
2210
|
+
# large function; to speed things up, we don't track sp
|
|
2211
|
+
minaddr, maxaddr = None, None
|
|
2212
|
+
if dst_addr - src_addr >= 0x100:
|
|
2213
|
+
minaddr = src_addr
|
|
2214
|
+
maxaddr = dst_addr
|
|
2215
|
+
elif dst_addr < src_addr:
|
|
2216
|
+
# jumping back; is it jumping beyond the function header?
|
|
2217
|
+
src_func = blockaddr_to_function[src_addr]
|
|
2218
|
+
if dst_addr < src_func.addr and src_func.addr - dst_addr >= 0x100:
|
|
2219
|
+
minaddr = dst_addr
|
|
2220
|
+
maxaddr = src_func.addr
|
|
2221
|
+
|
|
2222
|
+
if minaddr is not None and maxaddr is not None:
|
|
2223
|
+
# are there other function in between?
|
|
2224
|
+
funcaddrs_in_between = list(
|
|
2225
|
+
known_functions._function_map.irange(minimum=minaddr + 1, maximum=maxaddr - 1)
|
|
2226
|
+
)
|
|
2227
|
+
funcs_in_between = [known_functions.get_by_addr(a) for a in funcaddrs_in_between]
|
|
2228
|
+
funcs_in_between = [func for func in funcs_in_between if not func.is_alignment]
|
|
2229
|
+
if len(funcs_in_between) >= 3:
|
|
2230
|
+
return True
|
|
2263
2231
|
|
|
2264
2232
|
return False
|
|
2265
2233
|
|
|
@@ -2639,7 +2607,7 @@ class CFGBase(Analysis):
|
|
|
2639
2607
|
:return: True if the instruction does no-op, False otherwise.
|
|
2640
2608
|
"""
|
|
2641
2609
|
|
|
2642
|
-
insn_name = insn.
|
|
2610
|
+
insn_name = insn.mnemonic
|
|
2643
2611
|
|
|
2644
2612
|
if insn_name == "nop":
|
|
2645
2613
|
# nops
|
|
@@ -472,7 +472,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
|
|
|
472
472
|
src_blocknode: BlockNode = back_edge[0]
|
|
473
473
|
dst_blocknode: BlockNode = back_edge[1]
|
|
474
474
|
|
|
475
|
-
for src in self.get_all_nodes(src_blocknode.addr):
|
|
475
|
+
for src in self.model.get_all_nodes(src_blocknode.addr):
|
|
476
476
|
for dst in graph.successors(src):
|
|
477
477
|
if dst.addr != dst_blocknode.addr:
|
|
478
478
|
continue
|
|
@@ -524,7 +524,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
|
|
|
524
524
|
start = self._starts[0]
|
|
525
525
|
if isinstance(start, tuple):
|
|
526
526
|
start, _ = start # pylint: disable=unpacking-non-sequence
|
|
527
|
-
start_node = self.get_any_node(start)
|
|
527
|
+
start_node = self.model.get_any_node(start)
|
|
528
528
|
if start_node is None:
|
|
529
529
|
raise AngrCFGError("Cannot find start node when trying to unroll loops. The CFG might be empty.")
|
|
530
530
|
|
|
@@ -720,7 +720,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
|
|
|
720
720
|
# FIXME: syscalls are not supported
|
|
721
721
|
# FIXME: start should also take a CFGNode instance
|
|
722
722
|
|
|
723
|
-
start_node = self.get_any_node(start)
|
|
723
|
+
start_node = self.model.get_any_node(start)
|
|
724
724
|
assert start_node is not None
|
|
725
725
|
|
|
726
726
|
node_wrapper = (start_node, 0)
|
|
@@ -2286,9 +2286,9 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
|
|
|
2286
2286
|
# Remove that edge!
|
|
2287
2287
|
graph.remove_edge(call_func_addr, return_to_addr)
|
|
2288
2288
|
# Remove the edge in CFG
|
|
2289
|
-
nodes = self.get_all_nodes(callsite_block_addr)
|
|
2289
|
+
nodes = self.model.get_all_nodes(callsite_block_addr)
|
|
2290
2290
|
for n in nodes:
|
|
2291
|
-
successors = self.get_successors_and_jumpkind(n, excluding_fakeret=False)
|
|
2291
|
+
successors = self.model.get_successors_and_jumpkind(n, excluding_fakeret=False)
|
|
2292
2292
|
for successor, jumpkind in successors:
|
|
2293
2293
|
if jumpkind == "Ijk_FakeRet" and successor.addr == return_to_addr:
|
|
2294
2294
|
self.remove_edge(n, successor)
|
|
@@ -2548,7 +2548,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
|
|
|
2548
2548
|
for start in starts:
|
|
2549
2549
|
l.debug("Start symbolic execution at 0x%x on program slice.", start)
|
|
2550
2550
|
# Get the state from our CFG
|
|
2551
|
-
node = self.get_any_node(start)
|
|
2551
|
+
node = self.model.get_any_node(start)
|
|
2552
2552
|
if node is None:
|
|
2553
2553
|
# Well, we have to live with an empty state
|
|
2554
2554
|
base_state = self.project.factory.blank_state(addr=start)
|
|
@@ -2561,7 +2561,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
|
|
|
2561
2561
|
initial_nodes = [n for n in bc.taint_graph.nodes() if bc.taint_graph.in_degree(n) == 0]
|
|
2562
2562
|
for cl in initial_nodes:
|
|
2563
2563
|
# Iterate in all actions of this node, and pick corresponding actions
|
|
2564
|
-
cfg_nodes = self.get_all_nodes(cl.block_addr)
|
|
2564
|
+
cfg_nodes = self.model.get_all_nodes(cl.block_addr)
|
|
2565
2565
|
for n in cfg_nodes:
|
|
2566
2566
|
if not n.final_states:
|
|
2567
2567
|
continue
|
|
@@ -3376,9 +3376,9 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
|
|
|
3376
3376
|
|
|
3377
3377
|
all_predecessors = []
|
|
3378
3378
|
|
|
3379
|
-
nodes = self.get_all_nodes(function_address)
|
|
3379
|
+
nodes = self.model.get_all_nodes(function_address)
|
|
3380
3380
|
for n in nodes:
|
|
3381
|
-
predecessors = list(self.get_predecessors(n))
|
|
3381
|
+
predecessors = list(self.model.get_predecessors(n))
|
|
3382
3382
|
all_predecessors.extend(predecessors)
|
|
3383
3383
|
|
|
3384
3384
|
return all_predecessors
|
|
@@ -3391,8 +3391,8 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
|
|
|
3391
3391
|
Return: a list of lists of nodes representing paths.
|
|
3392
3392
|
"""
|
|
3393
3393
|
if isinstance(begin, int) and isinstance(end, int):
|
|
3394
|
-
n_begin = self.get_any_node(begin)
|
|
3395
|
-
n_end = self.get_any_node(end)
|
|
3394
|
+
n_begin = self.model.get_any_node(begin)
|
|
3395
|
+
n_end = self.model.get_any_node(end)
|
|
3396
3396
|
|
|
3397
3397
|
elif isinstance(begin, CFGENode) and isinstance(end, CFGENode):
|
|
3398
3398
|
n_begin = begin
|
|
@@ -3417,7 +3417,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
|
|
|
3417
3417
|
|
|
3418
3418
|
for ep in self._entry_points:
|
|
3419
3419
|
# FIXME: This is not always correct. We'd better store CFGNodes in self._entry_points
|
|
3420
|
-
ep_node = self.get_any_node(ep)
|
|
3420
|
+
ep_node = self.model.get_any_node(ep)
|
|
3421
3421
|
|
|
3422
3422
|
if not ep_node:
|
|
3423
3423
|
continue
|
angr/analyses/cfg/cfg_fast.py
CHANGED
|
@@ -22,10 +22,10 @@ from archinfo.arch_soot import SootAddressDescriptor
|
|
|
22
22
|
from archinfo.arch_arm import is_arm_arch, get_real_address_if_arm
|
|
23
23
|
|
|
24
24
|
from angr.analyses import AnalysesHub
|
|
25
|
+
from angr.misc.ux import once
|
|
25
26
|
from angr.knowledge_plugins.cfg import CFGNode, MemoryDataSort, MemoryData, IndirectJump, IndirectJumpType
|
|
26
27
|
from angr.knowledge_plugins.xrefs import XRef, XRefType
|
|
27
28
|
from angr.knowledge_plugins.functions import Function
|
|
28
|
-
from angr.misc.ux import deprecated
|
|
29
29
|
from angr.codenode import HookNode
|
|
30
30
|
from angr import sim_options as o
|
|
31
31
|
from angr.errors import (
|
|
@@ -589,10 +589,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
589
589
|
regions=None,
|
|
590
590
|
pickle_intermediate_results=False,
|
|
591
591
|
symbols=True,
|
|
592
|
-
function_prologues=
|
|
592
|
+
function_prologues: bool | None = None,
|
|
593
593
|
resolve_indirect_jumps=True,
|
|
594
594
|
force_segment=False,
|
|
595
|
-
force_smart_scan=
|
|
595
|
+
force_smart_scan: bool | None = None,
|
|
596
596
|
force_complete_scan=False,
|
|
597
597
|
indirect_jump_target_limit=100000,
|
|
598
598
|
data_references=True,
|
|
@@ -712,6 +712,20 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
712
712
|
if binary is not None and not objects:
|
|
713
713
|
objects = [binary]
|
|
714
714
|
|
|
715
|
+
is_dotnet = (
|
|
716
|
+
isinstance(self.project.loader.main_object, cle.backends.pe.PE)
|
|
717
|
+
and self.project.loader.main_object.is_dotnet
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
if function_prologues is None:
|
|
721
|
+
function_prologues = not is_dotnet
|
|
722
|
+
if is_dotnet and once("dotnet_native"):
|
|
723
|
+
l.warning("You're trying to analyze a .NET binary as native code. Are you sure?")
|
|
724
|
+
if force_smart_scan is None:
|
|
725
|
+
force_smart_scan = not is_dotnet
|
|
726
|
+
if is_dotnet and once("dotnet_native"):
|
|
727
|
+
l.warning("You're trying to analyze a .NET binary as native code. Are you sure?")
|
|
728
|
+
|
|
715
729
|
CFGBase.__init__(
|
|
716
730
|
self,
|
|
717
731
|
"fast",
|
|
@@ -1649,6 +1663,13 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1649
1663
|
if addr is not None:
|
|
1650
1664
|
# if this is ARM and addr % 4 != 0, it has to be THUMB
|
|
1651
1665
|
if is_arm_arch(self.project.arch):
|
|
1666
|
+
if (
|
|
1667
|
+
"has_arm_code" in self._arch_options
|
|
1668
|
+
and self._arch_options["has_arm_code"] is False
|
|
1669
|
+
and addr % 2 == 0
|
|
1670
|
+
):
|
|
1671
|
+
addr |= 1
|
|
1672
|
+
|
|
1652
1673
|
if addr % 2 == 0 and addr % 4 != 0:
|
|
1653
1674
|
# it's not aligned by 4, so it's definitely not ARM mode
|
|
1654
1675
|
addr |= 1
|
|
@@ -1968,9 +1989,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1968
1989
|
|
|
1969
1990
|
# Pre-compile all regexes
|
|
1970
1991
|
regexes = []
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1992
|
+
if "has_arm_code" not in self._arch_options or self._arch_options["has_arm_code"]:
|
|
1993
|
+
for ins_regex in self.project.arch.function_prologs:
|
|
1994
|
+
r = re.compile(ins_regex)
|
|
1995
|
+
regexes.append(r)
|
|
1974
1996
|
# EDG says: I challenge anyone bothering to read this to come up with a better
|
|
1975
1997
|
# way to handle CPU modes that affect instruction decoding.
|
|
1976
1998
|
# Since the only one we care about is ARM/Thumb right now
|
|
@@ -2109,6 +2131,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
2109
2131
|
src_func = self.functions.function(addr=cfg_job.src_node.addr, create=True)
|
|
2110
2132
|
else:
|
|
2111
2133
|
src_func = self.functions.get_by_addr(cfg_job.src_node.addr)
|
|
2134
|
+
assert src_func is not None
|
|
2112
2135
|
if len(src_func.block_addrs_set) <= 1 and src_func.is_default_name:
|
|
2113
2136
|
# assign a name to the caller function that jumps to this procedure
|
|
2114
2137
|
src_func.name = procedure.display_name
|
|
@@ -2832,6 +2855,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
2832
2855
|
and not self._seg_list.is_occupied(v)
|
|
2833
2856
|
and v % self.project.arch.instruction_alignment == 0
|
|
2834
2857
|
):
|
|
2858
|
+
if is_arm_arch(self.project.arch) and not self._arch_options.has_arm_code and v % 2 != 1:
|
|
2859
|
+
# no ARM code in this binary!
|
|
2860
|
+
return
|
|
2861
|
+
|
|
2835
2862
|
# create a new CFG job
|
|
2836
2863
|
ce = CFGJob(
|
|
2837
2864
|
v,
|
|
@@ -3831,6 +3858,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
3831
3858
|
|
|
3832
3859
|
for ep in endpoints:
|
|
3833
3860
|
src = self.model.get_any_node(ep.addr)
|
|
3861
|
+
assert src is not None
|
|
3834
3862
|
for rt in return_targets:
|
|
3835
3863
|
if not src.instruction_addrs:
|
|
3836
3864
|
ins_addr = None
|
|
@@ -4321,7 +4349,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
4321
4349
|
|
|
4322
4350
|
# extra check for ARM
|
|
4323
4351
|
if is_arm_arch(self.project.arch) and self._seg_list.occupied_by_sort(addr) == "code":
|
|
4324
|
-
existing_node = self.get_any_node(addr, anyaddr=True)
|
|
4352
|
+
existing_node = self.model.get_any_node(addr, anyaddr=True)
|
|
4325
4353
|
if existing_node is not None and (addr & 1) != (existing_node.addr & 1):
|
|
4326
4354
|
# we are trying to break an existing ARM node with a THUMB node, or vice versa
|
|
4327
4355
|
# this is probably because our current node is unexpected
|
|
@@ -4430,6 +4458,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
4430
4458
|
self._cascading_remove_lifted_blocks(cfg_job.src_node.addr & 0xFFFF_FFFE)
|
|
4431
4459
|
return None, None, None, None
|
|
4432
4460
|
|
|
4461
|
+
if not self._arch_options.has_arm_code and addr % 2 == 0:
|
|
4462
|
+
# No ARM code for this architecture!
|
|
4463
|
+
return None, None, None, None
|
|
4464
|
+
|
|
4433
4465
|
initial_regs = self._get_initial_registers(addr, cfg_job, current_function_addr)
|
|
4434
4466
|
|
|
4435
4467
|
# Let's try to create the pyvex IRSB directly, since it's much faster
|
|
@@ -5196,18 +5228,5 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
5196
5228
|
def output(self):
|
|
5197
5229
|
return f"{self._graph.edges(data=True)}"
|
|
5198
5230
|
|
|
5199
|
-
@deprecated(replacement="angr.analyses.CFB")
|
|
5200
|
-
def generate_code_cover(self):
|
|
5201
|
-
"""
|
|
5202
|
-
Generate a list of all recovered basic blocks.
|
|
5203
|
-
"""
|
|
5204
|
-
|
|
5205
|
-
lst = []
|
|
5206
|
-
for cfg_node in self.graph.nodes():
|
|
5207
|
-
size = cfg_node.size
|
|
5208
|
-
lst.append((cfg_node.addr, size))
|
|
5209
|
-
|
|
5210
|
-
return sorted(lst, key=lambda x: x[0])
|
|
5211
|
-
|
|
5212
5231
|
|
|
5213
5232
|
AnalysesHub.register_default("CFGFast", CFGFast)
|
|
@@ -487,7 +487,7 @@ class CFGFastSoot(CFGFast):
|
|
|
487
487
|
# it might be a jumpout
|
|
488
488
|
target_func_addr = None
|
|
489
489
|
if target_addr in self._traced_addresses:
|
|
490
|
-
node = self.get_any_node(target_addr)
|
|
490
|
+
node = self.model.get_any_node(target_addr)
|
|
491
491
|
if node is not None:
|
|
492
492
|
target_func_addr = node.function_address
|
|
493
493
|
if target_func_addr is None:
|
|
@@ -578,7 +578,7 @@ class CFGFastSoot(CFGFast):
|
|
|
578
578
|
if jumpkind == "Ijk_Call" or jumpkind.startswith("Ijk_Sys"):
|
|
579
579
|
function_nodes.add(dst)
|
|
580
580
|
|
|
581
|
-
entry_node = self.get_any_node(self._binary.entry)
|
|
581
|
+
entry_node = self.model.get_any_node(self._binary.entry)
|
|
582
582
|
if entry_node is not None:
|
|
583
583
|
function_nodes.add(entry_node)
|
|
584
584
|
|
|
@@ -616,7 +616,7 @@ class CFGFastSoot(CFGFast):
|
|
|
616
616
|
secondary_function_nodes = set()
|
|
617
617
|
# add all function chunks ("functions" that are not called from anywhere)
|
|
618
618
|
for func_addr in tmp_functions:
|
|
619
|
-
node = self.get_any_node(func_addr)
|
|
619
|
+
node = self.model.get_any_node(func_addr)
|
|
620
620
|
if node is None:
|
|
621
621
|
continue
|
|
622
622
|
if node.addr not in blockaddr_to_function:
|
|
@@ -18,7 +18,7 @@ from angr.sim_type import (
|
|
|
18
18
|
SimTypeFunction,
|
|
19
19
|
SimTypeLongLong,
|
|
20
20
|
)
|
|
21
|
-
from angr.calling_conventions import SimRegArg, SimStackArg, SimCC, SimStructArg, SimComboArg
|
|
21
|
+
from angr.calling_conventions import SimReferenceArgument, SimRegArg, SimStackArg, SimCC, SimStructArg, SimComboArg
|
|
22
22
|
from angr.knowledge_plugins.key_definitions.constants import OP_BEFORE
|
|
23
23
|
from angr.analyses import Analysis, register_analysis
|
|
24
24
|
from angr.analyses.s_reaching_definitions import SRDAView
|
|
@@ -111,10 +111,10 @@ class CallSiteMaker(Analysis):
|
|
|
111
111
|
prototype_libname = func.prototype_libname
|
|
112
112
|
type_collections = []
|
|
113
113
|
if prototype_libname is not None:
|
|
114
|
-
prototype_lib
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
for prototype_lib in SIM_LIBRARIES[prototype_libname]:
|
|
115
|
+
if prototype_lib.type_collection_names:
|
|
116
|
+
for typelib_name in prototype_lib.type_collection_names:
|
|
117
|
+
type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
|
|
118
118
|
if type_collections:
|
|
119
119
|
prototype = dereference_simtype(prototype, type_collections).with_arch( # type: ignore
|
|
120
120
|
self.project.arch
|
|
@@ -144,17 +144,30 @@ class CallSiteMaker(Analysis):
|
|
|
144
144
|
arg_locs = cc.arg_locs(callsite_ty)
|
|
145
145
|
|
|
146
146
|
if arg_locs is not None and cc is not None:
|
|
147
|
-
expanded_arg_locs = []
|
|
147
|
+
expanded_arg_locs: list[SimStackArg | SimRegArg | SimReferenceArgument] = []
|
|
148
148
|
for arg_loc in arg_locs:
|
|
149
149
|
if isinstance(arg_loc, SimComboArg):
|
|
150
150
|
# a ComboArg spans across multiple locations (mostly stack but *in theory* can also be spanning
|
|
151
151
|
# across registers). most importantly, a ComboArg represents one variable, not multiple, but we
|
|
152
152
|
# have no way to know that until later down the pipeline.
|
|
153
153
|
expanded_arg_locs += arg_loc.locations
|
|
154
|
-
|
|
154
|
+
elif isinstance(arg_loc, (SimRegArg, SimStackArg, SimReferenceArgument)):
|
|
155
155
|
expanded_arg_locs.append(arg_loc)
|
|
156
|
+
else:
|
|
157
|
+
raise NotImplementedError("Not implemented yet.")
|
|
156
158
|
|
|
157
159
|
for arg_loc in expanded_arg_locs:
|
|
160
|
+
if isinstance(arg_loc, SimReferenceArgument):
|
|
161
|
+
if not isinstance(arg_loc.ptr_loc, (SimRegArg, SimStackArg)):
|
|
162
|
+
raise NotImplementedError("Why would a calling convention produce this?")
|
|
163
|
+
if isinstance(arg_loc.main_loc, SimStructArg):
|
|
164
|
+
dereference_size = arg_loc.main_loc.struct.size // self.project.arch.byte_width
|
|
165
|
+
else:
|
|
166
|
+
dereference_size = arg_loc.main_loc.size
|
|
167
|
+
arg_loc = arg_loc.ptr_loc
|
|
168
|
+
else:
|
|
169
|
+
dereference_size = None
|
|
170
|
+
|
|
158
171
|
if isinstance(arg_loc, SimRegArg):
|
|
159
172
|
size = arg_loc.size
|
|
160
173
|
offset = arg_loc.check_offset(cc.arch)
|
|
@@ -202,7 +215,7 @@ class CallSiteMaker(Analysis):
|
|
|
202
215
|
vvar_use,
|
|
203
216
|
**vvar_use.tags,
|
|
204
217
|
)
|
|
205
|
-
|
|
218
|
+
arg_expr = vvar_use
|
|
206
219
|
else:
|
|
207
220
|
reg = Expr.Register(
|
|
208
221
|
self._atom_idx(),
|
|
@@ -212,20 +225,17 @@ class CallSiteMaker(Analysis):
|
|
|
212
225
|
reg_name=arg_loc.reg_name,
|
|
213
226
|
ins_addr=last_stmt.ins_addr,
|
|
214
227
|
)
|
|
215
|
-
|
|
228
|
+
arg_expr = reg
|
|
216
229
|
elif isinstance(arg_loc, SimStackArg):
|
|
217
230
|
stack_arg_locs.append(arg_loc)
|
|
218
231
|
_, the_arg = self._resolve_stack_argument(call_stmt, arg_loc)
|
|
219
|
-
|
|
220
|
-
if the_arg is not None:
|
|
221
|
-
args.append(the_arg)
|
|
222
|
-
else:
|
|
223
|
-
args.append(None)
|
|
224
|
-
elif isinstance(arg_loc, SimStructArg):
|
|
225
|
-
l.warning("SimStructArg is not yet supported")
|
|
226
|
-
|
|
232
|
+
arg_expr = the_arg if the_arg is not None else None
|
|
227
233
|
else:
|
|
228
|
-
|
|
234
|
+
assert False, "Unreachable"
|
|
235
|
+
|
|
236
|
+
if arg_expr is not None and dereference_size is not None:
|
|
237
|
+
arg_expr = Expr.Load(self._atom_idx(), arg_expr, dereference_size, endness=archinfo.Endness.BE)
|
|
238
|
+
args.append(arg_expr)
|
|
229
239
|
|
|
230
240
|
# Remove the old call statement
|
|
231
241
|
new_stmts = self.block.statements[:-1]
|
|
@@ -1197,10 +1197,10 @@ class Clinic(Analysis):
|
|
|
1197
1197
|
prototype_libname = func.prototype_libname
|
|
1198
1198
|
type_collections = []
|
|
1199
1199
|
if prototype_libname is not None:
|
|
1200
|
-
prototype_lib
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1200
|
+
for prototype_lib in SIM_LIBRARIES[prototype_libname]:
|
|
1201
|
+
if prototype_lib.type_collection_names:
|
|
1202
|
+
for typelib_name in prototype_lib.type_collection_names:
|
|
1203
|
+
type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
|
|
1204
1204
|
if type_collections:
|
|
1205
1205
|
prototype = dereference_simtype(prototype, type_collections).with_arch( # type: ignore
|
|
1206
1206
|
self.project.arch
|
|
@@ -961,27 +961,6 @@ class ConditionProcessor:
|
|
|
961
961
|
sympy_expr = ConditionProcessor.claripy_ast_to_sympy_expr(cond, memo=memo)
|
|
962
962
|
return ConditionProcessor.sympy_expr_to_claripy_ast(sympy.simplify_logic(sympy_expr, deep=False), memo)
|
|
963
963
|
|
|
964
|
-
@staticmethod
|
|
965
|
-
def simplify_condition_deprecated(cond):
|
|
966
|
-
# Z3's simplification may yield weird and unreadable results
|
|
967
|
-
# hence we mostly rely on our own simplification. we only use Z3's simplification results when it returns a
|
|
968
|
-
# concrete value.
|
|
969
|
-
claripy_simplified = claripy.simplify(cond)
|
|
970
|
-
if not claripy_simplified.symbolic:
|
|
971
|
-
return claripy_simplified
|
|
972
|
-
|
|
973
|
-
simplified = ConditionProcessor._fold_double_negations(cond)
|
|
974
|
-
cond = simplified if simplified is not None else cond
|
|
975
|
-
simplified = ConditionProcessor._revert_short_circuit_conditions(cond)
|
|
976
|
-
cond = simplified if simplified is not None else cond
|
|
977
|
-
simplified = ConditionProcessor._extract_common_subexpressions(cond)
|
|
978
|
-
cond = simplified if simplified is not None else cond
|
|
979
|
-
# simplified = ConditionProcessor._remove_redundant_terms(cond)
|
|
980
|
-
# cond = simplified if simplified is not None else cond
|
|
981
|
-
# in the end, use claripy's simplification to handle really easy cases again
|
|
982
|
-
simplified = ConditionProcessor._simplify_trivial_cases(cond)
|
|
983
|
-
return simplified if simplified is not None else cond
|
|
984
|
-
|
|
985
964
|
@staticmethod
|
|
986
965
|
def _simplify_trivial_cases(cond):
|
|
987
966
|
if cond.op == "And":
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from ailment import Block
|
|
5
|
+
from ailment.statement import Label
|
|
5
6
|
from ailment.block_walker import AILBlockWalkerBase
|
|
6
7
|
|
|
7
8
|
from angr.analyses.decompiler.sequence_walker import SequenceWalker
|
|
@@ -37,8 +38,10 @@ class AILCallCounter(SequenceWalker):
|
|
|
37
38
|
}
|
|
38
39
|
super().__init__(handlers)
|
|
39
40
|
self.calls = 0
|
|
41
|
+
self.non_label_stmts = 0
|
|
40
42
|
|
|
41
43
|
def _handle_Block(self, node: Block, **kwargs): # pylint:disable=unused-argument
|
|
42
44
|
ctr = AILBlockCallCounter()
|
|
43
45
|
ctr.walk(node)
|
|
44
46
|
self.calls += ctr.calls
|
|
47
|
+
self.non_label_stmts += sum(1 for stmt in node.statements if not isinstance(stmt, Label))
|
|
@@ -66,7 +66,7 @@ class PairAILBlockWalker:
|
|
|
66
66
|
def _handle_call_expr(expr_idx: int, expr: Call, stmt_idx: int, stmt: Statement, block_):
|
|
67
67
|
walked_objs[Call].add(expr)
|
|
68
68
|
|
|
69
|
-
_stmt_handlers =
|
|
69
|
+
_stmt_handlers = dict.fromkeys(walked_objs, _handle_ail_obj)
|
|
70
70
|
walker.stmt_handlers = _stmt_handlers
|
|
71
71
|
walker.expr_handlers[Call] = _handle_call_expr
|
|
72
72
|
|
|
@@ -164,6 +164,20 @@ class RemoveRedundantConversions(PeepholeOptimizationExprBase):
|
|
|
164
164
|
**expr.tags,
|
|
165
165
|
)
|
|
166
166
|
|
|
167
|
+
# simpler cases
|
|
168
|
+
# (A & mask) & mask ==> A & mask
|
|
169
|
+
if (
|
|
170
|
+
expr.op == "And"
|
|
171
|
+
and isinstance(expr.operands[1], Const)
|
|
172
|
+
and isinstance(expr.operands[0], BinaryOp)
|
|
173
|
+
and expr.operands[0].op == "And"
|
|
174
|
+
):
|
|
175
|
+
inner_op0, inner_op1 = expr.operands[0].operands
|
|
176
|
+
if (isinstance(inner_op0, Const) and inner_op0.value == expr.operands[1].value) or (
|
|
177
|
+
isinstance(inner_op1, Const) and inner_op1.value == expr.operands[1].value
|
|
178
|
+
):
|
|
179
|
+
return expr.operands[0]
|
|
180
|
+
|
|
167
181
|
return None
|
|
168
182
|
|
|
169
183
|
@staticmethod
|
|
@@ -1281,11 +1281,11 @@ class CFunctionCall(CStatement, CExpression):
|
|
|
1281
1281
|
if self.callee_func.prototype_libname is not None:
|
|
1282
1282
|
# we need to deref the prototype in case it uses SimTypeRef internally
|
|
1283
1283
|
type_collections = []
|
|
1284
|
-
prototype_lib
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1284
|
+
for prototype_lib in SIM_LIBRARIES[self.callee_func.prototype_libname]:
|
|
1285
|
+
if prototype_lib.type_collection_names:
|
|
1286
|
+
for typelib_name in prototype_lib.type_collection_names:
|
|
1287
|
+
type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
|
|
1288
|
+
proto = dereference_simtype(proto, type_collections)
|
|
1289
1289
|
return proto
|
|
1290
1290
|
returnty = SimTypeInt(signed=False)
|
|
1291
1291
|
return SimTypeFunction([arg.type for arg in self.args], returnty).with_arch(self.codegen.project.arch)
|