angr 9.2.135__py3-none-macosx_11_0_arm64.whl → 9.2.137__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/__init__.py +3 -7
- angr/analyses/analysis.py +4 -0
- angr/analyses/backward_slice.py +1 -2
- angr/analyses/binary_optimizer.py +3 -4
- angr/analyses/bindiff.py +4 -6
- angr/analyses/boyscout.py +1 -3
- angr/analyses/callee_cleanup_finder.py +4 -4
- angr/analyses/calling_convention/calling_convention.py +6 -4
- angr/analyses/calling_convention/fact_collector.py +10 -3
- angr/analyses/cdg.py +1 -2
- angr/analyses/cfg/cfb.py +1 -3
- angr/analyses/cfg/cfg.py +2 -2
- angr/analyses/cfg/cfg_base.py +40 -68
- angr/analyses/cfg/cfg_emulated.py +1 -104
- angr/analyses/cfg/cfg_fast.py +90 -27
- angr/analyses/cfg/cfg_fast_soot.py +1 -1
- angr/analyses/cfg/indirect_jump_resolvers/__init__.py +2 -0
- angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +46 -10
- angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +5 -1
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +65 -14
- angr/analyses/cfg/indirect_jump_resolvers/memload_resolver.py +81 -0
- angr/analyses/cfg/indirect_jump_resolvers/propagator_utils.py +24 -5
- angr/analyses/cfg/indirect_jump_resolvers/x86_pe_iat.py +2 -5
- angr/analyses/class_identifier.py +1 -2
- angr/analyses/complete_calling_conventions.py +3 -0
- angr/analyses/congruency_check.py +2 -3
- angr/analyses/data_dep/data_dependency_analysis.py +2 -2
- angr/analyses/ddg.py +1 -4
- angr/analyses/decompiler/ail_simplifier.py +15 -5
- angr/analyses/decompiler/block_simplifier.py +2 -2
- angr/analyses/decompiler/ccall_rewriters/__init__.py +2 -0
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -1
- angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +69 -0
- angr/analyses/decompiler/clinic.py +119 -72
- angr/analyses/decompiler/condition_processor.py +2 -0
- angr/analyses/decompiler/decompiler.py +1 -0
- angr/analyses/decompiler/dephication/dephication_base.py +2 -0
- angr/analyses/decompiler/dephication/rewriting_engine.py +8 -6
- angr/analyses/decompiler/dephication/seqnode_dephication.py +10 -1
- angr/analyses/decompiler/optimization_passes/duplication_reverter/ail_merge_graph.py +2 -2
- angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +2 -2
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +1 -1
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +1 -2
- angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +1 -1
- angr/analyses/decompiler/sequence_walker.py +6 -2
- angr/analyses/decompiler/ssailification/rewriting.py +11 -1
- angr/analyses/decompiler/ssailification/rewriting_engine.py +56 -19
- angr/analyses/decompiler/ssailification/ssailification.py +13 -3
- angr/analyses/decompiler/ssailification/traversal.py +28 -2
- angr/analyses/decompiler/ssailification/traversal_state.py +6 -1
- angr/analyses/decompiler/structured_codegen/c.py +44 -21
- angr/analyses/decompiler/structuring/phoenix.py +118 -15
- angr/analyses/decompiler/utils.py +113 -8
- angr/analyses/disassembly.py +5 -5
- angr/analyses/fcp/__init__.py +4 -0
- angr/analyses/fcp/fcp.py +429 -0
- angr/analyses/identifier/identify.py +1 -3
- angr/analyses/loopfinder.py +4 -3
- angr/analyses/patchfinder.py +1 -1
- angr/analyses/propagator/engine_base.py +4 -3
- angr/analyses/propagator/propagator.py +14 -53
- angr/analyses/reaching_definitions/function_handler.py +1 -1
- angr/analyses/reassembler.py +1 -2
- angr/analyses/s_liveness.py +5 -1
- angr/analyses/s_propagator.py +26 -7
- angr/analyses/s_reaching_definitions/s_rda_model.py +2 -1
- angr/analyses/s_reaching_definitions/s_rda_view.py +20 -1
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +11 -1
- angr/analyses/soot_class_hierarchy.py +1 -2
- angr/analyses/stack_pointer_tracker.py +29 -3
- angr/analyses/static_hooker.py +1 -2
- angr/analyses/typehoon/simple_solver.py +2 -2
- angr/analyses/variable_recovery/engine_ail.py +19 -7
- angr/analyses/variable_recovery/engine_base.py +16 -14
- angr/analyses/variable_recovery/engine_vex.py +2 -2
- angr/analyses/variable_recovery/variable_recovery_fast.py +23 -3
- angr/analyses/veritesting.py +4 -7
- angr/analyses/vfg.py +1 -1
- angr/analyses/vsa_ddg.py +1 -2
- angr/block.py +62 -22
- angr/callable.py +1 -3
- angr/calling_conventions.py +3 -3
- angr/codenode.py +5 -1
- angr/concretization_strategies/__init__.py +1 -83
- angr/concretization_strategies/any.py +2 -1
- angr/concretization_strategies/any_named.py +1 -1
- angr/concretization_strategies/base.py +81 -0
- angr/concretization_strategies/controlled_data.py +2 -1
- angr/concretization_strategies/eval.py +2 -1
- angr/concretization_strategies/logging.py +3 -1
- angr/concretization_strategies/max.py +2 -1
- angr/concretization_strategies/nonzero.py +2 -1
- angr/concretization_strategies/nonzero_range.py +2 -1
- angr/concretization_strategies/norepeats.py +2 -1
- angr/concretization_strategies/norepeats_range.py +2 -1
- angr/concretization_strategies/range.py +2 -1
- angr/concretization_strategies/signed_add.py +2 -1
- angr/concretization_strategies/single.py +2 -1
- angr/concretization_strategies/solutions.py +2 -1
- angr/concretization_strategies/unlimited_range.py +2 -1
- angr/engines/__init__.py +8 -5
- angr/engines/engine.py +3 -5
- angr/engines/failure.py +4 -5
- angr/engines/pcode/emulate.py +1 -1
- angr/engines/pcode/lifter.py +31 -18
- angr/engines/procedure.py +5 -7
- angr/engines/soot/expressions/__init__.py +20 -23
- angr/engines/soot/expressions/base.py +4 -4
- angr/engines/soot/expressions/invoke.py +1 -2
- angr/engines/soot/statements/__init__.py +10 -12
- angr/engines/soot/values/__init__.py +10 -12
- angr/engines/soot/values/arrayref.py +3 -3
- angr/engines/soot/values/instancefieldref.py +3 -2
- angr/engines/successors.py +18 -12
- angr/engines/syscall.py +4 -6
- angr/engines/unicorn.py +3 -2
- angr/engines/vex/claripy/ccall.py +8 -10
- angr/engines/vex/claripy/datalayer.py +4 -5
- angr/engines/vex/lifter.py +9 -6
- angr/exploration_techniques/__init__.py +0 -2
- angr/exploration_techniques/spiller.py +1 -3
- angr/exploration_techniques/stochastic.py +2 -3
- angr/factory.py +3 -9
- angr/flirt/build_sig.py +8 -15
- angr/knowledge_plugins/cfg/cfg_model.py +20 -17
- angr/knowledge_plugins/functions/function.py +70 -79
- angr/knowledge_plugins/functions/function_manager.py +8 -7
- angr/knowledge_plugins/functions/function_parser.py +1 -1
- angr/knowledge_plugins/functions/soot_function.py +21 -24
- angr/knowledge_plugins/propagations/propagation_model.py +4 -5
- angr/knowledge_plugins/propagations/states.py +0 -511
- angr/knowledge_plugins/variables/variable_manager.py +16 -10
- angr/lib/angr_native.dylib +0 -0
- angr/procedures/libc/memcpy.py +4 -4
- angr/procedures/procedure_dict.py +3 -2
- angr/protos/__init__.py +2 -5
- angr/protos/cfg_pb2.py +21 -18
- angr/protos/function_pb2.py +17 -14
- angr/protos/primitives_pb2.py +44 -39
- angr/protos/variables_pb2.py +36 -31
- angr/protos/xrefs_pb2.py +15 -12
- angr/sim_procedure.py +15 -16
- angr/sim_variable.py +13 -1
- angr/simos/__init__.py +2 -0
- angr/simos/javavm.py +4 -6
- angr/simos/xbox.py +32 -0
- angr/state_plugins/__init__.py +0 -2
- angr/state_plugins/callstack.py +4 -4
- angr/state_plugins/cgc.py +3 -2
- angr/state_plugins/gdb.py +6 -5
- angr/state_plugins/globals.py +1 -2
- angr/state_plugins/heap/heap_brk.py +1 -2
- angr/state_plugins/history.py +10 -12
- angr/state_plugins/inspect.py +3 -5
- angr/state_plugins/libc.py +2 -2
- angr/state_plugins/log.py +8 -10
- angr/state_plugins/loop_data.py +1 -2
- angr/state_plugins/posix.py +7 -7
- angr/state_plugins/preconstrainer.py +2 -3
- angr/state_plugins/scratch.py +5 -8
- angr/state_plugins/sim_action.py +3 -3
- angr/state_plugins/solver.py +8 -3
- angr/state_plugins/symbolizer.py +5 -4
- angr/state_plugins/uc_manager.py +3 -3
- angr/state_plugins/unicorn_engine.py +5 -1
- angr/state_plugins/view.py +3 -5
- angr/storage/file.py +3 -5
- angr/storage/memory_mixins/address_concretization_mixin.py +2 -2
- angr/storage/memory_mixins/bvv_conversion_mixin.py +3 -3
- angr/storage/memory_mixins/clouseau_mixin.py +1 -3
- angr/storage/memory_mixins/name_resolution_mixin.py +1 -3
- angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +13 -15
- angr/storage/memory_mixins/paged_memory/pages/__init__.py +1 -22
- angr/storage/memory_mixins/paged_memory/pages/base.py +31 -0
- angr/storage/memory_mixins/paged_memory/pages/list_page.py +1 -1
- angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +1 -1
- angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +2 -4
- angr/storage/memory_mixins/paged_memory/privileged_mixin.py +3 -4
- angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +4 -2
- angr/storage/memory_mixins/smart_find_mixin.py +1 -1
- angr/storage/memory_mixins/underconstrained_mixin.py +1 -1
- angr/storage/memory_mixins/unwrapper_mixin.py +1 -3
- angr/utils/enums_conv.py +28 -12
- angr/utils/segment_list.py +25 -22
- angr/utils/timing.py +18 -1
- angr/vaults.py +5 -6
- {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/METADATA +7 -7
- {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/RECORD +194 -192
- {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/WHEEL +1 -1
- angr/analyses/propagator/outdated_definition_walker.py +0 -159
- angr/analyses/propagator/tmpvar_finder.py +0 -18
- angr/engines/concrete.py +0 -180
- angr/exploration_techniques/symbion.py +0 -80
- angr/state_plugins/concrete.py +0 -295
- {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/LICENSE +0 -0
- {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/entry_points.txt +0 -0
- {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/top_level.txt +0 -0
angr/analyses/cfg/cfg_fast.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# pylint:disable=superfluous-parens,too-many-boolean-expressions,line-too-long
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
3
4
|
import itertools
|
|
4
5
|
import logging
|
|
5
6
|
import math
|
|
@@ -52,6 +53,10 @@ from .cfg_arch_options import CFGArchOptions
|
|
|
52
53
|
from .cfg_base import CFGBase
|
|
53
54
|
from .indirect_jump_resolvers.jumptable import JumpTableResolver
|
|
54
55
|
|
|
56
|
+
if TYPE_CHECKING:
|
|
57
|
+
from angr.block import Block
|
|
58
|
+
from angr.engines.pcode.lifter import IRSB as PcodeIRSB
|
|
59
|
+
|
|
55
60
|
|
|
56
61
|
VEX_IRSB_MAX_SIZE = 400
|
|
57
62
|
|
|
@@ -135,9 +140,9 @@ class PendingJobs:
|
|
|
135
140
|
A collection of pending jobs during CFG recovery.
|
|
136
141
|
"""
|
|
137
142
|
|
|
138
|
-
def __init__(self,
|
|
143
|
+
def __init__(self, kb, deregister_job_callback):
|
|
139
144
|
self._jobs = OrderedDict() # A mapping between function addresses and lists of pending jobs
|
|
140
|
-
self.
|
|
145
|
+
self._kb = kb
|
|
141
146
|
self._deregister_job_callback = deregister_job_callback
|
|
142
147
|
|
|
143
148
|
self._returning_functions = set()
|
|
@@ -145,6 +150,10 @@ class PendingJobs:
|
|
|
145
150
|
# consecutive calls to cleanup().
|
|
146
151
|
self._job_count = 0
|
|
147
152
|
|
|
153
|
+
@property
|
|
154
|
+
def _functions(self):
|
|
155
|
+
return self._kb.functions
|
|
156
|
+
|
|
148
157
|
def __len__(self):
|
|
149
158
|
return self._job_count
|
|
150
159
|
|
|
@@ -821,10 +830,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
821
830
|
#
|
|
822
831
|
# Variables used during analysis
|
|
823
832
|
#
|
|
824
|
-
self._pending_jobs = None
|
|
825
|
-
self._traced_addresses = None
|
|
833
|
+
self._pending_jobs = None # type:ignore
|
|
834
|
+
self._traced_addresses = None # type:ignore
|
|
826
835
|
self._function_returns = None
|
|
827
|
-
self._function_exits = None
|
|
836
|
+
self._function_exits = None # type:ignore
|
|
828
837
|
self._gp_value: int | None = None
|
|
829
838
|
self._ro_region_cdata_cache: list | None = None
|
|
830
839
|
self._job_ctr = 0
|
|
@@ -868,7 +877,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
868
877
|
if size is None:
|
|
869
878
|
size = len(data)
|
|
870
879
|
|
|
871
|
-
data = bytes(pyvex.ffi.buffer(data, size))
|
|
880
|
+
data = bytes(pyvex.ffi.buffer(data, size)) # type:ignore
|
|
872
881
|
for x in range(256):
|
|
873
882
|
p_x = float(data.count(x)) / size
|
|
874
883
|
if p_x > 0:
|
|
@@ -1225,7 +1234,9 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1225
1234
|
# umm now it's probably code
|
|
1226
1235
|
break
|
|
1227
1236
|
|
|
1228
|
-
instr_alignment = self.
|
|
1237
|
+
instr_alignment = self.project.arch.instruction_alignment
|
|
1238
|
+
if not instr_alignment:
|
|
1239
|
+
instr_alignment = 1
|
|
1229
1240
|
if start_addr % instr_alignment > 0:
|
|
1230
1241
|
# occupy those few bytes
|
|
1231
1242
|
size = instr_alignment - (start_addr % instr_alignment)
|
|
@@ -1282,7 +1293,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1282
1293
|
nodecode_bytes_ratio = (
|
|
1283
1294
|
0.0 if self._next_addr is None else self._nodecode_bytes_ratio(self._next_addr, self._nodecode_window_size)
|
|
1284
1295
|
)
|
|
1285
|
-
if nodecode_bytes_ratio >= self._nodecode_threshold:
|
|
1296
|
+
if nodecode_bytes_ratio >= self._nodecode_threshold and self._next_addr is not None:
|
|
1286
1297
|
next_allowed_addr = self._next_addr + self._nodecode_step
|
|
1287
1298
|
else:
|
|
1288
1299
|
next_allowed_addr = 0
|
|
@@ -1309,6 +1320,9 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1309
1320
|
return job.addr
|
|
1310
1321
|
|
|
1311
1322
|
def _pre_analysis(self):
|
|
1323
|
+
# Create a read-only memory view in loader for faster data loading
|
|
1324
|
+
self.project.loader.gen_ro_memview()
|
|
1325
|
+
|
|
1312
1326
|
# Call _initialize_cfg() before self.functions is used.
|
|
1313
1327
|
self._initialize_cfg()
|
|
1314
1328
|
|
|
@@ -1316,7 +1330,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1316
1330
|
self._known_thunks = self._find_thunks()
|
|
1317
1331
|
|
|
1318
1332
|
# Initialize variables used during analysis
|
|
1319
|
-
self._pending_jobs: PendingJobs = PendingJobs(self.
|
|
1333
|
+
self._pending_jobs: PendingJobs = PendingJobs(self.kb, self._deregister_analysis_job)
|
|
1320
1334
|
self._traced_addresses: set[int] = {a for a, n in self._nodes_by_addr.items() if n}
|
|
1321
1335
|
self._function_returns = defaultdict(set)
|
|
1322
1336
|
|
|
@@ -1498,6 +1512,18 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1498
1512
|
pass
|
|
1499
1513
|
|
|
1500
1514
|
def _function_completed(self, func_addr: int):
|
|
1515
|
+
if self.project.arch.name == "AMD64":
|
|
1516
|
+
# determine if the function is __rust_probestack
|
|
1517
|
+
func = self.kb.functions.get_by_addr(func_addr) if self.kb.functions.contains_addr(func_addr) else None
|
|
1518
|
+
if func is not None and len(func.block_addrs_set) == 3:
|
|
1519
|
+
block_bytes = {func.get_block(block_addr).bytes for block_addr in func.block_addrs_set}
|
|
1520
|
+
if block_bytes == {
|
|
1521
|
+
b"UH\x89\xe5I\x89\xc3I\x81\xfb\x00\x10\x00\x00v\x1c",
|
|
1522
|
+
b"H\x81\xec\x00\x10\x00\x00H\x85d$\x08I\x81\xeb\x00\x10\x00\x00I\x81\xfb\x00\x10\x00\x00w\xe4",
|
|
1523
|
+
b"L)\xdcH\x85d$\x08H\x01\xc4\xc9\xc3",
|
|
1524
|
+
}:
|
|
1525
|
+
func.info["is_rust_probestack"] = True
|
|
1526
|
+
|
|
1501
1527
|
if self._collect_data_ref and self.project is not None and ":" in self.project.arch.name:
|
|
1502
1528
|
# this is a pcode arch - use Clinic to recover data references
|
|
1503
1529
|
|
|
@@ -1741,9 +1767,13 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1741
1767
|
|
|
1742
1768
|
CFGBase._post_analysis(self)
|
|
1743
1769
|
|
|
1770
|
+
# drop the read-only memory view in loader
|
|
1771
|
+
self.project.loader.discard_ro_memview()
|
|
1772
|
+
|
|
1744
1773
|
# Clean up
|
|
1745
1774
|
self._traced_addresses = None
|
|
1746
1775
|
self._lifter_deregister_readonly_regions()
|
|
1776
|
+
self._function_returns = None
|
|
1747
1777
|
|
|
1748
1778
|
self._finish_progress()
|
|
1749
1779
|
|
|
@@ -1825,19 +1855,18 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1825
1855
|
break
|
|
1826
1856
|
|
|
1827
1857
|
# special handling: some binaries do not have SecurityCookie set, but still contain _security_init_cookie
|
|
1828
|
-
if security_init_cookie_found is False:
|
|
1858
|
+
if security_init_cookie_found is False and self.functions.contains_addr(self.project.entry):
|
|
1829
1859
|
start_func = self.functions.get_by_addr(self.project.entry)
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
break
|
|
1860
|
+
for callee in start_func.transition_graph:
|
|
1861
|
+
if (
|
|
1862
|
+
isinstance(callee, Function)
|
|
1863
|
+
and not security_init_cookie_found
|
|
1864
|
+
and is_function_likely_security_init_cookie(callee)
|
|
1865
|
+
):
|
|
1866
|
+
security_init_cookie_found = True
|
|
1867
|
+
callee.is_default_name = False
|
|
1868
|
+
callee.name = "_security_init_cookie"
|
|
1869
|
+
break
|
|
1841
1870
|
|
|
1842
1871
|
def _post_process_string_references(self) -> None:
|
|
1843
1872
|
"""
|
|
@@ -2910,7 +2939,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
2910
2939
|
if v is not None:
|
|
2911
2940
|
tmps[stmt.tmp] = v
|
|
2912
2941
|
|
|
2913
|
-
elif type(stmt.data)
|
|
2942
|
+
elif type(stmt.data) is pyvex.IRExpr.Binop: # pylint: disable=unidiomatic-typecheck
|
|
2914
2943
|
# rip-related addressing
|
|
2915
2944
|
if stmt.data.op in ("Iop_Add32", "Iop_Add64"):
|
|
2916
2945
|
if all(type(arg) is pyvex.expr.Const for arg in stmt.data.args):
|
|
@@ -4396,8 +4425,8 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
4396
4425
|
|
|
4397
4426
|
# Let's try to create the pyvex IRSB directly, since it's much faster
|
|
4398
4427
|
nodecode = False
|
|
4399
|
-
irsb = None
|
|
4400
|
-
lifted_block = None
|
|
4428
|
+
irsb: pyvex.IRSB | PcodeIRSB | None = None
|
|
4429
|
+
lifted_block: Block = None # type:ignore
|
|
4401
4430
|
try:
|
|
4402
4431
|
lifted_block = self._lift(
|
|
4403
4432
|
addr,
|
|
@@ -4411,10 +4440,13 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
4411
4440
|
except SimTranslationError:
|
|
4412
4441
|
nodecode = True
|
|
4413
4442
|
|
|
4414
|
-
irsb_string: bytes =
|
|
4443
|
+
irsb_string: bytes = b""
|
|
4444
|
+
lifted_block_bytes = lifted_block.bytes if lifted_block.bytes is not None else b""
|
|
4445
|
+
if lifted_block is not None:
|
|
4446
|
+
irsb_string = lifted_block_bytes[: irsb.size] if irsb is not None else lifted_block_bytes
|
|
4415
4447
|
|
|
4416
4448
|
# special logic during the complete scanning phase
|
|
4417
|
-
if cfg_job.job_type == CFGJobType.COMPLETE_SCANNING and is_arm_arch(self.project.arch):
|
|
4449
|
+
if cfg_job.job_type == CFGJobType.COMPLETE_SCANNING and is_arm_arch(self.project.arch) and irsb is not None:
|
|
4418
4450
|
# it's way too easy to incorrectly disassemble THUMB code contains 0x4f as ARM code svc?? #????
|
|
4419
4451
|
# if we get a single block that getting decoded to svc?? under ARM mode, we treat it as nodecode
|
|
4420
4452
|
if (
|
|
@@ -4452,7 +4484,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
4452
4484
|
except SimTranslationError:
|
|
4453
4485
|
nodecode = True
|
|
4454
4486
|
|
|
4455
|
-
irsb_string
|
|
4487
|
+
irsb_string = lifted_block.bytes[: irsb.size] if irsb is not None else lifted_block.bytes
|
|
4456
4488
|
|
|
4457
4489
|
if not (nodecode or irsb.size == 0 or irsb.jumpkind == "Ijk_NoDecode"):
|
|
4458
4490
|
# it is decodeable
|
|
@@ -4765,6 +4797,37 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
4765
4797
|
func = self.kb.functions.get_by_addr(func_addr)
|
|
4766
4798
|
func.info["get_pc"] = "ebx"
|
|
4767
4799
|
|
|
4800
|
+
# determine if the function uses ebp as a general purpose register or not
|
|
4801
|
+
if addr == func_addr or 0 < addr - func_addr <= 0x20:
|
|
4802
|
+
ebp_as_gpr = True
|
|
4803
|
+
cap = self._lift(addr, size=cfg_node.size).capstone
|
|
4804
|
+
for insn in cap.insns:
|
|
4805
|
+
if (
|
|
4806
|
+
insn.mnemonic == "mov"
|
|
4807
|
+
and len(insn.operands) == 2
|
|
4808
|
+
and insn.operands[0].type == capstone.x86.X86_OP_REG
|
|
4809
|
+
and insn.operands[1].type == capstone.x86.X86_OP_REG
|
|
4810
|
+
):
|
|
4811
|
+
if (
|
|
4812
|
+
insn.operands[0].reg == capstone.x86.X86_REG_EBP
|
|
4813
|
+
and insn.operands[1].reg == capstone.x86.X86_REG_ESP
|
|
4814
|
+
):
|
|
4815
|
+
ebp_as_gpr = False
|
|
4816
|
+
break
|
|
4817
|
+
elif (
|
|
4818
|
+
insn.mnemonic == "lea"
|
|
4819
|
+
and len(insn.operands) == 2
|
|
4820
|
+
and insn.operands[0].type == capstone.x86.X86_OP_REG
|
|
4821
|
+
and insn.operands[1].type == capstone.x86.X86_OP_MEM
|
|
4822
|
+
) and (
|
|
4823
|
+
insn.operands[0].reg == capstone.x86.X86_REG_EBP
|
|
4824
|
+
and insn.operands[1].mem.base == capstone.x86.X86_REG_ESP
|
|
4825
|
+
):
|
|
4826
|
+
ebp_as_gpr = False
|
|
4827
|
+
break
|
|
4828
|
+
func = self.kb.functions.get_by_addr(func_addr)
|
|
4829
|
+
func.info["bp_as_gpr"] = ebp_as_gpr
|
|
4830
|
+
|
|
4768
4831
|
elif self.project.arch.name == "AMD64":
|
|
4769
4832
|
# determine if the function uses rbp as a general purpose register or not
|
|
4770
4833
|
if addr == func_addr or 0 < addr - func_addr <= 0x20:
|
|
@@ -50,7 +50,7 @@ class CFGFastSoot(CFGFast):
|
|
|
50
50
|
self._initialize_cfg()
|
|
51
51
|
|
|
52
52
|
# Initialize variables used during analysis
|
|
53
|
-
self._pending_jobs = PendingJobs(self.
|
|
53
|
+
self._pending_jobs = PendingJobs(self.kb, self._deregister_analysis_job)
|
|
54
54
|
self._traced_addresses = set()
|
|
55
55
|
self._changed_functions = set()
|
|
56
56
|
self._updated_nonreturning_functions = set()
|
|
@@ -9,6 +9,7 @@ from .amd64_elf_got import AMD64ElfGotResolver
|
|
|
9
9
|
from .arm_elf_fast import ArmElfFastResolver
|
|
10
10
|
from .const_resolver import ConstantResolver
|
|
11
11
|
from .amd64_pe_iat import AMD64PeIatResolver
|
|
12
|
+
from .memload_resolver import MemoryLoadResolver
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
__all__ = (
|
|
@@ -17,6 +18,7 @@ __all__ = (
|
|
|
17
18
|
"ArmElfFastResolver",
|
|
18
19
|
"ConstantResolver",
|
|
19
20
|
"JumpTableResolver",
|
|
21
|
+
"MemoryLoadResolver",
|
|
20
22
|
"MipsElfFastResolver",
|
|
21
23
|
"MipsElfGotResolver",
|
|
22
24
|
"X86ElfPicPltResolver",
|
|
@@ -75,7 +75,31 @@ class ConstantResolver(IndirectJumpResolver):
|
|
|
75
75
|
|
|
76
76
|
vex_block = block.vex
|
|
77
77
|
if isinstance(vex_block.next, pyvex.expr.RdTmp):
|
|
78
|
-
|
|
78
|
+
tmp_stmt_idx, tmp_ins_addr = self._find_tmp_write_stmt_and_ins(vex_block, vex_block.next.tmp)
|
|
79
|
+
if tmp_stmt_idx is None or tmp_ins_addr is None:
|
|
80
|
+
return False, []
|
|
81
|
+
|
|
82
|
+
# first check: is it jumping to a target loaded from memory? if so, it should have been resolved by
|
|
83
|
+
# MemoryLoadResolver.
|
|
84
|
+
stmt = vex_block.statements[tmp_stmt_idx]
|
|
85
|
+
assert isinstance(stmt, pyvex.IRStmt.WrTmp)
|
|
86
|
+
if (
|
|
87
|
+
isinstance(stmt.data, pyvex.IRExpr.Load)
|
|
88
|
+
and isinstance(stmt.data.addr, pyvex.IRExpr.Const)
|
|
89
|
+
and stmt.data.result_size(vex_block.tyenv) == self.project.arch.bits
|
|
90
|
+
):
|
|
91
|
+
# well, if MemoryLoadResolver hasn't resolved it, we can try to resolve it here, or bail early because
|
|
92
|
+
# ConstantResolver won't help.
|
|
93
|
+
load_addr = stmt.data.addr.con.value
|
|
94
|
+
try:
|
|
95
|
+
value = self.project.loader.memory.unpack_word(load_addr, size=self.project.arch.bytes)
|
|
96
|
+
if isinstance(value, int) and self._is_target_valid(cfg, value):
|
|
97
|
+
return True, [value]
|
|
98
|
+
except KeyError:
|
|
99
|
+
pass
|
|
100
|
+
return False, []
|
|
101
|
+
|
|
102
|
+
# second check: what does the jump rely on? slice it back and see
|
|
79
103
|
b = Blade(
|
|
80
104
|
cfg.graph,
|
|
81
105
|
addr,
|
|
@@ -104,26 +128,25 @@ class ConstantResolver(IndirectJumpResolver):
|
|
|
104
128
|
block = self.project.factory.block(pred_addr, cross_insn_opt=True).vex
|
|
105
129
|
if stmt_idx != DEFAULT_STATEMENT:
|
|
106
130
|
stmt = block.statements[stmt_idx]
|
|
107
|
-
if
|
|
131
|
+
if (
|
|
132
|
+
isinstance(stmt, pyvex.IRStmt.WrTmp)
|
|
133
|
+
and isinstance(stmt.data, pyvex.IRExpr.Load)
|
|
134
|
+
and not isinstance(stmt.data.addr, pyvex.IRExpr.Const)
|
|
135
|
+
):
|
|
108
136
|
# loading from memory - unsupported
|
|
109
137
|
return False, []
|
|
110
138
|
break
|
|
111
139
|
|
|
112
140
|
_l.debug("ConstantResolver: Propagating for %r at %#x.", func, addr)
|
|
113
|
-
prop = self.project.analyses.
|
|
114
|
-
func
|
|
115
|
-
only_consts=True,
|
|
116
|
-
do_binops=True,
|
|
141
|
+
prop = self.project.analyses.FastConstantPropagation(
|
|
142
|
+
func,
|
|
117
143
|
vex_cross_insn_opt=False,
|
|
118
|
-
completed_funcs=cfg._completed_functions,
|
|
119
144
|
load_callback=PropagatorLoadCallback(self.project).propagator_load_callback,
|
|
120
|
-
cache_results=True,
|
|
121
|
-
key_prefix="cfg_intermediate",
|
|
122
145
|
)
|
|
123
146
|
|
|
124
147
|
replacements = prop.replacements
|
|
125
148
|
if replacements:
|
|
126
|
-
block_loc = CodeLocation(block.addr,
|
|
149
|
+
block_loc = CodeLocation(block.addr, tmp_stmt_idx, ins_addr=tmp_ins_addr)
|
|
127
150
|
tmp_var = vex_vars.VEXTmp(vex_block.next.tmp)
|
|
128
151
|
|
|
129
152
|
if exists_in_replacements(replacements, block_loc, tmp_var):
|
|
@@ -135,5 +158,18 @@ class ConstantResolver(IndirectJumpResolver):
|
|
|
135
158
|
and self._is_target_valid(cfg, resolved_tmp.args[0])
|
|
136
159
|
):
|
|
137
160
|
return True, [resolved_tmp.args[0]]
|
|
161
|
+
if isinstance(resolved_tmp, int) and self._is_target_valid(cfg, resolved_tmp):
|
|
162
|
+
return True, [resolved_tmp]
|
|
138
163
|
|
|
139
164
|
return False, []
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def _find_tmp_write_stmt_and_ins(vex_block, tmp: int) -> tuple[int | None, int | None]:
|
|
168
|
+
stmt_idx = None
|
|
169
|
+
for idx, stmt in enumerate(reversed(vex_block.statements)):
|
|
170
|
+
if isinstance(stmt, pyvex.IRStmt.IMark) and stmt_idx is not None:
|
|
171
|
+
ins_addr = stmt.addr + stmt.delta
|
|
172
|
+
return stmt_idx, ins_addr
|
|
173
|
+
if isinstance(stmt, pyvex.IRStmt.WrTmp) and stmt.tmp == tmp:
|
|
174
|
+
stmt_idx = len(vex_block.statements) - idx - 1
|
|
175
|
+
return None, None
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import cle
|
|
3
|
+
from angr.analyses.cfg.indirect_jump_resolvers import MemoryLoadResolver
|
|
3
4
|
|
|
4
5
|
from . import MipsElfFastResolver
|
|
5
6
|
from . import X86ElfPicPltResolver
|
|
@@ -19,6 +20,9 @@ DEFAULT_RESOLVERS = {
|
|
|
19
20
|
cle.PE: [
|
|
20
21
|
X86PeIatResolver,
|
|
21
22
|
],
|
|
23
|
+
cle.XBE: [
|
|
24
|
+
X86PeIatResolver,
|
|
25
|
+
],
|
|
22
26
|
},
|
|
23
27
|
"AMD64": {
|
|
24
28
|
cle.MetaELF: [
|
|
@@ -54,7 +58,7 @@ DEFAULT_RESOLVERS = {
|
|
|
54
58
|
ArmElfFastResolver,
|
|
55
59
|
]
|
|
56
60
|
},
|
|
57
|
-
"ALL": [JumpTableResolver, ConstantResolver],
|
|
61
|
+
"ALL": [MemoryLoadResolver, JumpTableResolver, ConstantResolver],
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
|
|
@@ -10,6 +10,7 @@ import functools
|
|
|
10
10
|
import pyvex
|
|
11
11
|
import claripy
|
|
12
12
|
from archinfo.arch_arm import is_arm_arch
|
|
13
|
+
from claripy.annotation import UninitializedAnnotation
|
|
13
14
|
|
|
14
15
|
from angr import sim_options as o
|
|
15
16
|
from angr import BP, BP_BEFORE, BP_AFTER
|
|
@@ -144,20 +145,22 @@ class ConstantValueManager:
|
|
|
144
145
|
|
|
145
146
|
__slots__ = (
|
|
146
147
|
"func",
|
|
148
|
+
"indirect_jump_addr",
|
|
147
149
|
"kb",
|
|
148
150
|
"mapping",
|
|
149
151
|
"project",
|
|
150
152
|
)
|
|
151
153
|
|
|
152
|
-
def __init__(self, project: Project, kb, func: Function):
|
|
154
|
+
def __init__(self, project: Project, kb, func: Function, ij_addr: int):
|
|
153
155
|
self.project = project
|
|
154
156
|
self.kb = kb
|
|
155
157
|
self.func = func
|
|
158
|
+
self.indirect_jump_addr = ij_addr
|
|
156
159
|
|
|
157
160
|
self.mapping: dict[Any, dict[Any, claripy.ast.Base]] | None = None
|
|
158
161
|
|
|
159
162
|
def reg_read_callback(self, state: SimState):
|
|
160
|
-
if
|
|
163
|
+
if self.mapping is None:
|
|
161
164
|
self._build_mapping()
|
|
162
165
|
assert self.mapping is not None
|
|
163
166
|
|
|
@@ -168,18 +171,54 @@ class ConstantValueManager:
|
|
|
168
171
|
reg_read_offset = reg_read_offset.args[0]
|
|
169
172
|
variable = VEXReg(reg_read_offset, state.inspect.reg_read_length)
|
|
170
173
|
if variable in self.mapping[codeloc]:
|
|
171
|
-
|
|
174
|
+
v = self.mapping[codeloc][variable]
|
|
175
|
+
if isinstance(v, int):
|
|
176
|
+
v = claripy.BVV(v, state.inspect.reg_read_length * state.arch.byte_width)
|
|
177
|
+
state.inspect.reg_read_expr = v
|
|
172
178
|
|
|
173
179
|
def _build_mapping(self):
|
|
174
180
|
# constant propagation
|
|
175
|
-
l.debug("JumpTable: Propagating for %r.", self.func)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
l.debug("JumpTable: Propagating for %r at %#x.", self.func, self.indirect_jump_addr)
|
|
182
|
+
|
|
183
|
+
# determine blocks to run FCP on
|
|
184
|
+
|
|
185
|
+
# - include at most three levels of successors from the entrypoint
|
|
186
|
+
startpoint = self.func.startpoint
|
|
187
|
+
blocks = set()
|
|
188
|
+
succs = [startpoint]
|
|
189
|
+
for _ in range(3):
|
|
190
|
+
new_succs = []
|
|
191
|
+
for node in succs:
|
|
192
|
+
if node in blocks:
|
|
193
|
+
continue
|
|
194
|
+
blocks.add(node)
|
|
195
|
+
if node.addr == self.indirect_jump_addr:
|
|
196
|
+
# stop at the indirect jump block
|
|
197
|
+
continue
|
|
198
|
+
new_succs += list(self.func.graph.successors(node))
|
|
199
|
+
succs = new_succs
|
|
200
|
+
if not succs:
|
|
201
|
+
break
|
|
202
|
+
|
|
203
|
+
# - include at most six levels of predecessors from the indirect jump block
|
|
204
|
+
ij_block = self.func.get_node(self.indirect_jump_addr)
|
|
205
|
+
preds = [ij_block]
|
|
206
|
+
for _ in range(6):
|
|
207
|
+
new_preds = []
|
|
208
|
+
for node in preds:
|
|
209
|
+
if node in blocks:
|
|
210
|
+
continue
|
|
211
|
+
blocks.add(node)
|
|
212
|
+
new_preds += list(self.func.graph.predecessors(node))
|
|
213
|
+
preds = new_preds
|
|
214
|
+
if not preds:
|
|
215
|
+
break
|
|
216
|
+
|
|
217
|
+
prop = self.project.analyses.FastConstantPropagation(
|
|
218
|
+
self.func,
|
|
219
|
+
blocks=blocks,
|
|
179
220
|
vex_cross_insn_opt=True,
|
|
180
221
|
load_callback=PropagatorLoadCallback(self.project).propagator_load_callback,
|
|
181
|
-
cache_results=True,
|
|
182
|
-
key_prefix="cfg_intermediate",
|
|
183
222
|
)
|
|
184
223
|
self.mapping = prop.replacements
|
|
185
224
|
|
|
@@ -466,6 +505,21 @@ class JumpTableProcessor(
|
|
|
466
505
|
return self._top(expr.result_size(self.tyenv))
|
|
467
506
|
return v
|
|
468
507
|
|
|
508
|
+
@binop_handler
|
|
509
|
+
def _handle_binop_And(self, expr):
|
|
510
|
+
arg0 = self._expr(expr.args[0])
|
|
511
|
+
if (
|
|
512
|
+
isinstance(arg0, claripy.ast.BV)
|
|
513
|
+
and self._is_registeroffset(arg0)
|
|
514
|
+
and isinstance(expr.args[1], pyvex.IRExpr.Const)
|
|
515
|
+
):
|
|
516
|
+
mask_value = expr.args[1].con.value
|
|
517
|
+
if mask_value in {1, 3, 7, 15, 31, 63, 127, 255}:
|
|
518
|
+
# 1cbbf108f44c8f4babde546d26425ca5340dccf878d306b90eb0fbec2f83ab51:0x40bd1b
|
|
519
|
+
self.state.is_jumptable = True
|
|
520
|
+
return arg0 & mask_value
|
|
521
|
+
return self._top(expr.result_size(self.tyenv))
|
|
522
|
+
|
|
469
523
|
@binop_handler
|
|
470
524
|
def _handle_binop_CmpLE(self, expr):
|
|
471
525
|
return self._handle_Comparison(*expr.args)
|
|
@@ -853,7 +907,7 @@ class JumpTableResolver(IndirectJumpResolver):
|
|
|
853
907
|
potential_call_table = jumpkind == "Ijk_Call" or self._sp_moved_up(block) or len(func.block_addrs_set) <= 5
|
|
854
908
|
# we only perform full-function propagation for jump tables or call tables in really small functions
|
|
855
909
|
if not potential_call_table or len(func.block_addrs_set) <= 5:
|
|
856
|
-
cv_manager = ConstantValueManager(self.project, cfg.kb, func)
|
|
910
|
+
cv_manager = ConstantValueManager(self.project, cfg.kb, func, addr)
|
|
857
911
|
else:
|
|
858
912
|
cv_manager = None
|
|
859
913
|
|
|
@@ -2131,7 +2185,7 @@ class JumpTableResolver(IndirectJumpResolver):
|
|
|
2131
2185
|
read_addr = state.inspect.mem_read_address
|
|
2132
2186
|
cond = state.inspect.mem_read_condition
|
|
2133
2187
|
|
|
2134
|
-
if not isinstance(read_addr, int) and read_addr.
|
|
2188
|
+
if not isinstance(read_addr, int) and read_addr.has_annotation_type(UninitializedAnnotation) and cond is None:
|
|
2135
2189
|
# if this AST has been initialized before, just use the cached addr
|
|
2136
2190
|
cached_addr = self._cached_memread_addrs.get(read_addr, None)
|
|
2137
2191
|
if cached_addr is not None:
|
|
@@ -2402,6 +2456,3 @@ class JumpTableResolver(IndirectJumpResolver):
|
|
|
2402
2456
|
if load_stmt_ids:
|
|
2403
2457
|
return [load_stmt_ids[-1]]
|
|
2404
2458
|
return []
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
from angr.analyses.propagator import PropagatorAnalysis
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
import pyvex
|
|
5
|
+
|
|
6
|
+
from .resolver import IndirectJumpResolver
|
|
7
|
+
|
|
8
|
+
_l = logging.getLogger(name=__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MemoryLoadResolver(IndirectJumpResolver):
|
|
12
|
+
"""
|
|
13
|
+
Resolve an indirect jump that looks like the following::
|
|
14
|
+
|
|
15
|
+
.text:
|
|
16
|
+
call off_3314A8
|
|
17
|
+
|
|
18
|
+
.data:
|
|
19
|
+
off_3314A8 dd offset sub_1E426F
|
|
20
|
+
|
|
21
|
+
This indirect jump resolver may not be the best solution for all cases (e.g., when the .data section can be
|
|
22
|
+
intentionally altered by the binary itself).
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, project):
|
|
26
|
+
super().__init__(project, timeless=True)
|
|
27
|
+
|
|
28
|
+
def filter(self, cfg, addr, func_addr, block, jumpkind):
|
|
29
|
+
return jumpkind in {"Ijk_Boring", "Ijk_Call"}
|
|
30
|
+
|
|
31
|
+
def resolve( # pylint:disable=unused-argument
|
|
32
|
+
self,
|
|
33
|
+
cfg,
|
|
34
|
+
addr: int,
|
|
35
|
+
func_addr: int,
|
|
36
|
+
block: pyvex.IRSB,
|
|
37
|
+
jumpkind: str,
|
|
38
|
+
func_graph_complete: bool = True,
|
|
39
|
+
**kwargs,
|
|
40
|
+
):
|
|
41
|
+
"""
|
|
42
|
+
:param cfg: CFG with specified function
|
|
43
|
+
:param addr: Address of indirect jump
|
|
44
|
+
:param func_addr: Address of function of indirect jump
|
|
45
|
+
:param block: Block of indirect jump (Block object)
|
|
46
|
+
:param jumpkind: VEX jumpkind (Ijk_Boring or Ijk_Call)
|
|
47
|
+
:return: Bool tuple with replacement address
|
|
48
|
+
"""
|
|
49
|
+
vex_block = block
|
|
50
|
+
if isinstance(vex_block.next, pyvex.expr.RdTmp):
|
|
51
|
+
tmp_stmt_idx, tmp_ins_addr = self._find_tmp_write_stmt_and_ins(vex_block, vex_block.next.tmp)
|
|
52
|
+
if tmp_stmt_idx is None or tmp_ins_addr is None:
|
|
53
|
+
return False, []
|
|
54
|
+
|
|
55
|
+
stmt = vex_block.statements[tmp_stmt_idx]
|
|
56
|
+
assert isinstance(stmt, pyvex.IRStmt.WrTmp)
|
|
57
|
+
if (
|
|
58
|
+
isinstance(stmt.data, pyvex.IRExpr.Load)
|
|
59
|
+
and isinstance(stmt.data.addr, pyvex.IRExpr.Const)
|
|
60
|
+
and stmt.data.result_size(vex_block.tyenv) == self.project.arch.bits
|
|
61
|
+
):
|
|
62
|
+
load_addr = stmt.data.addr.con.value
|
|
63
|
+
try:
|
|
64
|
+
value = self.project.loader.memory.unpack_word(load_addr, size=self.project.arch.bytes)
|
|
65
|
+
if isinstance(value, int) and self._is_target_valid(cfg, value):
|
|
66
|
+
return True, [value]
|
|
67
|
+
except KeyError:
|
|
68
|
+
return False, []
|
|
69
|
+
|
|
70
|
+
return False, []
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def _find_tmp_write_stmt_and_ins(vex_block, tmp: int) -> tuple[int | None, int | None]:
|
|
74
|
+
stmt_idx = None
|
|
75
|
+
for idx, stmt in enumerate(reversed(vex_block.statements)):
|
|
76
|
+
if isinstance(stmt, pyvex.IRStmt.IMark) and stmt_idx is not None:
|
|
77
|
+
ins_addr = stmt.addr + stmt.delta
|
|
78
|
+
return stmt_idx, ins_addr
|
|
79
|
+
if isinstance(stmt, pyvex.IRStmt.WrTmp) and stmt.tmp == tmp:
|
|
80
|
+
stmt_idx = len(vex_block.statements) - idx - 1
|
|
81
|
+
return None, None
|
|
@@ -10,7 +10,7 @@ class PropagatorLoadCallback:
|
|
|
10
10
|
def __init__(self, project):
|
|
11
11
|
self.project = project
|
|
12
12
|
|
|
13
|
-
def propagator_load_callback(self, addr, size) -> bool: # pylint:disable=unused-argument
|
|
13
|
+
def propagator_load_callback(self, addr: claripy.ast.BV | int, size: int) -> bool: # pylint:disable=unused-argument
|
|
14
14
|
# only allow loading if the address falls into a read-only region
|
|
15
15
|
if isinstance(addr, claripy.ast.BV) and addr.op == "BVV":
|
|
16
16
|
addr_v = addr.args[0]
|
|
@@ -19,9 +19,28 @@ class PropagatorLoadCallback:
|
|
|
19
19
|
else:
|
|
20
20
|
return False
|
|
21
21
|
section = self.project.loader.find_section_containing(addr_v)
|
|
22
|
+
segment = None
|
|
22
23
|
if section is not None:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
if section.is_readable and not section.is_writable:
|
|
25
|
+
# read-only section
|
|
26
|
+
return True
|
|
27
|
+
else:
|
|
28
|
+
segment = self.project.loader.find_segment_containing(addr_v)
|
|
29
|
+
if segment is not None and segment.is_readable and not segment.is_writable:
|
|
30
|
+
# read-only segment
|
|
31
|
+
return True
|
|
32
|
+
|
|
33
|
+
if (size == self.project.arch.bytes and (section is not None and section.is_readable)) or (
|
|
34
|
+
segment is not None and segment.is_readable
|
|
35
|
+
):
|
|
36
|
+
# memory is mapped and readable. does it contain a valid address?
|
|
37
|
+
try:
|
|
38
|
+
target_addr = self.project.loader.memory.unpack_word(
|
|
39
|
+
addr_v, size=size, endness=self.project.arch.memory_endness
|
|
40
|
+
)
|
|
41
|
+
if target_addr >= 0x1000 and self.project.loader.find_object_containing(target_addr) is not None:
|
|
42
|
+
return True
|
|
43
|
+
except KeyError:
|
|
44
|
+
return False
|
|
45
|
+
|
|
27
46
|
return False
|
|
@@ -3,7 +3,6 @@ import logging
|
|
|
3
3
|
|
|
4
4
|
from capstone.x86_const import X86_OP_MEM
|
|
5
5
|
|
|
6
|
-
from angr.simos import SimWindows
|
|
7
6
|
from .resolver import IndirectJumpResolver
|
|
8
7
|
|
|
9
8
|
l = logging.getLogger(name=__name__)
|
|
@@ -11,16 +10,14 @@ l = logging.getLogger(name=__name__)
|
|
|
11
10
|
|
|
12
11
|
class X86PeIatResolver(IndirectJumpResolver):
|
|
13
12
|
"""
|
|
14
|
-
A timeless indirect jump resolver for IAT in x86 PEs.
|
|
13
|
+
A timeless indirect jump resolver for IAT in x86 PEs and xbes.
|
|
15
14
|
"""
|
|
16
15
|
|
|
17
16
|
def __init__(self, project):
|
|
18
17
|
super().__init__(project, timeless=True)
|
|
19
18
|
|
|
20
19
|
def filter(self, cfg, addr, func_addr, block, jumpkind):
|
|
21
|
-
if not
|
|
22
|
-
return False
|
|
23
|
-
if jumpkind != "Ijk_Call":
|
|
20
|
+
if jumpkind not in {"Ijk_Call", "Ijk_Boring"}: # both call and jmp
|
|
24
21
|
return False
|
|
25
22
|
|
|
26
23
|
insns = self.project.factory.block(addr).capstone.insns
|