angr 9.2.164__cp310-abi3-macosx_11_0_arm64.whl → 9.2.165__cp310-abi3-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.

@@ -27,6 +27,42 @@ STEP_LIMIT_FIND = 500
27
27
  STEP_LIMIT_ANALYSIS = 5000
28
28
 
29
29
 
30
+ ALL_X64_XMM_REGS = {
31
+ capstone.x86.X86_REG_XMM0,
32
+ capstone.x86.X86_REG_XMM1,
33
+ capstone.x86.X86_REG_XMM2,
34
+ capstone.x86.X86_REG_XMM3,
35
+ capstone.x86.X86_REG_XMM4,
36
+ capstone.x86.X86_REG_XMM5,
37
+ capstone.x86.X86_REG_XMM6,
38
+ capstone.x86.X86_REG_XMM7,
39
+ capstone.x86.X86_REG_XMM8,
40
+ capstone.x86.X86_REG_XMM9,
41
+ capstone.x86.X86_REG_XMM10,
42
+ capstone.x86.X86_REG_XMM11,
43
+ capstone.x86.X86_REG_XMM12,
44
+ capstone.x86.X86_REG_XMM13,
45
+ capstone.x86.X86_REG_XMM14,
46
+ capstone.x86.X86_REG_XMM15,
47
+ capstone.x86.X86_REG_XMM16,
48
+ capstone.x86.X86_REG_XMM17,
49
+ capstone.x86.X86_REG_XMM18,
50
+ capstone.x86.X86_REG_XMM19,
51
+ capstone.x86.X86_REG_XMM20,
52
+ capstone.x86.X86_REG_XMM21,
53
+ capstone.x86.X86_REG_XMM22,
54
+ capstone.x86.X86_REG_XMM23,
55
+ capstone.x86.X86_REG_XMM24,
56
+ capstone.x86.X86_REG_XMM25,
57
+ capstone.x86.X86_REG_XMM26,
58
+ capstone.x86.X86_REG_XMM27,
59
+ capstone.x86.X86_REG_XMM28,
60
+ capstone.x86.X86_REG_XMM29,
61
+ capstone.x86.X86_REG_XMM30,
62
+ capstone.x86.X86_REG_XMM31,
63
+ }
64
+
65
+
30
66
  class StringDeobFuncDescriptor:
