angr 9.2.175__cp310-abi3-macosx_11_0_arm64.whl → 9.2.177__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.

Files changed (51) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +12 -0
  3. angr/analyses/complete_calling_conventions.py +39 -26
  4. angr/analyses/decompiler/ail_simplifier.py +14 -12
  5. angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +5 -1
  6. angr/analyses/decompiler/clinic.py +54 -40
  7. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +3 -3
  8. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +2 -2
  9. angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -4
  10. angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +69 -12
  11. angr/analyses/decompiler/peephole_optimizations/{inlined_wstrcpy.py → inlined_wcscpy.py} +16 -8
  12. angr/analyses/decompiler/peephole_optimizations/inlined_wcscpy_consolidation.py +296 -0
  13. angr/analyses/decompiler/ssailification/rewriting_engine.py +14 -1
  14. angr/analyses/decompiler/structured_codegen/c.py +6 -5
  15. angr/analyses/decompiler/structuring/dream.py +2 -2
  16. angr/analyses/decompiler/structuring/phoenix.py +101 -23
  17. angr/analyses/decompiler/utils.py +10 -3
  18. angr/analyses/flirt/flirt.py +5 -4
  19. angr/analyses/stack_pointer_tracker.py +4 -3
  20. angr/analyses/typehoon/lifter.py +29 -18
  21. angr/analyses/typehoon/simple_solver.py +157 -50
  22. angr/analyses/typehoon/translator.py +34 -34
  23. angr/analyses/typehoon/typeconsts.py +33 -15
  24. angr/analyses/typehoon/typevars.py +9 -2
  25. angr/analyses/variable_recovery/engine_ail.py +43 -2
  26. angr/analyses/variable_recovery/engine_base.py +4 -1
  27. angr/analyses/variable_recovery/variable_recovery_fast.py +3 -1
  28. angr/emulator.py +2 -1
  29. angr/engines/hook.py +1 -1
  30. angr/engines/icicle.py +21 -5
  31. angr/engines/vex/claripy/ccall.py +3 -3
  32. angr/knowledge_plugins/functions/function.py +19 -2
  33. angr/procedures/definitions/__init__.py +9 -0
  34. angr/procedures/definitions/parse_win32json.py +11 -0
  35. angr/procedures/definitions/wdk/ntoskrnl.json +4 -0
  36. angr/procedures/posix/pthread.py +4 -4
  37. angr/procedures/stubs/format_parser.py +3 -3
  38. angr/rustylib.abi3.so +0 -0
  39. angr/sim_type.py +11 -6
  40. angr/simos/windows.py +1 -1
  41. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +1 -1
  42. angr/unicornlib.dylib +0 -0
  43. angr/utils/constants.py +1 -1
  44. angr/utils/strings.py +20 -0
  45. {angr-9.2.175.dist-info → angr-9.2.177.dist-info}/METADATA +5 -5
  46. {angr-9.2.175.dist-info → angr-9.2.177.dist-info}/RECORD +50 -49
  47. angr/analyses/decompiler/peephole_optimizations/inlined_wstrcpy_consolidation.py +0 -113
  48. {angr-9.2.175.dist-info → angr-9.2.177.dist-info}/WHEEL +0 -0
  49. {angr-9.2.175.dist-info → angr-9.2.177.dist-info}/entry_points.txt +0 -0
  50. {angr-9.2.175.dist-info → angr-9.2.177.dist-info}/licenses/LICENSE +0 -0
  51. {angr-9.2.175.dist-info → angr-9.2.177.dist-info}/top_level.txt +0 -0
@@ -85,7 +85,7 @@ class TypeTranslator:
85
85
  internal = sim_type.SimTypeBottom(label="void").with_arch(self.arch)
86
86
  else:
87
87
  internal = self._tc2simtype(tc.basetype)
88
- return sim_type.SimTypePointer(internal).with_arch(self.arch)
88
+ return sim_type.SimTypePointer(internal, label=tc.name).with_arch(self.arch)
89
89
 
90
90
  def _translate_Pointer32(self, tc):
91
91
  if isinstance(tc.basetype, typeconsts.BottomType):
@@ -93,11 +93,11 @@ class TypeTranslator:
93
93
  internal = sim_type.SimTypeBottom(label="void").with_arch(self.arch)
94
94
  else:
95
95
  internal = self._tc2simtype(tc.basetype)
96
- return sim_type.SimTypePointer(internal).with_arch(self.arch)
96
+ return sim_type.SimTypePointer(internal, label=tc.name).with_arch(self.arch)
97
97
 
98
98
  def _translate_Array(self, tc: typeconsts.Array) -> sim_type.SimTypeArray:
99
99
  elem_type = self._tc2simtype(tc.element)
100
- return sim_type.SimTypeArray(elem_type, length=tc.count).with_arch(self.arch)
100
+ return sim_type.SimTypeArray(elem_type, length=tc.count, label=tc.name).with_arch(self.arch)
101
101
 
