angr 9.2.78__py3-none-win_amd64.whl → 9.2.80__py3-none-win_amd64.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/__main__.py +59 -0
- angr/analyses/cfg/cfg_fast.py +140 -3
- angr/analyses/decompiler/ail_simplifier.py +8 -0
- angr/analyses/decompiler/block_simplifier.py +25 -5
- angr/analyses/decompiler/clinic.py +33 -19
- angr/analyses/decompiler/decompilation_options.py +9 -0
- angr/analyses/decompiler/optimization_passes/__init__.py +6 -0
- angr/analyses/decompiler/optimization_passes/engine_base.py +2 -2
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +2 -2
- angr/analyses/decompiler/optimization_passes/multi_simplifier.py +0 -12
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +8 -5
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +82 -12
- angr/analyses/decompiler/peephole_optimizations/__init__.py +11 -2
- angr/analyses/decompiler/peephole_optimizations/base.py +29 -2
- angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +14 -2
- angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +83 -0
- angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +103 -0
- angr/analyses/decompiler/region_simplifiers/ifelse.py +19 -10
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -2
- angr/analyses/decompiler/structured_codegen/c.py +20 -4
- angr/analyses/decompiler/utils.py +131 -2
- angr/analyses/propagator/engine_ail.py +3 -1
- angr/analyses/propagator/engine_vex.py +45 -0
- angr/analyses/propagator/propagator.py +24 -15
- angr/analyses/proximity_graph.py +30 -0
- angr/analyses/reaching_definitions/engine_ail.py +1 -1
- angr/analyses/stack_pointer_tracker.py +55 -0
- angr/callable.py +4 -4
- angr/engines/light/engine.py +30 -18
- angr/knowledge_plugins/__init__.py +1 -0
- angr/knowledge_plugins/custom_strings.py +40 -0
- angr/knowledge_plugins/functions/function.py +29 -0
- angr/knowledge_plugins/propagations/propagation_model.py +4 -0
- angr/knowledge_plugins/propagations/states.py +54 -4
- angr/lib/angr_native.dll +0 -0
- angr/procedures/definitions/__init__.py +2 -1
- angr/procedures/definitions/msvcr.py +0 -3
- angr/procedures/definitions/ntoskrnl.py +9 -0
- angr/procedures/win32_kernel/ExAllocatePool.py +12 -0
- angr/procedures/win32_kernel/ExFreePoolWithTag.py +7 -0
- angr/procedures/win32_kernel/__init__.py +3 -0
- angr/sim_type.py +3 -0
- angr/storage/memory_mixins/__init__.py +1 -1
- angr/utils/funcid.py +128 -0
- {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/METADATA +6 -6
- {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/RECORD +55 -45
- {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/WHEEL +1 -1
- angr-9.2.80.dist-info/entry_points.txt +2 -0
- tests/analyses/cfg/test_cfgfast.py +24 -0
- tests/analyses/decompiler/test_decompiler.py +128 -0
- tests/analyses/test_constantpropagation.py +34 -0
- {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/LICENSE +0 -0
- {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
from typing import Optional, Tuple, Any, Union, List
|
|
1
|
+
import pathlib
|
|
2
|
+
from typing import Optional, Tuple, Any, Union, List, Iterable
|
|
3
|
+
import logging
|
|
3
4
|
|
|
4
5
|
import networkx
|
|
5
6
|
|
|
6
7
|
import ailment
|
|
8
|
+
import angr
|
|
9
|
+
|
|
10
|
+
_l = logging.getLogger(__name__)
|
|
7
11
|
|
|
8
12
|
|
|
9
13
|
def remove_last_statement(node):
|
|
@@ -533,6 +537,131 @@ def peephole_optimize_stmts(block, stmt_opts):
|
|
|
533
537
|
return statements, any_update
|
|
534
538
|
|
|
535
539
|
|
|
540
|
+
def match_stmt_classes(all_stmts: List, idx: int, stmt_class_seq: Iterable[type]) -> bool:
|
|
541
|
+
for i, cls in enumerate(stmt_class_seq):
|
|
542
|
+
if idx + i >= len(all_stmts):
|
|
543
|
+
return False
|
|
544
|
+
if not isinstance(all_stmts[idx + i], cls):
|
|
545
|
+
return False
|
|
546
|
+
return True
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def peephole_optimize_multistmts(block, stmt_opts):
|
|
550
|
+
any_update = False
|
|
551
|
+
statements = block.statements[::]
|
|
552
|
+
|
|
553
|
+
# run multi-statement optimizers
|
|
554
|
+
stmt_idx = 0
|
|
555
|
+
while stmt_idx < len(statements):
|
|
556
|
+
redo = True
|
|
557
|
+
while redo and stmt_idx < len(statements):
|
|
558
|
+
redo = False
|
|
559
|
+
for opt in stmt_opts:
|
|
560
|
+
matched = False
|
|
561
|
+
stmt_seq_len = None
|
|
562
|
+
for stmt_class_seq in opt.stmt_classes:
|
|
563
|
+
if match_stmt_classes(statements, stmt_idx, stmt_class_seq):
|
|
564
|
+
stmt_seq_len = len(stmt_class_seq)
|
|
565
|
+
matched = True
|
|
566
|
+
break
|
|
567
|
+
|
|
568
|
+
if matched:
|
|
569
|
+
matched_stmts = statements[stmt_idx : stmt_idx + stmt_seq_len]
|
|
570
|
+
r = opt.optimize(matched_stmts, stmt_idx=stmt_idx, block=block)
|
|
571
|
+
if r is not None:
|
|
572
|
+
# update statements
|
|
573
|
+
statements = statements[:stmt_idx] + r + statements[stmt_idx + stmt_seq_len :]
|
|
574
|
+
any_update = True
|
|
575
|
+
redo = True
|
|
576
|
+
break
|
|
577
|
+
|
|
578
|
+
# move on to the next statement
|
|
579
|
+
stmt_idx += 1
|
|
580
|
+
|
|
581
|
+
return statements, any_update
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def decompile_functions(path, functions=None, structurer=None, catch_errors=False) -> Optional[str]:
|
|
585
|
+
"""
|
|
586
|
+
Decompile a binary into a set of functions.
|
|
587
|
+
|
|
588
|
+
:param path: The path to the binary to decompile.
|
|
589
|
+
:param functions: The functions to decompile. If None, all functions will be decompiled.
|
|
590
|
+
:param structurer: The structuring algorithms to use.
|
|
591
|
+
:param catch_errors: The structuring algorithms to use.
|
|
592
|
+
:return: The decompilation of all functions appended in order.
|
|
593
|
+
"""
|
|
594
|
+
# delayed imports to avoid circular imports
|
|
595
|
+
from angr.analyses.decompiler.decompilation_options import PARAM_TO_OPTION
|
|
596
|
+
|
|
597
|
+
structurer = structurer or "phoenix"
|
|
598
|
+
path = pathlib.Path(path).resolve().absolute()
|
|
599
|
+
proj = angr.Project(path, auto_load_libs=False)
|
|
600
|
+
cfg = proj.analyses.CFG(normalize=True, data_references=True)
|
|
601
|
+
proj.analyses.CompleteCallingConventions(recover_variables=True, analyze_callsites=True)
|
|
602
|
+
|
|
603
|
+
# collect all functions when None are provided
|
|
604
|
+
if functions is None:
|
|
605
|
+
functions = cfg.functions.values()
|
|
606
|
+
|
|
607
|
+
# normalize the functions that could be ints as names
|
|
608
|
+
normalized_functions = []
|
|
609
|
+
for func in functions:
|
|
610
|
+
try:
|
|
611
|
+
normalized_name = int(func, 0)
|
|
612
|
+
except ValueError:
|
|
613
|
+
normalized_name = func
|
|
614
|
+
normalized_functions.append(normalized_name)
|
|
615
|
+
functions = normalized_functions
|
|
616
|
+
|
|
617
|
+
# verify that all functions exist
|
|
618
|
+
for func in list(functions):
|
|
619
|
+
if func not in cfg.functions:
|
|
620
|
+
if catch_errors:
|
|
621
|
+
_l.warning("Function %s does not exist in the CFG.", str(func))
|
|
622
|
+
functions.remove(func)
|
|
623
|
+
else:
|
|
624
|
+
raise ValueError(f"Function {func} does not exist in the CFG.")
|
|
625
|
+
|
|
626
|
+
# decompile all functions
|
|
627
|
+
decompilation = ""
|
|
628
|
+
dec_options = [
|
|
629
|
+
(PARAM_TO_OPTION["structurer_cls"], structurer),
|
|
630
|
+
]
|
|
631
|
+
for func in functions:
|
|
632
|
+
f = cfg.functions[func]
|
|
633
|
+
if f is None or f.is_plt:
|
|
634
|
+
continue
|
|
635
|
+
|
|
636
|
+
exception_string = ""
|
|
637
|
+
if not catch_errors:
|
|
638
|
+
dec = proj.analyses.Decompiler(f, cfg=cfg, options=dec_options)
|
|
639
|
+
else:
|
|
640
|
+
try:
|
|
641
|
+
# TODO: add a timeout
|
|
642
|
+
dec = proj.analyses.Decompiler(f, cfg=cfg, options=dec_options)
|
|
643
|
+
except Exception as e:
|
|
644
|
+
exception_string = str(e).replace("\n", " ")
|
|
645
|
+
dec = None
|
|
646
|
+
|
|
647
|
+
# do sanity checks on decompilation, skip checks if we already errored
|
|
648
|
+
if not exception_string:
|
|
649
|
+
if dec is None or not dec.codegen or not dec.codegen.text:
|
|
650
|
+
exception_string = "Decompilation had no code output (failed in Dec)"
|
|
651
|
+
elif "{\n}" in dec.codegen.text:
|
|
652
|
+
exception_string = "Decompilation outputted an empty function (failed in structuring)"
|
|
653
|
+
elif structurer in ["dream", "combing"] and "goto" in dec.codegen.text:
|
|
654
|
+
exception_string = "Decompilation outputted a goto for a Gotoless algorithm (failed in structuring)"
|
|
655
|
+
|
|
656
|
+
if exception_string:
|
|
657
|
+
_l.critical("Failed to decompile %s because %s", str(func), exception_string)
|
|
658
|
+
decompilation += f"// [error: {func} | {exception_string}]\n"
|
|
659
|
+
else:
|
|
660
|
+
decompilation += dec.codegen.text + "\n"
|
|
661
|
+
|
|
662
|
+
return decompilation
|
|
663
|
+
|
|
664
|
+
|
|
536
665
|
# delayed import
|
|
537
666
|
from .structuring.structurer_nodes import (
|
|
538
667
|
MultiNode,
|
|
@@ -233,7 +233,9 @@ class SimEnginePropagatorAIL(
|
|
|
233
233
|
sp_expr_new = sp_expr.copy()
|
|
234
234
|
sp_expr_new.offset += self.arch.bytes
|
|
235
235
|
else:
|
|
236
|
-
sp_expr_new =
|
|
236
|
+
sp_expr_new = Expr.BinaryOp(
|
|
237
|
+
None, "Add", [sp_expr, Expr.Const(None, None, self.arch.bytes, sp_expr.bits)], False
|
|
238
|
+
)
|
|
237
239
|
sp_value_new = PropValue(
|
|
238
240
|
sp_value.value + self.arch.bytes,
|
|
239
241
|
offset_and_details={
|
|
@@ -5,6 +5,7 @@ import claripy
|
|
|
5
5
|
import pyvex
|
|
6
6
|
import archinfo
|
|
7
7
|
|
|
8
|
+
from angr.knowledge_plugins.propagations.states import RegisterAnnotation, RegisterComparisonAnnotation
|
|
8
9
|
from ...engines.light import SimEngineLightVEXMixin
|
|
9
10
|
from ...calling_conventions import DEFAULT_CC, default_cc, SimRegArg
|
|
10
11
|
from .values import Top, Bottom
|
|
@@ -234,6 +235,16 @@ class SimEnginePropagatorVEX(
|
|
|
234
235
|
self.tmps[stmt.result] = 1
|
|
235
236
|
self.state.add_replacement(self._codeloc(block_only=True), VEXTmp(stmt.result), self.tmps[stmt.result])
|
|
236
237
|
|
|
238
|
+
def _handle_CmpEQ(self, expr):
|
|
239
|
+
arg0, arg1 = self._expr(expr.args[0]), self._expr(expr.args[1])
|
|
240
|
+
if arg1 is not None and arg1.concrete and arg0 is not None and len(arg0.annotations) == 1:
|
|
241
|
+
anno = arg0.annotations[0]
|
|
242
|
+
if isinstance(anno, RegisterAnnotation):
|
|
243
|
+
cmp_anno = RegisterComparisonAnnotation(anno.offset, anno.size, "eq", arg1.concrete_value)
|
|
244
|
+
bits = expr.result_size(self.tyenv)
|
|
245
|
+
return self.state.top(bits).annotate(cmp_anno)
|
|
246
|
+
return super()._handle_CmpEQ(expr)
|
|
247
|
+
|
|
237
248
|
#
|
|
238
249
|
# Expression handlers
|
|
239
250
|
#
|
|
@@ -259,3 +270,37 @@ class SimEnginePropagatorVEX(
|
|
|
259
270
|
r = super()._handle_Binop(expr)
|
|
260
271
|
# print(expr.op, r)
|
|
261
272
|
return r
|
|
273
|
+
|
|
274
|
+
def _handle_Conversion(self, expr):
|
|
275
|
+
expr_ = self._expr(expr.args[0])
|
|
276
|
+
to_size = expr.result_size(self.tyenv)
|
|
277
|
+
if expr_ is None:
|
|
278
|
+
return self._top(to_size)
|
|
279
|
+
if self._is_top(expr_):
|
|
280
|
+
return self._top(to_size).annotate(*expr_.annotations)
|
|
281
|
+
|
|
282
|
+
if isinstance(expr_, claripy.ast.Base) and expr_.op == "BVV":
|
|
283
|
+
if expr_.size() > to_size:
|
|
284
|
+
# truncation
|
|
285
|
+
return expr_[to_size - 1 : 0]
|
|
286
|
+
elif expr_.size() < to_size:
|
|
287
|
+
# extension
|
|
288
|
+
return claripy.ZeroExt(to_size - expr_.size(), expr_)
|
|
289
|
+
else:
|
|
290
|
+
return expr_
|
|
291
|
+
|
|
292
|
+
return self._top(to_size)
|
|
293
|
+
|
|
294
|
+
def _handle_Exit(self, stmt):
|
|
295
|
+
guard = self._expr(stmt.guard)
|
|
296
|
+
if guard is not None and len(guard.annotations) == 1:
|
|
297
|
+
dst = self._expr(stmt.dst)
|
|
298
|
+
if dst is not None and dst.concrete:
|
|
299
|
+
anno = guard.annotations[0]
|
|
300
|
+
if isinstance(anno, RegisterComparisonAnnotation):
|
|
301
|
+
if anno.cmp_op == "eq":
|
|
302
|
+
v = (anno.offset, anno.size, anno.value)
|
|
303
|
+
if v not in self.state.block_initial_reg_values[self.block.addr, dst.concrete_value]:
|
|
304
|
+
self.state.block_initial_reg_values[self.block.addr, dst.concrete_value].append(v)
|
|
305
|
+
|
|
306
|
+
super()._handle_Exit(stmt)
|
|
@@ -289,6 +289,9 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
|
|
|
289
289
|
if self._base_state is not None:
|
|
290
290
|
self._base_state.options.add(sim_options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS)
|
|
291
291
|
self._base_state.options.add(sim_options.SYMBOL_FILL_UNCONSTRAINED_MEMORY)
|
|
292
|
+
|
|
293
|
+
self.model.input_states[block_key] = state.copy()
|
|
294
|
+
|
|
292
295
|
state = engine.process(
|
|
293
296
|
state,
|
|
294
297
|
block=block,
|
|
@@ -305,8 +308,7 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
|
|
|
305
308
|
|
|
306
309
|
self.model.node_iterations[block_key] += 1
|
|
307
310
|
self.model.states[block_key] = state
|
|
308
|
-
|
|
309
|
-
self.model.block_initial_reg_values.update(state.block_initial_reg_values)
|
|
311
|
+
self.model.block_initial_reg_values.update(state.block_initial_reg_values)
|
|
310
312
|
|
|
311
313
|
if self.model.replacements is None:
|
|
312
314
|
self.model.replacements = state._replacements
|
|
@@ -325,19 +327,26 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
|
|
|
325
327
|
def _process_input_state_for_successor(
|
|
326
328
|
self, node, successor, input_state: Union[PropagatorAILState, PropagatorVEXState]
|
|
327
329
|
):
|
|
328
|
-
if self._only_consts
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
330
|
+
if self._only_consts:
|
|
331
|
+
if isinstance(input_state, PropagatorAILState):
|
|
332
|
+
key = node.addr, successor.addr
|
|
333
|
+
if key in self.model.block_initial_reg_values:
|
|
334
|
+
input_state: PropagatorAILState = input_state.copy()
|
|
335
|
+
for reg_atom, reg_value in self.model.block_initial_reg_values[key]:
|
|
336
|
+
input_state.store_register(
|
|
337
|
+
reg_atom,
|
|
338
|
+
PropValue(
|
|
339
|
+
claripy.BVV(reg_value.value, reg_value.bits),
|
|
340
|
+
offset_and_details={0: Detail(reg_atom.size, reg_value, self._initial_codeloc)},
|
|
341
|
+
),
|
|
342
|
+
)
|
|
343
|
+
return input_state
|
|
344
|
+
elif isinstance(input_state, PropagatorVEXState):
|
|
345
|
+
key = node.addr, successor.addr
|
|
346
|
+
if key in self.model.block_initial_reg_values:
|
|
347
|
+
input_state: PropagatorVEXState = input_state.copy()
|
|
348
|
+
for reg_offset, reg_size, value in self.model.block_initial_reg_values[key]:
|
|
349
|
+
input_state.store_register(reg_offset, reg_size, claripy.BVV(value, reg_size * 8))
|
|
341
350
|
return input_state
|
|
342
351
|
|
|
343
352
|
def _intra_analysis(self):
|
angr/analyses/proximity_graph.py
CHANGED
|
@@ -180,6 +180,33 @@ class ProximityGraphAnalysis(Analysis):
|
|
|
180
180
|
|
|
181
181
|
self._work()
|
|
182
182
|
|
|
183
|
+
def _condense_blank_nodes(self, graph: networkx.DiGraph) -> None:
|
|
184
|
+
nodes = list(graph.nodes)
|
|
185
|
+
blank_nodes: List[BaseProxiNode] = []
|
|
186
|
+
|
|
187
|
+
for node in nodes:
|
|
188
|
+
if isinstance(node, BaseProxiNode) and node.type_ == ProxiNodeTypes.Empty:
|
|
189
|
+
blank_nodes.append(node)
|
|
190
|
+
else:
|
|
191
|
+
if blank_nodes:
|
|
192
|
+
self._merge_nodes(graph, blank_nodes)
|
|
193
|
+
blank_nodes = []
|
|
194
|
+
|
|
195
|
+
if blank_nodes:
|
|
196
|
+
self._merge_nodes(graph, blank_nodes)
|
|
197
|
+
|
|
198
|
+
def _merge_nodes(self, graph: networkx.DiGraph, nodes: List[BaseProxiNode]) -> None:
|
|
199
|
+
for node in nodes:
|
|
200
|
+
predecessors = set(graph.predecessors(node))
|
|
201
|
+
successors = set(graph.successors(node))
|
|
202
|
+
|
|
203
|
+
for pred in predecessors:
|
|
204
|
+
for succ in successors:
|
|
205
|
+
edge_data = graph.get_edge_data(pred, node) or {}
|
|
206
|
+
graph.add_edge(pred, succ, **edge_data)
|
|
207
|
+
|
|
208
|
+
graph.remove_node(node)
|
|
209
|
+
|
|
183
210
|
def _work(self):
|
|
184
211
|
self.graph = networkx.DiGraph()
|
|
185
212
|
|
|
@@ -210,6 +237,9 @@ class ProximityGraphAnalysis(Analysis):
|
|
|
210
237
|
self.graph.add_nodes_from(subgraph.nodes())
|
|
211
238
|
self.graph.add_edges_from(subgraph.edges())
|
|
212
239
|
|
|
240
|
+
# condense blank nodes after the graph has been constructed
|
|
241
|
+
self._condense_blank_nodes(self.graph)
|
|
242
|
+
|
|
213
243
|
def _endnode_connector(self, func: "Function", subgraph: networkx.DiGraph):
|
|
214
244
|
"""
|
|
215
245
|
Properly connect expanded function call's to proximity graph.
|
|
@@ -141,7 +141,7 @@ class SimEngineRDAIL(
|
|
|
141
141
|
return handler(expr)
|
|
142
142
|
else:
|
|
143
143
|
self.l.warning("Unsupported expression type %s.", type(expr).__name__)
|
|
144
|
-
return
|
|
144
|
+
return MultiValues(self.state.top(self.arch.bits))
|
|
145
145
|
|
|
146
146
|
def _ail_handle_Assignment(self, stmt):
|
|
147
147
|
"""
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# pylint:disable=abstract-method,ungrouped-imports
|
|
2
2
|
|
|
3
3
|
from typing import Set, List, Optional, TYPE_CHECKING
|
|
4
|
+
import re
|
|
4
5
|
import logging
|
|
5
6
|
|
|
6
7
|
import pyvex
|
|
@@ -286,6 +287,9 @@ class CouldNotResolveException(Exception):
|
|
|
286
287
|
"""
|
|
287
288
|
|
|
288
289
|
|
|
290
|
+
IROP_CONVERT_REGEX = re.compile(r"^Iop_(\d+)(U{0,1})to(\d+)(U{0,1})$")
|
|
291
|
+
|
|
292
|
+
|
|
289
293
|
class StackPointerTracker(Analysis, ForwardAnalysis):
|
|
290
294
|
"""
|
|
291
295
|
Track the offset of stack pointer at the end of each basic block of a function.
|
|
@@ -366,6 +370,42 @@ class StackPointerTracker(Analysis, ForwardAnalysis):
|
|
|
366
370
|
else:
|
|
367
371
|
return self.offset_before(instr_addrs[0], reg)
|
|
368
372
|
|
|
373
|
+
def _constant_for(self, addr, pre_or_post, reg):
|
|
374
|
+
try:
|
|
375
|
+
s = self._state_for(addr, pre_or_post)
|
|
376
|
+
if s is None:
|
|
377
|
+
return TOP
|
|
378
|
+
regval = dict(s.regs)[reg]
|
|
379
|
+
except KeyError:
|
|
380
|
+
return TOP
|
|
381
|
+
if type(regval) is Constant:
|
|
382
|
+
return regval.val
|
|
383
|
+
return TOP
|
|
384
|
+
|
|
385
|
+
def constant_after(self, addr, reg):
|
|
386
|
+
return self._constant_for(addr, "post", reg)
|
|
387
|
+
|
|
388
|
+
def constant_before(self, addr, reg):
|
|
389
|
+
return self._constant_for(addr, "pre", reg)
|
|
390
|
+
|
|
391
|
+
def constant_after_block(self, block_addr, reg):
|
|
392
|
+
if block_addr not in self._blocks:
|
|
393
|
+
return TOP
|
|
394
|
+
instr_addrs = self._blocks[block_addr].instruction_addrs
|
|
395
|
+
if len(instr_addrs) == 0:
|
|
396
|
+
return TOP
|
|
397
|
+
else:
|
|
398
|
+
return self.constant_after(instr_addrs[-1], reg)
|
|
399
|
+
|
|
400
|
+
def constant_before_block(self, block_addr, reg):
|
|
401
|
+
if block_addr not in self._blocks:
|
|
402
|
+
return TOP
|
|
403
|
+
instr_addrs = self._blocks[block_addr].instruction_addrs
|
|
404
|
+
if len(instr_addrs) == 0:
|
|
405
|
+
return TOP
|
|
406
|
+
else:
|
|
407
|
+
return self.constant_before(instr_addrs[0], reg)
|
|
408
|
+
|
|
369
409
|
@property
|
|
370
410
|
def inconsistent(self):
|
|
371
411
|
return any(self.inconsistent_for(r) for r in self.reg_offsets)
|
|
@@ -515,6 +555,21 @@ class StackPointerTracker(Analysis, ForwardAnalysis):
|
|
|
515
555
|
return Constant(expr.con.value)
|
|
516
556
|
elif type(expr) is pyvex.IRExpr.Get:
|
|
517
557
|
return state.get(expr.offset)
|
|
558
|
+
elif type(expr) is pyvex.IRExpr.Unop:
|
|
559
|
+
m = IROP_CONVERT_REGEX.match(expr.op)
|
|
560
|
+
if m is not None:
|
|
561
|
+
from_bits = int(m.group(1))
|
|
562
|
+
# from_unsigned = m.group(2) == "U"
|
|
563
|
+
to_bits = int(m.group(3))
|
|
564
|
+
# to_unsigned = m.group(4) == "U"
|
|
565
|
+
v = resolve_expr(expr.args[0])
|
|
566
|
+
if not isinstance(v, Constant):
|
|
567
|
+
return TOP
|
|
568
|
+
if from_bits > to_bits:
|
|
569
|
+
# truncation
|
|
570
|
+
mask = (1 << to_bits) - 1
|
|
571
|
+
return Constant(v.val & mask)
|
|
572
|
+
return v
|
|
518
573
|
elif self.track_mem and type(expr) is pyvex.IRExpr.Load:
|
|
519
574
|
return state.load(_resolve_expr(expr.addr))
|
|
520
575
|
raise CouldNotResolveException()
|
angr/callable.py
CHANGED
|
@@ -74,10 +74,10 @@ class Callable:
|
|
|
74
74
|
self.perform_call(*args, prototype=prototype)
|
|
75
75
|
if self.result_state is not None and prototype.returnty is not None:
|
|
76
76
|
loc = self._cc.return_val(prototype.returnty)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
if loc is not None:
|
|
78
|
+
val = loc.get_value(self.result_state, stack_base=self.result_state.regs.sp - self._cc.STACKARG_SP_DIFF)
|
|
79
|
+
return self.result_state.solver.simplify(val)
|
|
80
|
+
return None
|
|
81
81
|
|
|
82
82
|
def perform_call(self, *args, prototype=None):
|
|
83
83
|
prototype = SimCC.guess_prototype(args, prototype or self._func_ty).with_arch(self._project.arch)
|
angr/engines/light/engine.py
CHANGED
|
@@ -177,12 +177,12 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
|
|
|
177
177
|
func_addr = (
|
|
178
178
|
self.block.vex.next if isinstance(self.block.vex.next, int) else self._expr(self.block.vex.next)
|
|
179
179
|
)
|
|
180
|
-
if func_addr is not None:
|
|
181
|
-
getattr(self, handler)(func_addr)
|
|
182
|
-
else:
|
|
180
|
+
if func_addr is None and self.l is not None:
|
|
183
181
|
self.l.debug("Cannot determine the callee address at %#x.", self.block.addr)
|
|
182
|
+
getattr(self, handler)(func_addr)
|
|
184
183
|
else:
|
|
185
|
-
self.l
|
|
184
|
+
if self.l is not None:
|
|
185
|
+
self.l.warning("Function handler not implemented.")
|
|
186
186
|
|
|
187
187
|
#
|
|
188
188
|
# Statement handlers
|
|
@@ -193,7 +193,8 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
|
|
|
193
193
|
if hasattr(self, handler):
|
|
194
194
|
getattr(self, handler)(stmt)
|
|
195
195
|
elif type(stmt).__name__ not in ("IMark", "AbiHint"):
|
|
196
|
-
self.l
|
|
196
|
+
if self.l is not None:
|
|
197
|
+
self.l.error("Unsupported statement type %s.", type(stmt).__name__)
|
|
197
198
|
|
|
198
199
|
# synchronize with function _handle_WrTmpData()
|
|
199
200
|
def _handle_WrTmp(self, stmt):
|
|
@@ -210,7 +211,8 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
|
|
|
210
211
|
self.tmps[tmp] = data
|
|
211
212
|
|
|
212
213
|
def _handle_Dirty(self, stmt):
|
|
213
|
-
self.l
|
|
214
|
+
if self.l is not None:
|
|
215
|
+
self.l.error("Unimplemented Dirty node for current architecture.")
|
|
214
216
|
|
|
215
217
|
def _handle_Put(self, stmt):
|
|
216
218
|
raise NotImplementedError("Please implement the Put handler with your own logic.")
|
|
@@ -232,12 +234,13 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
|
|
|
232
234
|
handler = "_handle_%s" % type(expr).__name__
|
|
233
235
|
if hasattr(self, handler):
|
|
234
236
|
return getattr(self, handler)(expr)
|
|
235
|
-
|
|
237
|
+
elif self.l is not None:
|
|
236
238
|
self.l.error("Unsupported expression type %s.", type(expr).__name__)
|
|
237
239
|
return None
|
|
238
240
|
|
|
239
241
|
def _handle_Triop(self, expr: pyvex.IRExpr.Triop): # pylint: disable=useless-return
|
|
240
|
-
self.l
|
|
242
|
+
if self.l is not None:
|
|
243
|
+
self.l.error("Unsupported Triop %s.", expr.op)
|
|
241
244
|
return None
|
|
242
245
|
|
|
243
246
|
def _handle_RdTmp(self, expr):
|
|
@@ -295,7 +298,8 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
|
|
|
295
298
|
if handler is not None and hasattr(self, handler):
|
|
296
299
|
return getattr(self, handler)(expr)
|
|
297
300
|
else:
|
|
298
|
-
self.l
|
|
301
|
+
if self.l is not None:
|
|
302
|
+
self.l.error("Unsupported Unop %s.", expr.op)
|
|
299
303
|
return None
|
|
300
304
|
|
|
301
305
|
def _handle_Binop(self, expr: pyvex.IRExpr.Binop):
|
|
@@ -372,13 +376,14 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
|
|
|
372
376
|
return getattr(self, handler)(expr, vector_size, vector_count)
|
|
373
377
|
return getattr(self, handler)(expr)
|
|
374
378
|
else:
|
|
375
|
-
if once(expr.op):
|
|
379
|
+
if once(expr.op) and self.l is not None:
|
|
376
380
|
self.l.warning("Unsupported Binop %s.", expr.op)
|
|
377
381
|
|
|
378
382
|
return None
|
|
379
383
|
|
|
380
384
|
def _handle_CCall(self, expr): # pylint:disable=useless-return
|
|
381
|
-
self.l
|
|
385
|
+
if self.l is not None:
|
|
386
|
+
self.l.warning("Unsupported expression type CCall with callee %s.", str(expr.cee))
|
|
382
387
|
return None
|
|
383
388
|
|
|
384
389
|
#
|
|
@@ -479,7 +484,8 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
|
|
|
479
484
|
try:
|
|
480
485
|
return ~expr_0 # pylint:disable=invalid-unary-operand-type
|
|
481
486
|
except TypeError as e:
|
|
482
|
-
self.l
|
|
487
|
+
if self.l is not None:
|
|
488
|
+
self.l.exception(e)
|
|
483
489
|
return None
|
|
484
490
|
|
|
485
491
|
def _handle_Clz(self, expr):
|
|
@@ -602,7 +608,8 @@ class SimEngineLightVEXMixin(SimEngineLightMixin):
|
|
|
602
608
|
try:
|
|
603
609
|
return expr_0 ^ expr_1
|
|
604
610
|
except TypeError as e:
|
|
605
|
-
self.l
|
|
611
|
+
if self.l is not None:
|
|
612
|
+
self.l.warning(e)
|
|
606
613
|
return None
|
|
607
614
|
|
|
608
615
|
def _handle_Shl(self, expr):
|
|
@@ -834,7 +841,8 @@ class SimEngineLightAILMixin(SimEngineLightMixin):
|
|
|
834
841
|
|
|
835
842
|
if h is not None:
|
|
836
843
|
return h(expr)
|
|
837
|
-
self.l
|
|
844
|
+
if self.l is not None:
|
|
845
|
+
self.l.warning("Unsupported expression type %s.", type(expr).__name__)
|
|
838
846
|
return None
|
|
839
847
|
|
|
840
848
|
#
|
|
@@ -866,7 +874,8 @@ class SimEngineLightAILMixin(SimEngineLightMixin):
|
|
|
866
874
|
getattr(self, old_handler)(stmt)
|
|
867
875
|
return
|
|
868
876
|
|
|
869
|
-
self.l
|
|
877
|
+
if self.l is not None:
|
|
878
|
+
self.l.warning("Unsupported statement type %s.", type(stmt).__name__)
|
|
870
879
|
|
|
871
880
|
def _ail_handle_Label(self, stmt):
|
|
872
881
|
pass
|
|
@@ -933,7 +942,8 @@ class SimEngineLightAILMixin(SimEngineLightMixin):
|
|
|
933
942
|
try:
|
|
934
943
|
handler = getattr(self, handler_name)
|
|
935
944
|
except AttributeError:
|
|
936
|
-
self.l
|
|
945
|
+
if self.l is not None:
|
|
946
|
+
self.l.warning("Unsupported UnaryOp %s.", expr.op)
|
|
937
947
|
return None
|
|
938
948
|
|
|
939
949
|
return handler(expr)
|
|
@@ -943,7 +953,8 @@ class SimEngineLightAILMixin(SimEngineLightMixin):
|
|
|
943
953
|
try:
|
|
944
954
|
handler = getattr(self, handler_name)
|
|
945
955
|
except AttributeError:
|
|
946
|
-
self.l
|
|
956
|
+
if self.l is not None:
|
|
957
|
+
self.l.warning("Unsupported BinaryOp %s.", expr.op)
|
|
947
958
|
return None
|
|
948
959
|
|
|
949
960
|
return handler(expr)
|
|
@@ -953,7 +964,8 @@ class SimEngineLightAILMixin(SimEngineLightMixin):
|
|
|
953
964
|
try:
|
|
954
965
|
handler = getattr(self, handler_name)
|
|
955
966
|
except AttributeError:
|
|
956
|
-
self.l
|
|
967
|
+
if self.l is not None:
|
|
968
|
+
self.l.warning("Unsupported Ternary %s.", expr.op)
|
|
957
969
|
return None
|
|
958
970
|
|
|
959
971
|
return handler(expr)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from .plugin import KnowledgeBasePlugin
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CustomStrings(KnowledgeBasePlugin):
|
|
7
|
+
"""
|
|
8
|
+
Store new strings that are recovered during various analysis. Each string has a unique ID associated.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, kb):
|
|
12
|
+
super().__init__()
|
|
13
|
+
self._kb = kb
|
|
14
|
+
|
|
15
|
+
self.string_id = 0
|
|
16
|
+
self.strings: Dict[int, bytes] = {}
|
|
17
|
+
|
|
18
|
+
def allocate(self, s: bytes) -> int:
|
|
19
|
+
# de-duplication
|
|
20
|
+
# TODO: Use a reverse map if this becomes a bottle-neck in the future
|
|
21
|
+
for idx, string in self.strings.items():
|
|
22
|
+
if string == s:
|
|
23
|
+
return idx
|
|
24
|
+
|
|
25
|
+
string_id = self.string_id
|
|
26
|
+
self.strings[string_id] = s
|
|
27
|
+
self.string_id += 1
|
|
28
|
+
return string_id
|
|
29
|
+
|
|
30
|
+
def __getitem__(self, idx):
|
|
31
|
+
return self.strings[idx]
|
|
32
|
+
|
|
33
|
+
def copy(self):
|
|
34
|
+
o = CustomStrings(self._kb)
|
|
35
|
+
o.strings = self.strings.copy()
|
|
36
|
+
o.string_id = self.string_id
|
|
37
|
+
return o
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
KnowledgeBasePlugin.register_default("custom_strings", CustomStrings)
|