31
67
  """
32
68
  Describes a string deobfuscation function.
@@ -478,10 +514,12 @@ class StringObfuscationFinder(Analysis):
478
514
  actual_addrs = action.actual_addrs
479
515
  if action.type == "mem":
480
516
  if action.action == "read":
517
+ assert action.size is not None
481
518
  for a in actual_addrs:
482
519
  for size in range(action.size.ast // 8):
483
520
  all_global_reads.append(a + size)
484
521
  elif action.action == "write":
522
+ assert action.size is not None
485
523
  for a in actual_addrs:
486
524
  for size in range(action.size.ast // 8):
487
525
  all_global_writes.append(a + size)
@@ -598,16 +636,16 @@ class StringObfuscationFinder(Analysis):
598
636
  type3_functions = []
599
637
 
600
638
  for func in function_candidates:
601
- if not 8 <= len(func.block_addrs_set) < 14:
639
+ if not 1 <= len(func.block_addrs_set) < 14:
602
640
  continue
603
641
 
604
642
  # if it has a prototype recovered, it must have four arguments
605
- if func.prototype is not None and len(func.prototype.args) != 4:
643
+ if func.prototype is not None and len(func.prototype.args) not in {3, 4}:
606
644
  continue
607
645
 
608
646
  # the function must call some other functions
609
- if callgraph_digraph.out_degree[func.addr] == 0:
610
- continue
647
+ # if callgraph_digraph.out_degree[func.addr] == 0:
648
+ # continue
611
649
 
612
650
  # take a look at its call sites
613
651
  func_node = cfg.get_any_node(func.addr)
@@ -635,30 +673,34 @@ class StringObfuscationFinder(Analysis):
635
673
  continue
636
674
  if dec.codegen is None or not dec.codegen.text:
637
675
  continue
676
+
638
677
  if not self._like_type3_deobfuscation_function(dec.codegen.text):
639
678
  continue
640
679
 
641
680
  # examine the first 100 call sites and see if any of them returns a valid string
642
681
  valid = False
682
+ guessed_size = False
643
683
  for i in range(min(100, len(call_sites))):
644
684
  call_site_block = self.project.factory.block(call_sites[i].addr)
645
685
  if not self._is_block_setting_constants_to_stack(call_site_block):
646
686
  continue
647
687
 
648
688
  # simulate an execution to see if it really works
649
- data = self._type3_prepare_and_execute(
650
- func.addr, call_sites[i].addr, call_sites[i].function_address, cfg
689
+ data, guessed_size = self._type3_prepare_and_execute(
690
+ func.addr, call_sites[i].addr, call_sites[i].function_address, cfg # type:ignore
651
691
  )
652
692
  if data is None:
653
693
  continue
654
- if len(data) > 3 and all(chr(x) in string.printable for x in data):
655
- valid = True
656
- break
694
+ if len(data) > 3:
695
+ consecutive_printable_strs = self._consecutive_printable_substrings(data, min_length=4)
696
+ if consecutive_printable_strs:
697
+ valid = True
698
+ break
657
699
 
658
700
  if valid:
659
701
  desc = StringDeobFuncDescriptor()
660
702
  desc.string_output_arg_idx = 0
661
- desc.string_length_arg_idx = 1
703
+ desc.string_length_arg_idx = 1 if not guessed_size else None
662
704
  desc.string_null_terminating = False
663
705
  type3_functions.append((func.addr, desc))
664
706
 
@@ -687,11 +729,15 @@ class StringObfuscationFinder(Analysis):
687
729
  if cfg is None:
688
730
  raise AngrAnalysisError("StringObfuscationFinder needs a CFG for the analysis")
689
731
 
690
- call_sites = cfg.get_predecessors(cfg.get_any_node(func_addr))
732
+ cfg_node = cfg.get_any_node(func_addr)
733
+ if cfg_node is None:
734
+ raise AngrAnalysisError(f"Cannot find the CFG node for function {func_addr:#x}")
735
+ call_sites = cfg.get_predecessors(cfg_node)
691
736
  callinsn2content = {}
692
737
  for idx, call_site in enumerate(call_sites):
693
738
  _l.debug("Analyzing type 3 candidate call site %#x (%d/%d)...", call_site.addr, idx + 1, len(call_sites))
694
- data = self._type3_prepare_and_execute(func_addr, call_site.addr, call_site.function_address, cfg)
739
+ assert call_site.function_address is not None
740
+ data, _ = self._type3_prepare_and_execute(func_addr, call_site.addr, call_site.function_address, cfg)
695
741
  if data:
696
742
  callinsn2content[call_site.instruction_addrs[-1]] = data
697
743
  # print(hex(call_site.addr), data)
@@ -722,12 +768,14 @@ class StringObfuscationFinder(Analysis):
722
768
 
723
769
  @staticmethod
724
770
  def _like_type3_deobfuscation_function(code: str) -> bool:
725
- return bool(
726
- ("^" in code or ">>" in code or "<<" in code or "~" in code)
727
- and ("do" in code or "while" in code or "for" in code)
728
- )
729
-
730
- def _type3_prepare_and_execute(self, func_addr: int, call_site_addr: int, call_site_func_addr: int, cfg):
771
+ has_bitwise_ops = "^" in code or ">>" in code or "<<" in code or "~" in code
772
+ has_loops = "do" in code or "while" in code or "for" in code
773
+ has_many_bitwise_ops = code.count("^") + code.count(">>") + code.count("<<") + code.count("~") > 5
774
+ return has_bitwise_ops and (has_loops or has_many_bitwise_ops)
775
+
776
+ def _type3_prepare_and_execute(
777
+ self, func_addr: int, call_site_addr: int, call_site_func_addr: int, cfg
778
+ ) -> tuple[bytes | None, bool]:
731
779
  blocks_at_callsite = [call_site_addr]
732
780
 
733
781
  # backtrack from call site to include all previous consecutive blocks
@@ -773,6 +821,7 @@ class StringObfuscationFinder(Analysis):
773
821
  # setup sp and bp, just in case
774
822
  state.regs._sp = 0x7FFF0000
775
823
  bp_set = False
824
+ assert prop.model.input_states is not None
776
825
  prop_state = prop.model.input_states.get(call_site_addr, None)
777
826
  if prop_state is not None:
778
827
  for reg_offset, reg_width in reg_reads:
@@ -798,7 +847,7 @@ class StringObfuscationFinder(Analysis):
798
847
  else:
799
848
  simgr.step()
800
849
  if not simgr.active:
801
- return None
850
+ return None, False
802
851
 
803
852
  in_state = simgr.active[0]
804
853
 
@@ -821,33 +870,63 @@ class StringObfuscationFinder(Analysis):
821
870
  try:
822
871
  ret_value = callable_0()
823
872
  except (AngrCallableMultistateError, AngrCallableError):
824
- return None
873
+ return None, False
825
874
 
826
875
  out_state = callable_0.result_state
827
876
 
828
877
  # figure out what was written
878
+ assert out_state is not None
829
879
  ptr = out_state.memory.load(ret_value, size=self.project.arch.bytes, endness=self.project.arch.memory_endness)
880
+ if out_state.memory.load(ptr, size=4).concrete_value == 0:
881
+ # fall back to using the return value as the pointer
882
+ ptr = ret_value
883
+ if out_state.memory.load(ptr, size=4).concrete_value == 0:
884
+ # can't find a valid pointer
885
+ return None, False
886
+
830
887
  size = out_state.memory.load(ret_value + 8, size=4, endness=self.project.arch.memory_endness)
888
+ guessed_size = False
889
+ if size.symbolic or size.concrete_value == 0 or size.concrete_value >= 1024:
890
+ size = 64
891
+ guessed_size = True
831
892
  # TODO: Support lists with varied-length elements
832
893
  data = out_state.memory.load(ptr, size=size, endness="Iend_BE")
833
894
  if data.symbolic:
834
- return None
895
+ return None, False
835
896
 
836
- return out_state.solver.eval(data, cast_to=bytes)
897
+ return out_state.solver.eval(data, cast_to=bytes), guessed_size
837
898
 
838
899
  @staticmethod
839
900
  def _is_block_setting_constants_to_stack(block, threshold: int = 5) -> bool:
840
- insn_setting_consts = 0
901
+ insn_setting_const_bytes = 0
902
+ xmm_has_const = False
841
903
  for insn in block.capstone.insns:
842
- if (
843
- insn.mnemonic.startswith("mov")
844
- and len(insn.operands) == 2
845
- and insn.operands[0].type == capstone.x86.X86_OP_MEM
846
- and insn.operands[0].mem.base in {capstone.x86.X86_REG_RSP, capstone.x86.X86_REG_RBP}
847
- and insn.operands[1].type == capstone.x86.X86_OP_IMM
848
- ):
849
- insn_setting_consts += 1
850
- return insn_setting_consts >= threshold
904
+ if insn.mnemonic.startswith("mov") and len(insn.operands) == 2:
905
+ if (
906
+ insn.operands[0].type == capstone.x86.X86_OP_MEM
907
+ and insn.operands[0].mem.base in {capstone.x86.X86_REG_RSP, capstone.x86.X86_REG_RBP}
908
+ and insn.operands[1].type == capstone.x86.X86_OP_IMM
909
+ ):
910
+ # mov [rsp|rbp + offset], imm
911
+ insn_setting_const_bytes += 1 # FIXME: How to get the size of the mov in capstone?
912
+ if (
913
+ insn.operands[0].type == capstone.x86.X86_OP_REG
914
+ and insn.operands[0].reg in ALL_X64_XMM_REGS
915
+ and insn.operands[1].type == capstone.x86.X86_OP_MEM
916
+ and insn.operands[1].mem.base == capstone.x86.X86_REG_RIP
917
+ ):
918
+ xmm_has_const = True
919
+ if (
920
+ xmm_has_const
921
+ and insn.operands[0].type == capstone.x86.X86_OP_MEM
922
+ and insn.operands[0].mem.base in {capstone.x86.X86_REG_RSP, capstone.x86.X86_REG_RBP}
923
+ and insn.operands[1].type == capstone.x86.X86_OP_REG
924
+ and insn.operands[1].reg in ALL_X64_XMM_REGS
925
+ ):
926
+ # mov [rsp|rbp + offset], xmm0 - 31
927
+ insn_setting_const_bytes += 16
928
+
929
+ return insn_setting_const_bytes >= threshold
851
930
 
852
931
  @staticmethod
853
932
  def _is_string_reasonable(s: bytes) -> bool:
@@ -857,5 +936,24 @@ class StringObfuscationFinder(Analysis):
857
936
  s = s.replace(b"\x00", b"")
858
937
  return all(chr(ch) in string.printable for ch in s)
859
938
 
939
+ @staticmethod
940
+ def _consecutive_printable_substrings(s: bytes, min_length: int = 3) -> list[bytes]:
941
+ """
942
+ Find all consecutive printable substrings in a string.
943
+ """
944
+ substrings = []
945
+ current_substring = b""
946
+ for ch in s:
947
+ if chr(ch) in string.printable:
948
+ current_substring += bytes([ch])
949
+ else:
950
+ if current_substring:
951
+ if len(current_substring) >= min_length:
952
+ substrings.append(current_substring)
953
+ current_substring = b""
954
+ if current_substring:
955
+ substrings.append(current_substring)
956
+ return substrings
957
+
860
958
 
861
959
  AnalysesHub.register_default("StringObfuscationFinder", StringObfuscationFinder)
angr/engines/icicle.py CHANGED
@@ -123,7 +123,7 @@ class IcicleEngine(ConcreteEngine):
123
123
  if proj is None:
124
124
  raise ValueError("IcicleEngine requires a project to be set")
125
125
 
126
- emu = Icicle(icicle_arch, PROCESSORS_DIR, True)
126
+ emu = Icicle(icicle_arch, PROCESSORS_DIR, True, True)
127
127
 
128
128
  copied_registers = set()
129
129
 
@@ -174,6 +174,11 @@ class IcicleEngine(ConcreteEngine):
174
174
  initial_cpu_icount=emu.cpu_icount,
175
175
  )
176
176
 
177
+ # 3. Copy edge hitmap
178
+ edge_hitmap = state.history.last_edge_hitmap
179
+ if edge_hitmap is not None:
180
+ emu.edge_hitmap = edge_hitmap
181
+
177
182
  return (emu, translation_data)
178
183
 
179
184
  @staticmethod
@@ -221,9 +226,12 @@ class IcicleEngine(ConcreteEngine):
221
226
  # Skip the last block, because it will be added by Successors
222
227
  state.history.recent_bbl_addrs.extend([b[0] for b in emu.recent_blocks][:-1])
223
228
 
224
- # 4. Set history.recent_instruction_count
229
+ # 3.3. Set history.recent_instruction_count
225
230
  state.history.recent_instruction_count = emu.cpu_icount - translation_data.initial_cpu_icount
226
231
 
232
+ # 3.4. Set edge hitmap
233
+ state.history.edge_hitmap = emu.edge_hitmap
234
+
227
235
  return state
228
236
 
229
237
  @override
angr/rustylib.abi3.so CHANGED
Binary file
@@ -59,6 +59,9 @@ class SimStateHistory(SimStatePlugin):
59
59
  self.recent_syscall_count = 0 if clone is None else clone.recent_syscall_count
60
60
  self.recent_instruction_count = -1 if clone is None else clone.recent_instruction_count
61
61
 
62
+ # afl-style hitmap
63
+ self.edge_hitmap: bytes | None = None if clone is None else clone.edge_hitmap
64
+
62
65
  # satness stuff
63
66
  self._all_constraints = ()
64
67
  self._satisfiable = None
@@ -402,6 +405,19 @@ class SimStateHistory(SimStatePlugin):
402
405
  def stack_actions(self):
403
406
  return LambdaIterIter(self, operator.attrgetter("recent_stack_actions"))
404
407
 
408
+ @property
409
+ def last_edge_hitmap(self) -> bytes | None:
410
+ """
411
+ Returns the last edge hitmap in the history chain, or None if there is no edge hitmap.
412
+ """
413
+ history = self
414
+ while history is not None:
415
+ if history.edge_hitmap is not None:
416
+ return history.edge_hitmap
417
+ # Traverse to the previous state in the history chain
418
+ history = history.parent
419
+ return None
420
+
405
421
  #
406
422
  # Merging support
407
423
  #
angr/unicornlib.dylib CHANGED
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: angr
3
- Version: 9.2.164
3
+ Version: 9.2.165
4
4
  Summary: A multi-architecture binary analysis toolkit, with the ability to perform dynamic symbolic execution and various static analyses on binaries
5
5
  License: BSD-2-Clause
6
6
  Project-URL: Homepage, https://angr.io/
@@ -16,12 +16,12 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: cxxheaderparser
18
18
  Requires-Dist: GitPython
19
- Requires-Dist: archinfo==9.2.164
19
+ Requires-Dist: archinfo==9.2.165
20
20
  Requires-Dist: cachetools
21
21
  Requires-Dist: capstone==5.0.3
22
22
  Requires-Dist: cffi>=1.14.0
23
- Requires-Dist: claripy==9.2.164
24
- Requires-Dist: cle==9.2.164
23
+ Requires-Dist: claripy==9.2.165
24
+ Requires-Dist: cle==9.2.165
25
25
  Requires-Dist: mulpyplexer
26
26
  Requires-Dist: networkx!=2.8.1,>=2.0
27
27
  Requires-Dist: protobuf>=5.28.2
@@ -30,7 +30,7 @@ Requires-Dist: pycparser>=2.18
30
30
  Requires-Dist: pydemumble
31
31
  Requires-Dist: pyformlang
32
32
  Requires-Dist: pypcode<4.0,>=3.2.1
33
- Requires-Dist: pyvex==9.2.164
33
+ Requires-Dist: pyvex==9.2.165
34
34
  Requires-Dist: rich>=13.1.0
35
35
  Requires-Dist: sortedcontainers
36
36
  Requires-Dist: sympy