angr 9.2.120__py3-none-macosx_11_0_arm64.whl → 9.2.122__py3-none-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (78) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/cfg/cfg_fast.py +2 -1
  3. angr/analyses/cfg/indirect_jump_resolvers/__init__.py +2 -0
  4. angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +2 -0
  5. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +10 -6
  6. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_fast.py +138 -367
  7. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_got.py +148 -0
  8. angr/analyses/decompiler/callsite_maker.py +11 -1
  9. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +20 -0
  10. angr/analyses/typehoon/typevars.py +37 -16
  11. angr/angrdb/db.py +1 -1
  12. angr/block.py +9 -1
  13. angr/calling_conventions.py +1 -1
  14. angr/engines/engine.py +8 -15
  15. angr/engines/pcode/lifter.py +8 -0
  16. angr/engines/successors.py +1 -1
  17. angr/engines/vex/claripy/ccall.py +1 -1
  18. angr/engines/vex/claripy/datalayer.py +7 -10
  19. angr/engines/vex/heavy/actions.py +19 -24
  20. angr/engines/vex/heavy/heavy.py +7 -1
  21. angr/engines/vex/lifter.py +3 -1
  22. angr/engines/vex/light/light.py +2 -2
  23. angr/factory.py +5 -0
  24. angr/knowledge_plugins/cfg/memory_data.py +1 -0
  25. angr/lib/angr_native.dylib +0 -0
  26. angr/protos/cfg_pb2.py +23 -15
  27. angr/protos/function_pb2.py +19 -21
  28. angr/protos/primitives_pb2.py +46 -112
  29. angr/protos/variables_pb2.py +38 -114
  30. angr/protos/xrefs_pb2.py +17 -18
  31. angr/sim_state.py +2 -78
  32. angr/sim_type.py +53 -18
  33. angr/state_plugins/solver.py +72 -15
  34. angr/storage/__init__.py +1 -2
  35. angr/storage/memory_mixins/__init__.py +5 -160
  36. angr/storage/memory_mixins/actions_mixin.py +1 -1
  37. angr/storage/memory_mixins/address_concretization_mixin.py +1 -1
  38. angr/storage/memory_mixins/bvv_conversion_mixin.py +1 -1
  39. angr/storage/memory_mixins/clouseau_mixin.py +2 -1
  40. angr/storage/memory_mixins/conditional_store_mixin.py +1 -1
  41. angr/storage/memory_mixins/convenient_mappings_mixin.py +1 -1
  42. angr/storage/memory_mixins/default_filler_mixin.py +1 -1
  43. angr/storage/memory_mixins/dirty_addrs_mixin.py +2 -1
  44. angr/storage/memory_mixins/hex_dumper_mixin.py +1 -1
  45. angr/storage/memory_mixins/javavm_memory_mixin.py +1 -1
  46. angr/storage/memory_mixins/keyvalue_memory_mixin.py +1 -1
  47. angr/storage/memory_mixins/label_merger_mixin.py +1 -1
  48. angr/storage/memory_mixins/memory_mixin.py +163 -0
  49. angr/storage/memory_mixins/multi_value_merger_mixin.py +1 -1
  50. angr/storage/memory_mixins/name_resolution_mixin.py +3 -1
  51. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +1 -1
  52. angr/storage/memory_mixins/paged_memory/paged_memory_multivalue_mixin.py +2 -1
  53. angr/storage/memory_mixins/paged_memory/pages/__init__.py +1 -1
  54. angr/storage/memory_mixins/paged_memory/pages/history_tracking_mixin.py +1 -1
  55. angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +2 -1
  56. angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +1 -1
  57. angr/storage/memory_mixins/paged_memory/pages/refcount_mixin.py +2 -1
  58. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +1 -1
  59. angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +1 -2
  60. angr/storage/memory_mixins/regioned_memory/region_category_mixin.py +2 -1
  61. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +1 -1
  62. angr/storage/memory_mixins/regioned_memory/regioned_address_concretization_mixin.py +1 -1
  63. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +2 -2
  64. angr/storage/memory_mixins/simple_interface_mixin.py +2 -1
  65. angr/storage/memory_mixins/simplification_mixin.py +2 -1
  66. angr/storage/memory_mixins/size_resolution_mixin.py +1 -1
  67. angr/storage/memory_mixins/slotted_memory.py +2 -2
  68. angr/storage/memory_mixins/smart_find_mixin.py +1 -1
  69. angr/storage/memory_mixins/symbolic_merger_mixin.py +2 -1
  70. angr/storage/memory_mixins/top_merger_mixin.py +3 -2
  71. angr/storage/memory_mixins/underconstrained_mixin.py +4 -2
  72. angr/storage/memory_mixins/unwrapper_mixin.py +2 -1
  73. {angr-9.2.120.dist-info → angr-9.2.122.dist-info}/METADATA +7 -7
  74. {angr-9.2.120.dist-info → angr-9.2.122.dist-info}/RECORD +78 -76
  75. {angr-9.2.120.dist-info → angr-9.2.122.dist-info}/LICENSE +0 -0
  76. {angr-9.2.120.dist-info → angr-9.2.122.dist-info}/WHEEL +0 -0
  77. {angr-9.2.120.dist-info → angr-9.2.122.dist-info}/entry_points.txt +0 -0
  78. {angr-9.2.120.dist-info → angr-9.2.122.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,14 @@
1
- # pylint:disable=too-many-boolean-expressions,global-statement
1
+ # pylint:disable=too-many-boolean-expressions,global-statement,too-many-positional-arguments
2
2
  from __future__ import annotations
3
3
  from typing import TYPE_CHECKING
4
4
  import logging
5
+ from enum import Enum
5
6
 
6
7
  import archinfo
7
- import claripy
8
8
  import pyvex
9
9
 
10
10
 
11
- from angr import options, BP_BEFORE
12
11
  from angr.blade import Blade
13
- from angr.annocfg import AnnotatedCFG
14
- from angr.exploration_techniques import Slicecutor
15
12
  from angr.utils.constants import DEFAULT_STATEMENT
16
13
  from .resolver import IndirectJumpResolver
17
14
 
@@ -22,14 +19,15 @@ if TYPE_CHECKING:
22
19
  l = logging.getLogger(name=__name__)
23
20
 
24
21
  PROFILING = False
25
- HITS_CASE_0, HITS_CASE_1, MISSES = 0, 0, 0
22
+ HITS_CASE_1, HITS_CASE_2, MISSES = 0, 0, 0
26
23
 
27
24
 
28
25
  def enable_profiling():
29
- global PROFILING, HITS_CASE_0, HITS_CASE_1, MISSES
26
+ global PROFILING, HITS_CASE_1, HITS_CASE_2, MISSES
27
+
30
28
  PROFILING = True
31
- HITS_CASE_0 = 0
32
29
  HITS_CASE_1 = 0
30
+ HITS_CASE_2 = 0
33
31
  MISSES = 0
34
32
 
35
33
 
@@ -38,16 +36,14 @@ def disable_profiling():
38
36
  PROFILING = False
39
37
 
40
38
 
41
- class OverwriteTmpValueCallback:
39
+ class Case2Result(Enum):
42
40
  """
43
- Overwrites temporary values during resolution
41
+ Describes the result of resolving case 2 function calls.
44
42
  """
45
43
 
46
- def __init__(self, gp_value):
47
- self.gp_value = gp_value
48
-
49
- def overwrite_tmp_value(self, state):
50
- state.inspect.tmp_write_expr = claripy.BVV(self.gp_value, state.arch.bits)
44
+ SUCCESS = 0
45
+ FAILURE = 1
46
+ RESUME = 2
51
47
 
52
48
 
53
49
  class MipsElfFastResolver(IndirectJumpResolver):
@@ -76,10 +72,14 @@ class MipsElfFastResolver(IndirectJumpResolver):
76
72
  :return: If it was resolved and targets alongside it
77
73
  :rtype: tuple
78
74
  """
79
- for max_level in range(2, 4):
80
- resolved, resolved_targets = self._resolve(cfg, addr, func_addr, block, jumpkind, max_level=max_level)
81
- if resolved:
82
- return resolved, resolved_targets
75
+ global MISSES
76
+
77
+ resolved, resolved_targets = self._resolve(cfg, addr, func_addr, block, jumpkind, max_level=2)
78
+ if resolved:
79
+ return resolved, resolved_targets
80
+
81
+ if PROFILING:
82
+ MISSES += 1
83
83
  return False, []
84
84
 
85
85
  def _resolve(self, cfg, addr, func_addr, block, jumpkind, max_level): # pylint:disable=unused-argument
@@ -96,16 +96,15 @@ class MipsElfFastResolver(IndirectJumpResolver):
96
96
  :rtype: tuple
97
97
  """
98
98
 
99
- global HITS_CASE_0, HITS_CASE_1, MISSES
100
-
101
- project = self.project
99
+ global HITS_CASE_1, HITS_CASE_2
102
100
 
101
+ func = cfg.kb.functions.function(addr=func_addr)
103
102
  b = Blade(
104
103
  cfg.graph,
105
104
  addr,
106
105
  -1,
107
106
  cfg=cfg,
108
- project=project,
107
+ project=self.project,
109
108
  ignore_sp=True,
110
109
  ignore_bp=True,
111
110
  ignored_regs=("gp",),
@@ -115,11 +114,10 @@ class MipsElfFastResolver(IndirectJumpResolver):
115
114
  include_imarks=False,
116
115
  )
117
116
 
118
- func = cfg.kb.functions.function(addr=func_addr)
119
117
  gp_value = func.info.get("gp", None)
120
118
 
121
119
  # see if gp is used on this slice at all
122
- gp_used = self._is_gp_used_on_slice(project, b)
120
+ gp_used = self._is_gp_used_on_slice(self.project, b)
123
121
  if gp_used and gp_value is None:
124
122
  # this might a special case: gp is only used once in this function, and it can be initialized right
125
123
  # before its use site.
@@ -128,362 +126,135 @@ class MipsElfFastResolver(IndirectJumpResolver):
128
126
  l.warning("Failed to determine value of register gp for function %#x.", func.addr)
129
127
  return False, []
130
128
 
131
- if gp_value is not None:
132
- target = self._try_handle_simple_case_0(gp_value, b)
133
- if target is not None:
134
- if PROFILING:
135
- HITS_CASE_0 += 1
136
- # print(f"hit/miss: {HITS_CASE_0 + HITS_CASE_1}/{MISSES}, {HITS_CASE_0}|{HITS_CASE_1}")
137
- return True, [target]
138
- target = self._try_handle_simple_case_1(gp_value, b)
139
- if target is not None:
129
+ # we support two cases:
130
+ # Case 1. t9 is set in the current block, and jalr $t9 at the end of the same block.
131
+ # Case 2. t9 is set in both predecessor blocks, and jalr $t9 at the end of the current block.
132
+
133
+ block_addrs = {block_addr for block_addr, _ in b.slice}
134
+ if len(block_addrs) == 2 and addr in block_addrs:
135
+ first_block_addr = next(iter(block_addrs - {addr}))
136
+ r, target = self._resolve_case_2(first_block_addr, block, func_addr, gp_value, cfg)
137
+ if r == Case2Result.SUCCESS:
140
138
  if PROFILING:
141
- HITS_CASE_1 += 1
142
- # print(f"hit/miss: {HITS_CASE_0 + HITS_CASE_1}/{MISSES}, {HITS_CASE_0}|{HITS_CASE_1}")
139
+ HITS_CASE_2 += 1
143
140
  return True, [target]
144
-
145
- if PROFILING:
146
- MISSES += 1
147
- # print(f"hit/miss: {HITS_CASE_0 + HITS_CASE_1}/{MISSES}, {HITS_CASE_0}|{HITS_CASE_1}")
148
-
149
- sources = [n for n in b.slice.nodes() if b.slice.in_degree(n) == 0]
150
- if not sources:
151
- return False, []
152
-
153
- source = sources[0]
154
- source_addr = source[0]
155
- annotated_cfg = AnnotatedCFG(project, None, detect_loops=False)
156
- annotated_cfg.from_digraph(b.slice)
157
-
158
- state = project.factory.blank_state(
159
- addr=source_addr,
160
- mode="fastpath",
161
- remove_options=options.refs,
162
- # suppress unconstrained stack reads for `gp`
163
- add_options={
164
- options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS,
165
- options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
166
- options.NO_CROSS_INSN_OPT,
167
- },
168
- )
169
- state.regs._t9 = func_addr
170
-
171
- if gp_used:
172
- # Special handling for cases where `gp` is stored on the stack
173
- gp_offset = project.arch.registers["gp"][0]
174
- self._set_gp_load_callback(state, b, project, gp_offset, gp_value)
175
- state.regs._gp = gp_value
176
-
177
- simgr = self.project.factory.simulation_manager(state)
178
- simgr.use_technique(Slicecutor(annotated_cfg, force_sat=True))
179
- simgr.run()
180
-
181
- if simgr.cut:
182
- # pick the successor that is cut right after executing `addr`
183
- try:
184
- target_state = next(iter(cut for cut in simgr.cut if cut.history.addr == addr))
185
- except StopIteration:
186
- l.info("Indirect jump at %#x cannot be resolved by %s.", addr, repr(self))
141
+ if r == Case2Result.FAILURE:
187
142
  return False, []
188
- target = target_state.addr
189
-
190
- if self._is_target_valid(cfg, target) and target != func_addr:
191
- l.debug("Indirect jump at %#x is resolved to target %#x.", addr, target)
192
- return True, [target]
143
+ # otherwise, we need to resume the analysis
193
144
 
194
- l.info("Indirect jump at %#x is resolved to target %#x, which seems to be invalid.", addr, target)
195
- return False, []
145
+ target = self._resolve_case_1(addr, block, func_addr, gp_value, cfg)
146
+ if target is not None:
147
+ if PROFILING:
148
+ HITS_CASE_1 += 1
149
+ return True, [target]
196
150
 
197
- l.info("Indirect jump at %#x cannot be resolved by %s.", addr, repr(self))
151
+ # no luck
198
152
  return False, []
199
153
 
200
- def _try_handle_simple_case_0(self, gp: int, blade: Blade) -> int | None:
201
- # we only attempt to support the following case:
202
- # + A | t37 = GET:I32(gp)
203
- # + B | t36 = Add32(t37,0xffff8624)
204
- # + C | t38 = LDbe:I32(t36)
205
- # + D | PUT(t9) = t38
206
- # + E | t8 = GET:I32(t9)
207
- # Next: t8
208
-
209
- nodes_with_no_outedges = []
210
- for node in blade.slice.nodes():
211
- if blade.slice.out_degree(node) == 0:
212
- nodes_with_no_outedges.append(node)
213
- if len(nodes_with_no_outedges) != 1:
214
- return None
215
-
216
- end_node = nodes_with_no_outedges[0]
217
- if end_node[-1] != DEFAULT_STATEMENT:
218
- return None
219
-
220
- end_block = self.project.factory.block(end_node[0], cross_insn_opt=blade._cross_insn_opt).vex
221
- if not isinstance(end_block.next, pyvex.IRExpr.RdTmp):
222
- return None
223
- next_tmp = end_block.next.tmp
224
-
225
- # step backward
226
-
227
- # E
228
- previous_node = self._previous_node(blade, end_node)
229
- if previous_node is None:
230
- return None
231
- stmt = end_block.statements[previous_node[1]]
232
- if not isinstance(stmt, pyvex.IRStmt.WrTmp) or not isinstance(stmt.data, pyvex.IRExpr.Get):
233
- return None
234
- if stmt.tmp != next_tmp:
235
- return None
236
- if stmt.data.offset != self.project.arch.registers["t9"][0]:
237
- return None
238
-
239
- # D
240
- previous_node = self._previous_node(blade, previous_node)
241
- if previous_node is None:
242
- return None
243
- stmt = end_block.statements[previous_node[1]]
244
- if not isinstance(stmt, pyvex.IRStmt.Put) or not isinstance(stmt.data, pyvex.IRExpr.RdTmp):
245
- return None
246
- if stmt.offset != self.project.arch.registers["t9"][0]:
247
- return None
248
- data_tmp = stmt.data.tmp
249
-
250
- # C
251
- previous_node = self._previous_node(blade, previous_node)
252
- if previous_node is None:
253
- return None
254
- stmt = end_block.statements[previous_node[1]]
255
- if (
256
- not isinstance(stmt, pyvex.IRStmt.WrTmp)
257
- or not isinstance(stmt.data, pyvex.IRExpr.Load)
258
- or not isinstance(stmt.data.addr, pyvex.IRExpr.RdTmp)
259
- ):
260
- return None
261
- if stmt.tmp != data_tmp:
262
- return None
263
- addr_tmp = stmt.data.addr.tmp
264
-
265
- # B
266
- previous_node = self._previous_node(blade, previous_node)
267
- if previous_node is None:
268
- return None
269
- stmt = end_block.statements[previous_node[1]]
270
- if (
271
- not isinstance(stmt, pyvex.IRStmt.WrTmp)
272
- or stmt.tmp != addr_tmp
273
- or not isinstance(stmt.data, pyvex.IRExpr.Binop)
274
- or stmt.data.op != "Iop_Add32"
275
- or not isinstance(stmt.data.args[0], pyvex.IRExpr.RdTmp)
276
- or not isinstance(stmt.data.args[1], pyvex.IRExpr.Const)
277
- ):
278
- return None
279
- add_tmp = stmt.data.args[0].tmp
280
- add_const = stmt.data.args[1].con.value
281
-
282
- # A
283
- previous_node = self._previous_node(blade, previous_node)
284
- if previous_node is None:
285
- return None
286
- stmt = end_block.statements[previous_node[1]]
287
- if (
288
- not isinstance(stmt, pyvex.IRStmt.WrTmp)
289
- or stmt.tmp != add_tmp
290
- or not isinstance(stmt.data, pyvex.IRExpr.Get)
291
- ):
292
- return None
293
- if stmt.data.offset != self.project.arch.registers["gp"][0]:
294
- return None
295
-
296
- # matching complete
297
- addr = (gp + add_const) & 0xFFFF_FFFF
298
- try:
299
- return self.project.loader.memory.unpack_word(addr, size=4)
300
- except KeyError:
301
- return None
302
-
303
- def _try_handle_simple_case_1(self, gp: int, blade: Blade) -> int | None:
304
- # we only attempt to support the following case:
305
- # + A | t22 = GET:I32(gp)
306
- # + B | t21 = Add32(t22,0xffff8020)
307
- # + C | t23 = LDbe:I32(t21)
308
- # + D | PUT(t9) = t23
309
- # + E | t27 = GET:I32(t9)
310
- # + F | t26 = Add32(t27,0x00007cec)
311
- # + G | PUT(t9) = t26
312
- # + H | t4 = GET:I32(t9)
313
- # + Next: t4
314
-
315
- nodes_with_no_outedges = []
316
- for node in blade.slice.nodes():
317
- if blade.slice.out_degree(node) == 0:
318
- nodes_with_no_outedges.append(node)
319
- if len(nodes_with_no_outedges) != 1:
320
- return None
321
-
322
- end_node = nodes_with_no_outedges[0]
323
- if end_node[-1] != DEFAULT_STATEMENT:
324
- return None
325
-
326
- end_block = self.project.factory.block(end_node[0], cross_insn_opt=blade._cross_insn_opt).vex
327
- if not isinstance(end_block.next, pyvex.IRExpr.RdTmp):
328
- return None
329
- next_tmp = end_block.next.tmp
330
-
331
- # step backward
332
-
333
- # H
334
- previous_node = self._previous_node(blade, end_node)
335
- if previous_node is None:
336
- return None
337
- stmt = end_block.statements[previous_node[1]]
338
- if not isinstance(stmt, pyvex.IRStmt.WrTmp) or not isinstance(stmt.data, pyvex.IRExpr.Get):
339
- return None
340
- if stmt.tmp != next_tmp:
341
- return None
342
- if stmt.data.offset != self.project.arch.registers["t9"][0]:
343
- return None
344
-
345
- # G
346
- previous_node = self._previous_node(blade, previous_node)
347
- if previous_node is None:
348
- return None
349
- stmt = end_block.statements[previous_node[1]]
350
- if not isinstance(stmt, pyvex.IRStmt.Put) or not isinstance(stmt.data, pyvex.IRExpr.RdTmp):
351
- return None
352
- if stmt.offset != self.project.arch.registers["t9"][0]:
353
- return None
354
- t9_tmp_G = stmt.data.tmp
355
-
356
- # F
357
- previous_node = self._previous_node(blade, previous_node)
358
- if previous_node is None:
359
- return None
360
- stmt = end_block.statements[previous_node[1]]
361
- if (
362
- not isinstance(stmt, pyvex.IRStmt.WrTmp)
363
- or stmt.tmp != t9_tmp_G
364
- or not isinstance(stmt.data, pyvex.IRExpr.Binop)
365
- or stmt.data.op != "Iop_Add32"
366
- or not isinstance(stmt.data.args[0], pyvex.IRExpr.RdTmp)
367
- or not isinstance(stmt.data.args[1], pyvex.IRExpr.Const)
368
- ):
369
- return None
370
- t9_tmp_F = stmt.data.args[0].tmp
371
- t9_add_const = stmt.data.args[1].con.value
372
-
373
- # E
374
- previous_node = self._previous_node(blade, previous_node)
375
- if previous_node is None:
376
- return None
377
- stmt = end_block.statements[previous_node[1]]
378
- if not isinstance(stmt, pyvex.IRStmt.WrTmp) or not isinstance(stmt.data, pyvex.IRExpr.Get):
379
- return None
380
- if stmt.tmp != t9_tmp_F:
381
- return None
382
- if stmt.data.offset != self.project.arch.registers["t9"][0]:
383
- return None
384
-
385
- # D
386
- previous_node = self._previous_node(blade, previous_node)
387
- if previous_node is None:
388
- return None
389
- stmt = end_block.statements[previous_node[1]]
390
- if not isinstance(stmt, pyvex.IRStmt.Put) or not isinstance(stmt.data, pyvex.IRExpr.RdTmp):
391
- return None
392
- if stmt.offset != self.project.arch.registers["t9"][0]:
393
- return None
394
- t9_tmp_D = stmt.data.tmp
395
-
396
- # C
397
- previous_node = self._previous_node(blade, previous_node)
398
- if previous_node is None:
399
- return None
400
- stmt = end_block.statements[previous_node[1]]
401
- if (
402
- not isinstance(stmt, pyvex.IRStmt.WrTmp)
403
- or not isinstance(stmt.data, pyvex.IRExpr.Load)
404
- or not isinstance(stmt.data.addr, pyvex.IRExpr.RdTmp)
405
- ):
406
- return None
407
- if stmt.tmp != t9_tmp_D:
408
- return None
409
- addr_tmp = stmt.data.addr.tmp
410
-
411
- # B
412
- previous_node = self._previous_node(blade, previous_node)
413
- if previous_node is None:
414
- return None
415
- stmt = end_block.statements[previous_node[1]]
416
- if (
417
- not isinstance(stmt, pyvex.IRStmt.WrTmp)
418
- or stmt.tmp != addr_tmp
419
- or not isinstance(stmt.data, pyvex.IRExpr.Binop)
420
- or stmt.data.op != "Iop_Add32"
421
- or not isinstance(stmt.data.args[0], pyvex.IRExpr.RdTmp)
422
- or not isinstance(stmt.data.args[1], pyvex.IRExpr.Const)
423
- ):
424
- return None
425
- add_tmp = stmt.data.args[0].tmp
426
- add_const = stmt.data.args[1].con.value
154
+ def _resolve_case_1(self, addr: int, block: pyvex.IRSB, func_addr: int, gp_value: int, cfg) -> int | None:
155
+ # lift the block again with the correct setting
156
+ first_irsb = self.project.factory.block(
157
+ addr,
158
+ size=block.size,
159
+ collect_data_refs=False,
160
+ const_prop=True,
161
+ cross_insn_opt=False,
162
+ load_from_ro_regions=True,
163
+ initial_regs=[
164
+ (self.project.arch.registers["t9"][0], self.project.arch.registers["t9"][1], func_addr),
165
+ (self.project.arch.registers["gp"][0], self.project.arch.registers["gp"][1], gp_value),
166
+ ],
167
+ ).vex_nostmt
168
+
169
+ if not isinstance(first_irsb.next, pyvex.IRExpr.RdTmp):
170
+ return None
171
+ target_tmp = first_irsb.next.tmp
172
+ if first_irsb.const_vals is None:
173
+ return None
174
+
175
+ # find the value of the next tmp
176
+ for cv in first_irsb.const_vals:
177
+ if cv.tmp == target_tmp:
178
+ target = cv.value
179
+ if self._is_target_valid(cfg, target):
180
+ return target
181
+ break
182
+
183
+ return None
184
+
185
+ def _resolve_case_2(
186
+ self, first_block_addr: int, second_block: pyvex.IRSB, func_addr: int, gp_value: int, cfg
187
+ ) -> tuple[Case2Result, int | None]:
188
+ jump_target_reg = self._get_jump_target_reg(second_block)
189
+ if jump_target_reg is None:
190
+ return Case2Result.FAILURE, None
191
+ last_reg_setting_tmp = self._get_last_reg_setting_tmp(second_block, jump_target_reg)
192
+ if last_reg_setting_tmp is not None:
193
+ # the register (t9) is set in this block - we can resolve the jump target using only the current block
194
+ return Case2Result.RESUME, None
195
+
196
+ # lift the first block again with the correct setting
197
+ first_irsb = self.project.factory.block(
198
+ first_block_addr,
199
+ cross_insn_opt=False,
200
+ collect_data_refs=False,
201
+ const_prop=True,
202
+ load_from_ro_regions=True,
203
+ initial_regs=[
204
+ (self.project.arch.registers["t9"][0], self.project.arch.registers["t9"][1], func_addr),
205
+ (self.project.arch.registers["gp"][0], self.project.arch.registers["gp"][1], gp_value),
206
+ ],
207
+ ).vex_nostmt
208
+
209
+ last_reg_setting_tmp = self._get_last_reg_setting_tmp(first_irsb, jump_target_reg)
210
+ if last_reg_setting_tmp is None:
211
+ return Case2Result.FAILURE, None
212
+
213
+ # find the value of the next tmp
214
+ if first_irsb.const_vals is None:
215
+ return Case2Result.FAILURE, None
216
+ for cv in first_irsb.const_vals:
217
+ if cv.tmp == last_reg_setting_tmp:
218
+ target = cv.value
219
+ if self._is_target_valid(cfg, target):
220
+ return Case2Result.SUCCESS, target
221
+ break
222
+
223
+ return Case2Result.FAILURE, None
427
224
 
428
- # A
429
- previous_node = self._previous_node(blade, previous_node)
430
- if previous_node is None:
431
- return None
432
- stmt = end_block.statements[previous_node[1]]
433
- if (
434
- not isinstance(stmt, pyvex.IRStmt.WrTmp)
435
- or stmt.tmp != add_tmp
436
- or not isinstance(stmt.data, pyvex.IRExpr.Get)
437
- ):
225
+ @staticmethod
226
+ def _get_jump_target_reg(block: pyvex.IRSB) -> int | None:
227
+ if block.jumpkind != "Ijk_Call":
438
228
  return None
439
- if stmt.data.offset != self.project.arch.registers["gp"][0]:
229
+ if not isinstance(block.next, pyvex.IRExpr.RdTmp):
440
230
  return None
231
+ next_tmp = block.next.tmp
441
232
 
442
- # matching complete
443
- addr = (gp + add_const) & 0xFFFF_FFFF
444
- try:
445
- target_0 = self.project.loader.memory.unpack_word(addr, size=4)
446
- return (target_0 + t9_add_const) & 0xFFFF_FFFF
447
- except KeyError:
448
- return None
233
+ for stmt in reversed(block.statements):
234
+ if (
235
+ isinstance(stmt, pyvex.IRStmt.Put)
236
+ and isinstance(stmt.data, pyvex.IRExpr.RdTmp)
237
+ and stmt.data.tmp == next_tmp
238
+ ):
239
+ return stmt.offset
240
+ if (
241
+ isinstance(stmt, pyvex.IRStmt.WrTmp)
242
+ and stmt.tmp == next_tmp
243
+ and isinstance(stmt.data, pyvex.IRExpr.Get)
244
+ ):
245
+ return stmt.data.offset
449
246
 
450
- @staticmethod
451
- def _previous_node(blade: Blade, curr_node: tuple[int, int]) -> tuple[int, int] | None:
452
- if blade.slice.in_degree(curr_node) != 1:
453
- return None
454
- nn = next(iter(blade.slice.predecessors(curr_node)))
455
- if nn[0] != curr_node[0]:
456
- return None
457
- return nn
247
+ return None
458
248
 
459
249
  @staticmethod
460
- def _set_gp_load_callback(state, blade, project, gp_offset, gp_value):
461
- tmps = {}
462
- for block_addr_in_slice in {slice_node[0] for slice_node in blade.slice.nodes()}:
463
- for stmt in project.factory.block(block_addr_in_slice, cross_insn_opt=False).vex.statements:
464
- if isinstance(stmt, pyvex.IRStmt.WrTmp) and isinstance(stmt.data, pyvex.IRExpr.Load):
465
- # Load from memory to a tmp - assuming it's loading from the stack
466
- tmps[stmt.tmp] = "stack"
467
- elif (
468
- isinstance(stmt, pyvex.IRStmt.Put)
469
- and stmt.offset == gp_offset
470
- and isinstance(stmt.data, pyvex.IRExpr.RdTmp)
471
- ):
472
- tmp_offset = stmt.data.tmp # pylint:disable=cell-var-from-loop
473
- if tmps.get(tmp_offset) == "stack":
474
- # found the load from stack
475
- # we must make sure value of that temporary variable equals to the correct gp value
476
- state.inspect.make_breakpoint(
477
- "tmp_write",
478
- when=BP_BEFORE,
479
- condition=(
480
- lambda s, bbl_addr_=block_addr_in_slice, tmp_offset_=tmp_offset: s.scratch.bbl_addr
481
- == bbl_addr_
482
- and s.inspect.tmp_write_num == tmp_offset_
483
- ),
484
- action=OverwriteTmpValueCallback(gp_value).overwrite_tmp_value,
485
- )
486
- break
250
+ def _get_last_reg_setting_tmp(block: pyvex.IRSB, target_reg: int) -> int | None:
251
+ for stmt in reversed(block.statements):
252
+ if isinstance(stmt, pyvex.IRStmt.Put) and stmt.offset == target_reg:
253
+ if isinstance(stmt.data, pyvex.IRExpr.RdTmp):
254
+ return stmt.data.tmp
255
+ return None
256
+
257
+ return None
487
258
 
488
259
  @staticmethod
489
260
  def _is_gp_used_on_slice(project, b: Blade) -> bool: