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.

Files changed (55) hide show
  1. angr/__init__.py +1 -1
  2. angr/__main__.py +59 -0
  3. angr/analyses/cfg/cfg_fast.py +140 -3
  4. angr/analyses/decompiler/ail_simplifier.py +8 -0
  5. angr/analyses/decompiler/block_simplifier.py +25 -5
  6. angr/analyses/decompiler/clinic.py +33 -19
  7. angr/analyses/decompiler/decompilation_options.py +9 -0
  8. angr/analyses/decompiler/optimization_passes/__init__.py +6 -0
  9. angr/analyses/decompiler/optimization_passes/engine_base.py +2 -2
  10. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +2 -2
  11. angr/analyses/decompiler/optimization_passes/multi_simplifier.py +0 -12
  12. angr/analyses/decompiler/optimization_passes/optimization_pass.py +8 -5
  13. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +82 -12
  14. angr/analyses/decompiler/peephole_optimizations/__init__.py +11 -2
  15. angr/analyses/decompiler/peephole_optimizations/base.py +29 -2
  16. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +1 -1
  17. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +14 -2
  18. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +83 -0
  19. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +103 -0
  20. angr/analyses/decompiler/region_simplifiers/ifelse.py +19 -10
  21. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -2
  22. angr/analyses/decompiler/structured_codegen/c.py +20 -4
  23. angr/analyses/decompiler/utils.py +131 -2
  24. angr/analyses/propagator/engine_ail.py +3 -1
  25. angr/analyses/propagator/engine_vex.py +45 -0
  26. angr/analyses/propagator/propagator.py +24 -15
  27. angr/analyses/proximity_graph.py +30 -0
  28. angr/analyses/reaching_definitions/engine_ail.py +1 -1
  29. angr/analyses/stack_pointer_tracker.py +55 -0
  30. angr/callable.py +4 -4
  31. angr/engines/light/engine.py +30 -18
  32. angr/knowledge_plugins/__init__.py +1 -0
  33. angr/knowledge_plugins/custom_strings.py +40 -0
  34. angr/knowledge_plugins/functions/function.py +29 -0
  35. angr/knowledge_plugins/propagations/propagation_model.py +4 -0
  36. angr/knowledge_plugins/propagations/states.py +54 -4
  37. angr/lib/angr_native.dll +0 -0
  38. angr/procedures/definitions/__init__.py +2 -1
  39. angr/procedures/definitions/msvcr.py +0 -3
  40. angr/procedures/definitions/ntoskrnl.py +9 -0
  41. angr/procedures/win32_kernel/ExAllocatePool.py +12 -0
  42. angr/procedures/win32_kernel/ExFreePoolWithTag.py +7 -0
  43. angr/procedures/win32_kernel/__init__.py +3 -0
  44. angr/sim_type.py +3 -0
  45. angr/storage/memory_mixins/__init__.py +1 -1
  46. angr/utils/funcid.py +128 -0
  47. {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/METADATA +6 -6
  48. {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/RECORD +55 -45
  49. {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/WHEEL +1 -1
  50. angr-9.2.80.dist-info/entry_points.txt +2 -0
  51. tests/analyses/cfg/test_cfgfast.py +24 -0
  52. tests/analyses/decompiler/test_decompiler.py +128 -0
  53. tests/analyses/test_constantpropagation.py +34 -0
  54. {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/LICENSE +0 -0
  55. {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,13 @@
1
- # pylint:disable=wrong-import-position
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 = sp_expr + self.arch.bytes
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
- if isinstance(state, PropagatorAILState):
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 and isinstance(input_state, PropagatorAILState):
329
- key = node.addr, successor.addr
330
- if key in self.model.block_initial_reg_values:
331
- input_state: PropagatorAILState = input_state.copy()
332
- for reg_atom, reg_value in self.model.block_initial_reg_values[key]:
333
- input_state.store_register(
334
- reg_atom,
335
- PropValue(
336
- claripy.BVV(reg_value.value, reg_value.bits),
337
- offset_and_details={0: Detail(reg_atom.size, reg_value, self._initial_codeloc)},
338
- ),
339
- )
340
- return input_state
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):
@@ -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 None
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
- val = loc.get_value(self.result_state, stack_base=self.result_state.regs.sp - self._cc.STACKARG_SP_DIFF)
78
- return self.result_state.solver.simplify(val)
79
- else:
80
- return None
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)
@@ -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.warning("Function handler not implemented.")
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.error("Unsupported statement type %s.", type(stmt).__name__)
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.error("Unimplemented Dirty node for current architecture.")
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
- else:
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.error("Unsupported Triop %s.", expr.op)
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.error("Unsupported Unop %s.", expr.op)
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.warning("Unsupported expression type CCall with callee %s.", str(expr.cee))
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.exception(e)
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.warning(e)
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.warning("Unsupported expression type %s.", type(expr).__name__)
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.warning("Unsupported statement type %s.", type(stmt).__name__)
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.warning("Unsupported UnaryOp %s.", expr.op)
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.warning("Unsupported BinaryOp %s.", expr.op)
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.warning("Unsupported Ternary %s.", expr.op)
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)
@@ -15,3 +15,4 @@ from .propagations import PropagationManager
15
15
  from .structured_code import StructuredCodeManager
16
16
  from .types import TypesStore
17
17
  from .callsite_prototypes import CallsitePrototypes
18
+ from .custom_strings import CustomStrings
@@ -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)