102
102
  def _translate_Struct(self, tc: typeconsts.Struct):
103
103
  if tc in self.structs:
@@ -136,26 +136,26 @@ class TypeTranslator:
136
136
 
137
137
  return s
138
138
 
139
- def _translate_Int8(self, tc): # pylint:disable=unused-argument
140
- return sim_type.SimTypeChar(signed=False).with_arch(self.arch)
139
+ def _translate_Int8(self, tc):
140
+ return sim_type.SimTypeChar(signed=False, label=tc.name).with_arch(self.arch)
141
141
 
142
- def _translate_Int16(self, tc): # pylint:disable=unused-argument
143
- return sim_type.SimTypeShort(signed=False).with_arch(self.arch)
142
+ def _translate_Int16(self, tc):
143
+ return sim_type.SimTypeShort(signed=False, label=tc.name).with_arch(self.arch)
144
144
 
145
- def _translate_Int32(self, tc): # pylint:disable=unused-argument
146
- return sim_type.SimTypeInt(signed=False).with_arch(self.arch)
145
+ def _translate_Int32(self, tc):
146
+ return sim_type.SimTypeInt(signed=False, label=tc.name).with_arch(self.arch)
147
147
 
148
- def _translate_Int64(self, tc): # pylint:disable=unused-argument
149
- return sim_type.SimTypeLongLong(signed=False).with_arch(self.arch)
148
+ def _translate_Int64(self, tc):
149
+ return sim_type.SimTypeLongLong(signed=False, label=tc.name).with_arch(self.arch)
150
150
 
151
- def _translate_Int128(self, tc): # pylint:disable=unused-argument
152
- return sim_type.SimTypeInt128(signed=False).with_arch(self.arch)
151
+ def _translate_Int128(self, tc):
152
+ return sim_type.SimTypeInt128(signed=False, label=tc.name).with_arch(self.arch)
153
153
 
154
- def _translate_Int256(self, tc): # pylint:disable=unused-argument
155
- return sim_type.SimTypeInt256(signed=False).with_arch(self.arch)
154
+ def _translate_Int256(self, tc):
155
+ return sim_type.SimTypeInt256(signed=False, label=tc.name).with_arch(self.arch)
156
156
 
157
- def _translate_Int512(self, tc): # pylint:disable=unused-argument
158
- return sim_type.SimTypeInt512(signed=False).with_arch(self.arch)
157
+ def _translate_Int512(self, tc):
158
+ return sim_type.SimTypeInt512(signed=False, label=tc.name).with_arch(self.arch)
159
159
 
160
160
  def _translate_TypeVariableReference(self, tc):
161
161
  if tc.typevar in self.translated:
@@ -164,11 +164,11 @@ class TypeTranslator:
164
164
  self._has_nonexistent_ref = True
165
165
  return SimTypeTempRef(tc.typevar)
166
166
 
167
- def _translate_Float32(self, tc: typeconsts.Float32) -> sim_type.SimTypeFloat: # pylint:disable=unused-argument
168
- return sim_type.SimTypeFloat().with_arch(self.arch)
167
+ def _translate_Float32(self, tc: typeconsts.Float32) -> sim_type.SimTypeFloat:
168
+ return sim_type.SimTypeFloat(label=tc.name).with_arch(self.arch)
169
169
 
170
- def _translate_Float64(self, tc: typeconsts.Float64) -> sim_type.SimTypeDouble: # pylint:disable=unused-argument
171
- return sim_type.SimTypeDouble().with_arch(self.arch)
170
+ def _translate_Float64(self, tc: typeconsts.Float64) -> sim_type.SimTypeDouble:
171
+ return sim_type.SimTypeDouble(label=tc.name).with_arch(self.arch)
172
172
 
173
173
  #
174
174
  # Backpatching
@@ -197,25 +197,25 @@ class TypeTranslator:
197
197
  #
198
198
 
199
199
  def _translate_SimTypeInt128(self, st: sim_type.SimTypeChar) -> typeconsts.Int128:
200
- return typeconsts.Int128()
200
+ return typeconsts.Int128(name=st.label)
201
201
 
202
202
  def _translate_SimTypeInt256(self, st: sim_type.SimTypeChar) -> typeconsts.Int256:
203
- return typeconsts.Int256()
203
+ return typeconsts.Int256(name=st.label)
204
204
 
205
205
  def _translate_SimTypeInt512(self, st: sim_type.SimTypeChar) -> typeconsts.Int512:
206
- return typeconsts.Int512()
206
+ return typeconsts.Int512(name=st.label)
207
207
 
208
208
  def _translate_SimTypeInt(self, st: sim_type.SimTypeInt) -> typeconsts.Int32:
209
- return typeconsts.Int32()
209
+ return typeconsts.Int32(name=st.label)
210
210
 
211
211
  def _translate_SimTypeLong(self, st: sim_type.SimTypeLong) -> typeconsts.Int32:
212
- return typeconsts.Int32()
212
+ return typeconsts.Int32(name=st.label)
213
213
 
214
214
  def _translate_SimTypeLongLong(self, st: sim_type.SimTypeLongLong) -> typeconsts.Int64:
215
- return typeconsts.Int64()
215
+ return typeconsts.Int64(name=st.label)
216
216
 
217
217
  def _translate_SimTypeChar(self, st: sim_type.SimTypeChar) -> typeconsts.Int8:
218
- return typeconsts.Int8()
218
+ return typeconsts.Int8(name=st.label)
219
219
 
220
220
  def _translate_SimStruct(self, st: sim_type.SimStruct) -> typeconsts.Struct:
221
221
  fields = {}
@@ -224,25 +224,25 @@ class TypeTranslator:
224
224
  offset = offsets[name]
225
225
  fields[offset] = self._simtype2tc(ty)
226
226
 
227
- return typeconsts.Struct(fields=fields)
227
+ return typeconsts.Struct(fields=fields, name=st.label)
228
228
 
229
229
  def _translate_SimTypeArray(self, st: sim_type.SimTypeArray) -> typeconsts.Array:
230
230
  elem_type = self._simtype2tc(st.elem_type)
231
- return typeconsts.Array(elem_type, count=st.length)
231
+ return typeconsts.Array(elem_type, count=st.length, name=st.label)
232
232
 
233
233
  def _translate_SimTypePointer(self, st: sim_type.SimTypePointer) -> typeconsts.Pointer32 | typeconsts.Pointer64:
234
234
  base = self._simtype2tc(st.pts_to)
235
235
  if self.arch.bits == 32:
236
- return typeconsts.Pointer32(base)
236
+ return typeconsts.Pointer32(base, name=st.label)
237
237
  if self.arch.bits == 64:
238
- return typeconsts.Pointer64(base)
238
+ return typeconsts.Pointer64(base, name=st.label)
239
239
  raise TypeError(f"Unsupported pointer size {self.arch.bits}")
240
240
 
241
241
  def _translate_SimTypeFloat(self, st: sim_type.SimTypeFloat) -> typeconsts.Float32:
242
- return typeconsts.Float32()
242
+ return typeconsts.Float32(name=st.label)
243
243
 
244
244
  def _translate_SimTypeDouble(self, st: sim_type.SimTypeDouble) -> typeconsts.Float64:
245
- return typeconsts.Float64()
245
+ return typeconsts.Float64(name=st.label)
246
246
 
247
247
 
248
248
  TypeConstHandlers = {
@@ -5,6 +5,7 @@ All type constants used in type inference. They can be mapped, translated, or re
5
5
  from __future__ import annotations
6
6
 
7
7
  import functools
8
+ import itertools
8
9
 
9
10
 
10
11
  def memoize(f):
@@ -24,6 +25,9 @@ def memoize(f):
24
25
  class TypeConstant:
25
26
  SIZE = None
26
27
 
28
+ def __init__(self, name: str | None = None):
29
+ self.name = name
30
+
27
31
  def pp_str(self, mapping) -> str: # pylint:disable=unused-argument
28
32
  return repr(self)
29
33
 
@@ -115,7 +119,8 @@ class Int512(Int):
115
119
 
116
120
 
117
121
  class IntVar(Int):
118
- def __init__(self, size):
122
+ def __init__(self, size, name: str | None = None):
123
+ super().__init__(name)
119
124
  self._size = size
120
125
 
121
126
  @property
@@ -146,7 +151,8 @@ class Float64(Float):
146
151
 
147
152
 
148
153
  class Pointer(TypeConstant):
149
- def __init__(self, basetype: TypeConstant | None):
154
+ def __init__(self, basetype: TypeConstant | None, name: str | None = None):
155
+ super().__init__(name=name)
150
156
  self.basetype: TypeConstant | None = basetype
151
157
 
152
158
  def __eq__(self, other):
@@ -169,13 +175,15 @@ class Pointer32(Pointer, Int32):
169
175
  32-bit pointers.
170
176
  """
171
177
 
172
- def __init__(self, basetype=None):
173
- Pointer.__init__(self, basetype)
178
+ def __init__(self, basetype=None, name: str | None = None):
179
+ Pointer.__init__(self, basetype, name=name)
180
+ Int32.__init__(self, name=name)
174
181
 
175
182
  @memoize
176
183
  def __repr__(self, memo=None):
177
184
  bt = self.basetype.__repr__(memo=memo) if isinstance(self.basetype, TypeConstant) else repr(self.basetype)
178
- return f"ptr32({bt})"
185
+ name_str = f"{self.name}#" if self.name else ""
186
+ return f"{name_str}ptr32({bt})"
179
187
 
180
188
 
181
189
  class Pointer64(Pointer, Int64):
@@ -183,17 +191,20 @@ class Pointer64(Pointer, Int64):
183
191
  64-bit pointers.
184
192
  """
185
193
 
186
- def __init__(self, basetype=None):
187
- Pointer.__init__(self, basetype)
194
+ def __init__(self, basetype=None, name: str | None = None):
195
+ Pointer.__init__(self, basetype, name=name)
196
+ Int64.__init__(self, name=name)
188
197
 
189
198
  @memoize
190
199
  def __repr__(self, memo=None):
191
200
  bt = self.basetype.__repr__(memo=memo) if isinstance(self.basetype, TypeConstant) else repr(self.basetype)
192
- return f"ptr64({bt})"
201
+ name_str = f"{self.name}#" if self.name else ""
202
+ return f"{name_str}ptr64({bt})"
193
203
 
194
204
 
195
205
  class Array(TypeConstant):
196
- def __init__(self, element=None, count=None):
206
+ def __init__(self, element=None, count=None, name: str | None = None):
207
+ super().__init__(name=name)
197
208
  self.element: TypeConstant | None = element
198
209
  self.count: int | None = count
199
210
 
@@ -222,18 +233,22 @@ class Array(TypeConstant):
222
233
  return self._hash(set())
223
234
 
224
235
 
236
+ _STRUCT_ID = itertools.count()
237
+
238
+
225
239
  class Struct(TypeConstant):
226
- def __init__(self, fields=None, name=None, field_names=None, is_cppclass: bool = False):
240
+ def __init__(self, fields=None, name=None, field_names=None, is_cppclass: bool = False, idx: int = -1):
241
+ super().__init__(name=name)
227
242
  self.fields = {} if fields is None else fields # offset to type
228
- self.name = name
229
243
  self.field_names = field_names
230
244
  self.is_cppclass = is_cppclass
245
+ self.idx = idx if idx != -1 else next(_STRUCT_ID)
231
246
 
232
247
  def _hash(self, visited: set[int]):
233
248
  if id(self) in visited:
234
249
  return 0
235
250
  visited.add(id(self))
236
- return hash((type(self), self._hash_fields(visited)))
251
+ return hash((type(self), self.idx, self._hash_fields(visited)))
237
252
 
238
253
  def _hash_fields(self, visited: set[int]):
239
254
  keys = sorted(self.fields.keys())
@@ -252,6 +267,7 @@ class Struct(TypeConstant):
252
267
  @memoize
253
268
  def __repr__(self, memo=None):
254
269
  prefix = "CppClass" if self.is_cppclass else "struct"
270
+ prefix += f"#{self.idx}"
255
271
  if self.name:
256
272
  prefix = f"{prefix} {self.name}"
257
273
  return (
@@ -262,14 +278,15 @@ class Struct(TypeConstant):
262
278
  )
263
279
 
264
280
  def __eq__(self, other):
265
- return type(other) is type(self) and hash(self) == hash(other)
281
+ return type(other) is type(self) and self.idx == other.idx and hash(self) == hash(other)
266
282
 
267
283
  def __hash__(self):
268
284
  return self._hash(set())
269
285
 
270
286
 
271
287
  class Function(TypeConstant):
272
- def __init__(self, params: list, outputs: list):
288
+ def __init__(self, params: list, outputs: list, name: str | None = None):
289
+ super().__init__(name=name)
273
290
  self.params = params
274
291
  self.outputs = outputs
275
292
 
@@ -298,7 +315,8 @@ class Function(TypeConstant):
298
315
 
299
316
 
300
317
  class TypeVariableReference(TypeConstant):
301
- def __init__(self, typevar):
318
+ def __init__(self, typevar, name: str | None = None):
319
+ super().__init__(name=name)
302
320
  self.typevar = typevar
303
321
 
304
322
  def __repr__(self, memo=None):
@@ -559,20 +559,27 @@ class ReinterpretAs(BaseLabel):
559
559
 
560
560
 
561
561
  class HasField(BaseLabel):
562
+ """
563
+ Has a field of the given bit size * elem_count at the given offset.
564
+ """
565
+
562
566
  __slots__ = (
563
567
  "bits",
568
+ "elem_count",
564
569
  "offset",
565
570
  )
566
571
 
567
- def __init__(self, bits, offset):
572
+ def __init__(self, bits, offset, elem_count: int = 1):
568
573
  self.bits = bits
569
574
  self.offset = offset
575
+ self.elem_count = elem_count
570
576
  super().__init__()
571
577
 
572
578
  def __repr__(self):
573
579
  if self.bits == MAX_POINTSTO_BITS:
574
580
  return f"<MAX_POINTSTO_BITS>@{self.offset}"
575
- return f"<{self.bits} bits>@{self.offset}"
581
+ elem_str = str(self.elem_count) if self.elem_count != 1 else ""
582
+ return f"<{self.bits} bits>@{self.offset}{elem_str}"
576
583
 
577
584
 
578
585
  class IsArray(BaseLabel):
@@ -34,6 +34,7 @@ class SimEngineVRAIL(
34
34
  def __init__(
35
35
  self,
36
36
  *args,
37
+ type_lifter: TypeLifter,
37
38
  call_info=None,
38
39
  vvar_to_vvar: dict[int, int] | None,
39
40
  vvar_type_hints: dict[int, typeconsts.TypeConstant] | None = None,
@@ -44,6 +45,7 @@ class SimEngineVRAIL(
44
45
  self._reference_spoffset: bool = False
45
46
  self.call_info = call_info or {}
46
47
  self.vvar_to_vvar = vvar_to_vvar
48
+ self.type_lifter = type_lifter
47
49
 
48
50
  def _mapped_vvarid(self, vvar_id: int) -> int | None:
49
51
  if self.vvar_to_vvar is not None and vvar_id in self.vvar_to_vvar:
@@ -169,6 +171,45 @@ class SimEngineVRAIL(
169
171
  if isinstance(funcaddr_typevar, typevars.TypeVariable):
170
172
  load_typevar = self._create_access_typevar(funcaddr_typevar, False, self.arch.bytes, 0)
171
173
  self.state.add_type_constraint(typevars.Subtype(funcaddr_typevar, load_typevar))
174
+ elif isinstance(target, str):
175
+ # special handling for some intrinsics
176
+ match target:
177
+ case (
178
+ "InterlockedExchange8"
179
+ | "InterlockedExchange16"
180
+ | "InterlockedExchange"
181
+ | "InterlockedExchange64"
182
+ | "InterlockedCompareExchange16"
183
+ | "InterlockedCompareExchange"
184
+ | "InterlockedCompareExchange64"
185
+ | "InterlockedCompareExchange128"
186
+ | "InterlockedExchangeAdd"
187
+ | "InterlockedExchangeAdd64"
188
+ ):
189
+ arg_tv = (
190
+ args[0].typevar
191
+ if args[0].typevar is not None
192
+ else args[1].typevar if args[1].typevar is not None else None
193
+ )
194
+ if arg_tv is not None:
195
+ ret_ty = self._create_access_typevar(
196
+ arg_tv, False, args[0].data.size() // self.arch.byte_width, 0
197
+ )
198
+ return RichR(self.state.top(ret_expr_bits), typevar=ret_ty)
199
+ case (
200
+ "InterlockedIncrement"
201
+ | "InterlockedIncrement16"
202
+ | "InterlockedIncrement64"
203
+ | "InterlockedDecrement"
204
+ | "InterlockedDecrement16"
205
+ | "InterlockedDecrement64"
206
+ ):
207
+ arg_tv = args[0].typevar if args[0].typevar is not None else None
208
+ if arg_tv is not None:
209
+ ret_ty = self._create_access_typevar(
210
+ arg_tv, False, args[0].data.size() // self.arch.byte_width, 0
211
+ )
212
+ return RichR(self.state.top(ret_expr_bits), typevar=ret_ty)
172
213
 
173
214
  # discover the prototype
174
215
  prototype: SimTypeFunction | None = None
@@ -195,7 +236,7 @@ class SimEngineVRAIL(
195
236
  arg_type = (
196
237
  dereference_simtype_by_lib(arg_type, prototype_libname) if prototype_libname else arg_type
197
238
  )
198
- arg_ty = TypeLifter(self.arch.bits).lift(arg_type)
239
+ arg_ty = self.type_lifter.lift(arg_type)
199
240
  type_constraint = typevars.Subtype(arg.typevar, arg_ty)
200
241
  self.state.add_type_constraint(type_constraint)
201
242
 
@@ -282,7 +323,7 @@ class SimEngineVRAIL(
282
323
  arg_type = (
283
324
  dereference_simtype_by_lib(arg_type, prototype_libname) if prototype_libname else arg_type
284
325
  )
285
- arg_ty = TypeLifter(self.arch.bits).lift(arg_type)
326
+ arg_ty = self.type_lifter.lift(arg_type)
286
327
  if arg.typevar is not None and isinstance(
287
328
  arg_ty, (typeconsts.TypeConstant, typevars.TypeVariable, typevars.DerivedTypeVariable)
288
329
  ):
@@ -277,7 +277,10 @@ class SimEngineVRBase(
277
277
  variable, _ = existing_vars[0]
278
278
 
279
279
  if not self.state.typevars.has_type_variable_for(variable):
280
- variable_typevar = typevars.TypeVariable()
280
+ if isinstance(variable, SimStackVariable) and variable.offset in self.state.stack_offset_typevars:
281
+ variable_typevar = self.state.stack_offset_typevars[variable.offset]
282
+ else:
283
+ variable_typevar = typevars.TypeVariable()
281
284
  self.state.typevars.add_type_variable(variable, variable_typevar)
282
285
  # we do not add any type constraint here because we are not sure if the given memory address will ever be
283
286
  # accessed or not
@@ -284,6 +284,7 @@ class VariableRecoveryFast(ForwardAnalysis, VariableRecoveryBase): # pylint:dis
284
284
  self._unify_variables = unify_variables
285
285
 
286
286
  # handle type hints
287
+ self.type_lifter = TypeLifter(self.project.arch.bits)
287
288
  self.vvar_type_hints = {}
288
289
  if type_hints:
289
290
  self._parse_type_hints(type_hints)
@@ -294,6 +295,7 @@ class VariableRecoveryFast(ForwardAnalysis, VariableRecoveryBase): # pylint:dis
294
295
  call_info=call_info,
295
296
  vvar_to_vvar=self.vvar_to_vvar,
296
297
  vvar_type_hints=self.vvar_type_hints,
298
+ type_lifter=self.type_lifter,
297
299
  )
298
300
  self._vex_engine: SimEngineVRVEX = SimEngineVRVEX(self.project, self.kb, call_info=call_info)
299
301
 
@@ -654,7 +656,7 @@ class VariableRecoveryFast(ForwardAnalysis, VariableRecoveryBase): # pylint:dis
654
656
  if ty is None:
655
657
  return None
656
658
  ty = ty.with_arch(self.project.arch)
657
- lifted = TypeLifter(self.project.arch.bits).lift(ty)
659
+ lifted = self.type_lifter.lift(ty)
658
660
  return None if isinstance(lifted, (BottomType, TopType)) else lifted
659
661
 
660
662
 
angr/emulator.py CHANGED
@@ -95,7 +95,8 @@ class Emulator:
95
95
  num_inst_executed: int = 0
96
96
  while self._state.history.jumpkind != "Ijk_Exit":
97
97
  # Check if there is a breakpoint at the current address
98
- if completed_engine_execs > 0 and self._state.addr in self._engine.get_breakpoints():
98
+ addr_with_lower_bit_cleared = self._state.addr & ~1
99
+ if completed_engine_execs > 0 and addr_with_lower_bit_cleared in self._engine.get_breakpoints():
99
100
  return EmulatorStopReason.BREAKPOINT
100
101
 
101
102
  # Check if we've already executed the requested number of instructions
angr/engines/hook.py CHANGED
@@ -54,7 +54,7 @@ class HooksMixin(SuccessorsEngine, ProcedureMixin):
54
54
  if procedure is None:
55
55
  procedure = self._lookup_hook(state, procedure)
56
56
  if procedure is None:
57
- return super().process_successors(successors, procedure=procedure, **kwargs)
57
+ return super().process_successors(successors, **kwargs)
58
58
 
59
59
  if isinstance(procedure.addr, SootAddressDescriptor):
60
60
  l.debug("Running %s (originally at %r)", repr(procedure), procedure.addr)
angr/engines/icicle.py CHANGED
@@ -8,7 +8,7 @@ from dataclasses import dataclass
8
8
  from typing_extensions import override
9
9
 
10
10
  import pypcode
11
- from archinfo import Arch, Endness
11
+ from archinfo import Arch, ArchPcode, Endness, ArchARMCortexM
12
12
 
13
13
  from angr.engines.concrete import ConcreteEngine, HeavyConcreteState
14
14
  from angr.engines.failure import SimEngineFailure
@@ -72,6 +72,8 @@ class IcicleEngine(ConcreteEngine):
72
72
  accurate, just a set of heuristics to get the right architecture. When
73
73
  adding a new architecture, this function may need to be updated.
74
74
  """
75
+ if isinstance(arch, ArchARMCortexM) or (isinstance(arch, ArchPcode) and arch.pcode_arch == "ARM:LE:32:Cortex"):
76
+ return "armv7m"
75
77
  if arch.linux_name == "arm":
76
78
  return "armv7a" if arch.memory_endness == Endness.LE else "armeb"
77
79
  return arch.linux_name
@@ -84,11 +86,20 @@ class IcicleEngine(ConcreteEngine):
84
86
  return icicle_arch.startswith(("arm", "thumb"))
85
87
 
86
88
  @staticmethod
87
- def __is_thumb(icicle_arch: str, addr: int) -> bool:
89
+ def __is_cortex_m(angr_arch: Arch, icicle_arch: str) -> bool:
90
+ """
91
+ Check if the architecture is cortex-m based on the address.
92
+ """
93
+ return isinstance(angr_arch, ArchARMCortexM) or icicle_arch == "armv7m"
94
+
95
+ @staticmethod
96
+ def __is_thumb(angr_arch: Arch, icicle_arch: str, addr: int) -> bool:
88
97
  """
89
98
  Check if the architecture is thumb based on the address.
90
99
  """
91
- return IcicleEngine.__is_arm(icicle_arch) and addr & 1 == 1
100
+ return IcicleEngine.__is_cortex_m(angr_arch, icicle_arch) or (
101
+ IcicleEngine.__is_arm(icicle_arch) and addr & 1 == 1
102
+ )
92
103
 
93
104
  @staticmethod
94
105
  def __get_pages(state: HeavyConcreteState) -> set[int]:
@@ -132,13 +143,16 @@ class IcicleEngine(ConcreteEngine):
132
143
  for register in state.arch.register_list:
133
144
  register = register.vex_name.lower() if register.vex_name is not None else register.name
134
145
  try:
135
- emu.reg_write(register, state.solver.eval(state.registers.load(register), cast_to=int))
146
+ emu.reg_write(
147
+ register,
148
+ state.solver.eval(state.registers.load(register), cast_to=int),
149
+ )
136
150
  copied_registers.add(register)
137
151
  except KeyError:
138
152
  log.debug("Register %s not found in icicle", register)
139
153
 
140
154
  # Unset the thumb bit if necessary
141
- if IcicleEngine.__is_thumb(icicle_arch, state.addr):
155
+ if IcicleEngine.__is_thumb(state.arch, icicle_arch, state.addr):
142
156
  emu.pc = state.addr & ~1
143
157
  emu.isa_mode = 1
144
158
  elif "arm" in icicle_arch: # Hack to work around us calling it r15t
@@ -242,11 +256,13 @@ class IcicleEngine(ConcreteEngine):
242
256
  @override
243
257
  def add_breakpoint(self, addr: int) -> None:
244
258
  """Add a breakpoint at the given address."""
259
+ addr = addr & ~1 # Clear thumb bit if set
245
260
  self.breakpoints.add(addr)
246
261
 
247
262
  @override
248
263
  def remove_breakpoint(self, addr: int) -> None:
249
264
  """Remove a breakpoint at the given address, if present."""
265
+ addr = addr & ~1 # Clear thumb bit if set
250
266
  self.breakpoints.discard(addr)
251
267
 
252
268
  @override
@@ -343,7 +343,7 @@ def pc_make_rdata_if_necessary(nbits, cf, pf, af, zf, sf, of, platform=None):
343
343
 
344
344
 
345
345
  def pc_actions_ADD(state, nbits, arg_l, arg_r, cc_ndep, platform=None):
346
- data_mask, sign_mask = pc_preamble(nbits)
346
+ data_mask, _sign_mask = pc_preamble(nbits)
347
347
  res = arg_l + arg_r
348
348
 
349
349
  cf = claripy.If(claripy.ULT(res, arg_l), claripy.BVV(1, 1), claripy.BVV(0, 1))
@@ -701,7 +701,7 @@ def pc_calculate_rdata_all(state, cc_op, cc_dep1, cc_dep2, cc_ndep, platform=Non
701
701
  def pc_calculate_condition(state, cond, cc_op, cc_dep1, cc_dep2, cc_ndep, platform=None):
702
702
  rdata_all = pc_calculate_rdata_all_WRK(state, cc_op, cc_dep1, cc_dep2, cc_ndep, platform=platform)
703
703
  if isinstance(rdata_all, tuple):
704
- cf, pf, af, zf, sf, of = rdata_all
704
+ cf, pf, _af, zf, sf, of = rdata_all
705
705
  v = op_concretize(cond)
706
706
 
707
707
  inv = v & 1
@@ -993,7 +993,7 @@ def pc_calculate_rdata_c(state, cc_op, cc_dep1, cc_dep2, cc_ndep, platform=None)
993
993
  rdata_all = pc_calculate_rdata_all_WRK(state, cc_op, cc_dep1, cc_dep2, cc_ndep, platform=platform)
994
994
 
995
995
  if isinstance(rdata_all, tuple):
996
- cf, pf, af, zf, sf, of = rdata_all
996
+ cf, _pf, _af, _zf, _sf, _of = rdata_all
997
997
  return claripy.Concat(claripy.BVV(0, data[platform]["size"] - 1), cf & 1)
998
998
  return claripy.LShR(rdata_all, data[platform]["CondBitOffsets"]["G_CC_SHIFT_C"]) & 1
999
999
 
@@ -759,8 +759,7 @@ class Function(Serializable):
759
759
  if hooker:
760
760
  if hasattr(hooker, "DYNAMIC_RET") and hooker.DYNAMIC_RET:
761
761
  return True
762
- if hasattr(hooker, "NO_RET"):
763
- return not hooker.NO_RET
762
+ return hooker.returns
764
763
 
765
764
  # Cannot determine
766
765
  return None
@@ -1579,6 +1578,7 @@ class Function(Serializable):
1579
1578
  return False
1580
1579
  self.prototype = proto.with_arch(self.project.arch)
1581
1580
  self.prototype_libname = library.name
1581
+ self.returning = library.is_returning(name)
1582
1582
 
1583
1583
  # update self.calling_convention if necessary
1584
1584
  if self.calling_convention is None:
@@ -1750,6 +1750,23 @@ class Function(Serializable):
1750
1750
  _find_called(self.addr)
1751
1751
  return {self._function_manager.function(a) for a in called}
1752
1752
 
1753
+ def holes(self, min_size: int = 8) -> int:
1754
+ """
1755
+ Find the number of non-consecutive areas in the function that are at least `min_size` bytes large.
1756
+ """
1757
+
1758
+ block_addrs = sorted(self._local_block_addrs)
1759
+ if not block_addrs:
1760
+ return 0
1761
+ holes = 0
1762
+ for i, addr in enumerate(block_addrs):
1763
+ if i == len(block_addrs) - 1:
1764
+ break
1765
+ next_addr = block_addrs[i + 1]
1766
+ if next_addr > addr + self._block_sizes[addr] and next_addr - (addr + self._block_sizes[addr]) >= min_size:
1767
+ holes += 1
1768
+ return holes
1769
+
1753
1770
  def copy(self):
1754
1771
  func = Function(self._function_manager, self.addr, name=self.name, syscall=self.is_syscall)
1755
1772
  func.transition_graph = networkx.DiGraph(self.transition_graph)
@@ -427,6 +427,15 @@ class SimLibrary:
427
427
 
428
428
  return func_name in self.prototypes or func_name in self.prototypes_json
429
429
 
430
+ def is_returning(self, name: str) -> bool:
431
+ """
432
+ Check if a function is known to return.
433
+
434
+ :param name: The name of the function.
435
+ :return: A bool indicating if the function is known to return or not.
436
+ """
437
+ return name not in self.non_returning
438
+
430
439
 
431
440
  class SimCppLibrary(SimLibrary):
432
441
  """
@@ -2443,22 +2443,33 @@ def do_it(in_dir):
2443
2443
 
2444
2444
  parsed_cprotos[(prefix, lib, suffix)].append((func, proto, ""))
2445
2445
 
2446
+ non_returning_functions = {
2447
+ "KeBugCheck",
2448
+ "KeBugCheckEx",
2449
+ }
2450
+
2446
2451
  # dump to JSON files
2447
2452
  for (prefix, libname, suffix), parsed_cprotos_per_lib in parsed_cprotos.items():
2448
2453
  filename = libname.replace(".", "_") + ".json"
2449
2454
  os.makedirs(prefix, exist_ok=True)
2450
2455
  logging.debug("Writing to file %s...", filename)
2456
+ non_returning = []
2451
2457
  d = {
2452
2458
  "_t": "lib",
2453
2459
  "type_collection_names": ["win32"],
2454
2460
  "library_names": [libname if not suffix else f"{libname}.{suffix}"],
2455
2461
  "default_cc": {"X86": "SimCCStdcall", "AMD64": "SimCCMicrosoftAMD64"},
2462
+ "non_returning": non_returning,
2456
2463
  "functions": OrderedDict(),
2457
2464
  }
2458
2465
  for func, cproto, doc in sorted(parsed_cprotos_per_lib, key=lambda x: x[0]):
2459
2466
  d["functions"][func] = {"proto": json.dumps(cproto.to_json()).replace('"', "'")}
2460
2467
  if doc:
2461
2468
  d["functions"][func]["doc"] = doc
2469
+ if func in non_returning_functions:
2470
+ non_returning.append(func)
2471
+ if not non_returning:
2472
+ del d["non_returning"]
2462
2473
  with open(os.path.join(prefix, filename), "w") as f:
2463
2474
  f.write(json.dumps(d, indent="\t"))
2464
2475
 
@@ -10,6 +10,10 @@
10
10
  "X86": "SimCCStdcall",
11
11
  "AMD64": "SimCCMicrosoftAMD64"
12
12
  },
13
+ "non_returning": [
14
+ "KeBugCheck",
15
+ "KeBugCheckEx"
16
+ ],
13
17
  "functions": {
14
18
  "CcAsyncCopyRead": {
15
19
  "proto": "{'_t': 'func', 'args': [{'_t': 'ptr', 'pts_to': {'_t': '_ref', 'name': 'FILE_OBJECT', 'ot': '_ref'}}, {'_t': 'ptr', 'pts_to': {'_t': 'llong', 'label': 'Int64'}}, {'_t': 'int', 'signed': false, 'label': 'UInt32'}, {'_t': '_ref', 'name': 'BOOLEAN', 'ot': 'char'}, {'_t': 'ptr', 'pts_to': {'_t': 'bot', 'label': 'Void'}}, {'_t': 'ptr', 'pts_to': {'_t': '_ref', 'name': 'IO_STATUS_BLOCK', 'ot': '_ref'}}, {'_t': '_ref', 'name': 'PETHREAD', 'ot': 'ptr'}, {'_t': 'ptr', 'pts_to': {'_t': '_ref', 'name': 'CC_ASYNC_READ_CONTEXT', 'ot': '_ref'}}], 'returnty': {'_t': '_ref', 'name': 'BOOLEAN', 'ot': 'char'}, 'arg_names': ['FileObject', 'FileOffset', 'Length', 'Wait', 'Buffer', 'IoStatus', 'IoIssuerThread', 'AsyncReadContext']}"