angr 9.2.146__py3-none-win_amd64.whl → 9.2.148__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 (67) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/analysis.py +3 -11
  3. angr/analyses/bindiff.py +343 -68
  4. angr/analyses/calling_convention/fact_collector.py +5 -4
  5. angr/analyses/calling_convention/utils.py +1 -0
  6. angr/analyses/cfg/cfg_arch_options.py +10 -0
  7. angr/analyses/cfg/cfg_base.py +42 -74
  8. angr/analyses/cfg/cfg_emulated.py +12 -12
  9. angr/analyses/cfg/cfg_fast.py +39 -20
  10. angr/analyses/cfg/cfg_fast_soot.py +3 -3
  11. angr/analyses/decompiler/callsite_maker.py +28 -18
  12. angr/analyses/decompiler/clinic.py +4 -4
  13. angr/analyses/decompiler/condition_processor.py +0 -21
  14. angr/analyses/decompiler/counters/call_counter.py +3 -0
  15. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +1 -1
  16. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +14 -0
  17. angr/analyses/decompiler/structured_codegen/c.py +5 -5
  18. angr/analyses/decompiler/structuring/phoenix.py +11 -3
  19. angr/analyses/deobfuscator/api_obf_finder.py +5 -1
  20. angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +1 -1
  21. angr/analyses/flirt/__init__.py +47 -0
  22. angr/analyses/flirt/consts.py +160 -0
  23. angr/analyses/{flirt.py → flirt/flirt.py} +99 -38
  24. angr/analyses/flirt/flirt_function.py +20 -0
  25. angr/analyses/flirt/flirt_matcher.py +351 -0
  26. angr/analyses/flirt/flirt_module.py +32 -0
  27. angr/analyses/flirt/flirt_node.py +23 -0
  28. angr/analyses/flirt/flirt_sig.py +356 -0
  29. angr/analyses/flirt/flirt_utils.py +31 -0
  30. angr/analyses/forward_analysis/visitors/graph.py +0 -8
  31. angr/analyses/identifier/runner.py +1 -1
  32. angr/analyses/reaching_definitions/function_handler.py +4 -4
  33. angr/analyses/reassembler.py +1 -1
  34. angr/analyses/stack_pointer_tracker.py +35 -1
  35. angr/analyses/static_hooker.py +11 -9
  36. angr/analyses/variable_recovery/engine_ail.py +8 -8
  37. angr/analyses/variable_recovery/engine_base.py +2 -0
  38. angr/block.py +6 -6
  39. angr/calling_conventions.py +74 -23
  40. angr/engines/vex/heavy/concretizers.py +10 -0
  41. angr/exploration_techniques/director.py +1 -1
  42. angr/flirt/__init__.py +15 -44
  43. angr/knowledge_plugins/functions/function.py +42 -39
  44. angr/knowledge_plugins/functions/function_manager.py +9 -0
  45. angr/knowledge_plugins/functions/function_parser.py +9 -1
  46. angr/knowledge_plugins/functions/soot_function.py +1 -1
  47. angr/knowledge_plugins/key_definitions/key_definition_manager.py +1 -1
  48. angr/lib/angr_native.dll +0 -0
  49. angr/procedures/definitions/__init__.py +14 -11
  50. angr/procedures/stubs/format_parser.py +1 -1
  51. angr/project.py +23 -29
  52. angr/protos/cfg_pb2.py +14 -25
  53. angr/protos/function_pb2.py +11 -22
  54. angr/protos/primitives_pb2.py +36 -47
  55. angr/protos/variables_pb2.py +28 -39
  56. angr/protos/xrefs_pb2.py +8 -19
  57. angr/sim_type.py +0 -16
  58. angr/simos/cgc.py +1 -1
  59. angr/simos/linux.py +5 -5
  60. angr/simos/windows.py +5 -5
  61. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +1 -1
  62. {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/METADATA +8 -8
  63. {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/RECORD +67 -59
  64. {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/WHEEL +1 -1
  65. {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/entry_points.txt +0 -0
  66. {angr-9.2.146.dist-info → angr-9.2.148.dist-info/licenses}/LICENSE +0 -0
  67. {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/top_level.txt +0 -0
@@ -90,6 +90,7 @@ class PhoenixStructurer(StructurerBase):
90
90
  parent_region=None,
91
91
  improve_algorithm=False,
92
92
  use_multistmtexprs: MultiStmtExprMode = MultiStmtExprMode.MAX_ONE_CALL,
93
+ multistmtexpr_stmt_threshold: int = 5,
93
94
  **kwargs,
94
95
  ):
95
96
  super().__init__(
@@ -126,6 +127,7 @@ class PhoenixStructurer(StructurerBase):
126
127
  self._edge_virtualization_hints = []
127
128
 
128
129
  self._use_multistmtexprs = use_multistmtexprs
130
+ self._multistmtexpr_stmt_threshold = multistmtexpr_stmt_threshold
129
131
  self._analyze()
130
132
 
131
133
  @staticmethod
@@ -540,7 +542,11 @@ class PhoenixStructurer(StructurerBase):
540
542
  ):
541
543
  stmts = self._build_multistatementexpr_statements(succ)
542
544
  assert stmts is not None
543
- if stmts:
545
+ if (
546
+ stmts
547
+ and sum(1 for stmt in stmts if not isinstance(stmt, Label))
548
+ <= self._multistmtexpr_stmt_threshold
549
+ ):
544
550
  edge_cond_succhead = MultiStatementExpression(
545
551
  None,
546
552
  stmts,
@@ -2612,12 +2618,14 @@ class PhoenixStructurer(StructurerBase):
2612
2618
  if self._use_multistmtexprs == MultiStmtExprMode.NEVER:
2613
2619
  return False
2614
2620
  if self._use_multistmtexprs == MultiStmtExprMode.ALWAYS:
2615
- return True
2621
+ ctr = AILCallCounter()
2622
+ ctr.walk(node)
2623
+ return ctr.non_label_stmts <= self._multistmtexpr_stmt_threshold
2616
2624
  if self._use_multistmtexprs == MultiStmtExprMode.MAX_ONE_CALL:
2617
2625
  # count the number of calls
2618
2626
  ctr = AILCallCounter()
2619
2627
  ctr.walk(node)
2620
- return ctr.calls <= 1
2628
+ return ctr.calls <= 1 and ctr.non_label_stmts <= self._multistmtexpr_stmt_threshold
2621
2629
  l.warning("Unsupported enum value for _use_multistmtexprs: %s", self._use_multistmtexprs)
2622
2630
  return False
2623
2631
 
@@ -315,7 +315,11 @@ class APIObfuscationFinder(Analysis):
315
315
 
316
316
  @staticmethod
317
317
  def is_apiname(name: str) -> bool:
318
- return any(not isinstance(lib, SimSyscallLibrary) and lib.has_prototype(name) for lib in SIM_LIBRARIES.values())
318
+ return any(
319
+ not isinstance(lib, SimSyscallLibrary) and lib.has_prototype(name)
320
+ for libs in SIM_LIBRARIES.values()
321
+ for lib in libs
322
+ )
319
323
 
320
324
 
321
325
  AnalysesHub.register_default("APIObfuscationFinder", APIObfuscationFinder)
@@ -30,7 +30,7 @@ class APIObfType1PeepholeOptimizer(PeepholeOptimizationExprBase):
30
30
  # assign a new function on-demand
31
31
  symbol = self.project.loader.extern_object.make_extern(funcname)
32
32
  hook_addr = self.project.hook_symbol(
33
- symbol.rebased_addr, SIM_LIBRARIES["linux"].get_stub(funcname, self.project.arch)
33
+ symbol.rebased_addr, SIM_LIBRARIES["linux"][0].get_stub(funcname, self.project.arch)
34
34
  )
35
35
  func = self.kb.functions.function(addr=hook_addr, name=funcname, create=True)
36
36
  func.is_simprocedure = True
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+ from .flirt import FlirtAnalysis
3
+ from .flirt_sig import FlirtSignature, FlirtSignatureParsed, FlirtSignatureError
4
+ from .consts import FLIRT_ARCH_TO_ARCHNAME, FLIRT_OS_TO_OSNAME, FlirtAppType, FlirtOSType
5
+
6
+
7
+ def flirt_arch_to_arch_name(flirt_arch: int, app_types: int) -> str:
8
+ """
9
+ Convert FLIRT architecture ID to architecture name.
10
+
11
+ :param flirt_arch: FLIRT architecture ID.
12
+ :param app_types: FLIRT application types.
13
+ :return: Architecture name.
14
+ """
15
+ try:
16
+ arches = FLIRT_ARCH_TO_ARCHNAME[flirt_arch]
17
+ except KeyError:
18
+ return "Unknown"
19
+ if app_types & FlirtAppType.APP_32_BIT and 32 in arches:
20
+ return arches[32]
21
+ if app_types & FlirtAppType.APP_64_BIT and 64 in arches:
22
+ return arches[64]
23
+ return "Unknown"
24
+
25
+
26
+ def flirt_os_type_to_os_name(os_type: int) -> str:
27
+ """
28
+ Convert FLIRT OS type to OS name.
29
+
30
+ :param os_type: FLIRT OS type.
31
+ :return: OS name.
32
+ """
33
+ try:
34
+ v = FlirtOSType(os_type)
35
+ return FLIRT_OS_TO_OSNAME.get(v, v.name)
36
+ except ValueError:
37
+ return "UnknownOS"
38
+
39
+
40
+ __all__ = [
41
+ "FlirtAnalysis",
42
+ "FlirtSignature",
43
+ "FlirtSignatureError",
44
+ "FlirtSignatureParsed",
45
+ "flirt_arch_to_arch_name",
46
+ "flirt_os_type_to_os_name",
47
+ ]
@@ -0,0 +1,160 @@
1
+ # pylint:disable=missing-class-docstring
2
+ from __future__ import annotations
3
+ from enum import Enum
4
+
5
+
6
+ class FlirtArch(int, Enum):
7
+ ARCH_386 = 0 # Intel 80x86
8
+ ARCH_Z80 = 1 # 8085, Z80
9
+ ARCH_I860 = 2 # Intel 860
10
+ ARCH_8051 = 3 # 8051
11
+ ARCH_TMS = 4 # Texas Instruments TMS320C5x
12
+ ARCH_6502 = 5 # 6502
13
+ ARCH_PDP = 6 # PDP11
14
+ ARCH_68K = 7 # Motorola 680x0
15
+ ARCH_JAVA = 8 # Java
16
+ ARCH_6800 = 9 # Motorola 68xx
17
+ ARCH_ST7 = 10 # SGS-Thomson ST7
18
+ ARCH_MC6812 = 11 # Motorola 68HC12
19
+ ARCH_MIPS = 12 # MIPS
20
+ ARCH_ARM = 13 # Advanced RISC Machines
21
+ ARCH_TMSC6 = 14 # Texas Instruments TMS320C6x
22
+ ARCH_PPC = 15 # PowerPC
23
+ ARCH_80196 = 16 # Intel 80196
24
+ ARCH_Z8 = 17 # Z8
25
+ ARCH_SH = 18 # Renesas (formerly Hitachi) SuperH
26
+ ARCH_NET = 19 # Microsoft Visual Studio.Net
27
+ ARCH_AVR = 20 # Atmel 8-bit RISC processor(s)
28
+ ARCH_H8 = 21 # Hitachi H8/300, H8/2000
29
+ ARCH_PIC = 22 # Microchip's PIC
30
+ ARCH_SPARC = 23 # SPARC
31
+ ARCH_ALPHA = 24 # DEC Alpha
32
+ ARCH_HPPA = 25 # Hewlett-Packard PA-RISC
33
+ ARCH_H8500 = 26 # Hitachi H8/500
34
+ ARCH_TRICORE = 27 # Tasking Tricore
35
+ ARCH_DSP56K = 28 # Motorola DSP5600x
36
+ ARCH_C166 = 29 # Siemens C166 family
37
+ ARCH_ST20 = 30 # SGS-Thomson ST20
38
+ ARCH_IA64 = 31 # Intel Itanium IA64
39
+ ARCH_I960 = 32 # Intel 960
40
+ ARCH_F2MC = 33 # Fujitsu F2MC-16
41
+ ARCH_TMS320C54 = 34 # Texas Instruments TMS320C54xx
42
+ ARCH_TMS320C55 = 35 # Texas Instruments TMS320C55xx
43
+ ARCH_TRIMEDIA = 36 # Trimedia
44
+ ARCH_M32R = 37 # Mitsubishi 32bit RISC
45
+ ARCH_NEC_78K0 = 38 # NEC 78K0
46
+ ARCH_NEC_78K0S = 39 # NEC 78K0S
47
+ ARCH_M740 = 40 # Mitsubishi 8bit
48
+ ARCH_M7700 = 41 # Mitsubishi 16bit
49
+ ARCH_ST9 = 42 # ST9+
50
+ ARCH_FR = 43 # Fujitsu FR Family
51
+ ARCH_MC6816 = 44 # Motorola 68HC16
52
+ ARCH_M7900 = 45 # Mitsubishi 7900
53
+ ARCH_TMS320C3 = 46 # Texas Instruments TMS320C3
54
+ ARCH_KR1878 = 47 # Angstrem KR1878
55
+ ARCH_AD218X = 48 # Analog Devices ADSP 218X
56
+ ARCH_OAKDSP = 49 # Atmel OAK DSP
57
+ ARCH_TLCS900 = 50 # Toshiba TLCS-900
58
+ ARCH_C39 = 51 # Rockwell C39
59
+ ARCH_CR16 = 52 # NSC CR16
60
+ ARCH_MN102L00 = 53 # Panasonic MN10200
61
+ ARCH_TMS320C1X = 54 # Texas Instruments TMS320C1x
62
+ ARCH_NEC_V850X = 55 # NEC V850 and V850ES/E1/E2
63
+ ARCH_SCR_ADPT = 56 # Processor module adapter for processor modules written in scripting languages
64
+ ARCH_EBC = 57 # EFI Bytecode
65
+ ARCH_MSP430 = 58 # Texas Instruments MSP430
66
+ ARCH_SPU = 59 # Cell Broadband Engine Synergistic Processor Unit
67
+ ARCH_DALVIK = 60 # Android Dalvik Virtual Machine
68
+
69
+
70
+ FLIRT_ARCH_TO_ARCHNAME: dict[int, dict[int, str]] = {
71
+ FlirtArch.ARCH_386: {32: "X86", 64: "AMD64"},
72
+ FlirtArch.ARCH_MIPS: {32: "MIPS32", 64: "MIPS64"},
73
+ FlirtArch.ARCH_ARM: {32: "ARM", 64: "AARCH64"},
74
+ FlirtArch.ARCH_PPC: {32: "PPC32", 64: "PPC64"},
75
+ FlirtArch.ARCH_SPARC: {32: "SPARC32", 64: "SPARC64"},
76
+ }
77
+
78
+
79
+ class FlirtFileType(int, Enum):
80
+ FILE_DOS_EXE_OLD = 0x00000001
81
+ FILE_DOS_COM_OLD = 0x00000002
82
+ FILE_BIN = 0x00000004
83
+ FILE_DOSDRV = 0x00000008
84
+ FILE_NE = 0x00000010
85
+ FILE_INTELHEX = 0x00000020
86
+ FILE_MOSHEX = 0x00000040
87
+ FILE_LX = 0x00000080
88
+ FILE_LE = 0x00000100
89
+ FILE_NLM = 0x00000200
90
+ FILE_COFF = 0x00000400
91
+ FILE_PE = 0x00000800
92
+ FILE_OMF = 0x00001000
93
+ FILE_SREC = 0x00002000
94
+ FILE_ZIP = 0x00004000
95
+ FILE_OMFLIB = 0x00008000
96
+ FILE_AR = 0x00010000
97
+ FILE_LOADER = 0x00020000
98
+ FILE_ELF = 0x00040000
99
+ FILE_W32RUN = 0x00080000
100
+ FILE_AOUT = 0x00100000
101
+ FILE_PILOT = 0x00200000
102
+ FILE_DOS_EXE = 0x00400000
103
+ FILE_DOS_COM = 0x00800000
104
+ FILE_AIXAR = 0x01000000
105
+
106
+
107
+ class FlirtOSType(int, Enum):
108
+ """
109
+ Actually no longer used in IDA.
110
+ """
111
+
112
+ OS_MSDOS = 0x01
113
+ OS_WIN = 0x02
114
+ OS_OS2 = 0x04
115
+ OS_NETWARE = 0x08
116
+ OS_UNIX = 0x10
117
+ OS_OTHER = 0x20
118
+
119
+
120
+ FLIRT_OS_TO_OSNAME = {
121
+ FlirtOSType.OS_MSDOS: "MSDOS",
122
+ FlirtOSType.OS_WIN: "Win32",
123
+ FlirtOSType.OS_OS2: "OS/2",
124
+ FlirtOSType.OS_UNIX: "Linux",
125
+ FlirtOSType.OS_OTHER: "Other",
126
+ }
127
+
128
+
129
+ class FlirtAppType(int, Enum):
130
+ APP_CONSOLE = 0x0001
131
+ APP_GRAPHICS = 0x0002
132
+ APP_EXE = 0x0004
133
+ APP_DLL = 0x0008
134
+ APP_DRV = 0x0010
135
+ APP_SINGLE_THREADED = 0x0020
136
+ APP_MULTI_THREADED = 0x0040
137
+ APP_16_BIT = 0x0080
138
+ APP_32_BIT = 0x0100
139
+ APP_64_BIT = 0x0200
140
+
141
+
142
+ class FlirtFeatureFlag(int, Enum):
143
+ FEATURE_STARTUP = 0x1
144
+ FEATURE_CTYPE_CRC = 0x2
145
+ FEATURE_2BYTE_CTYPE = 0x4
146
+ FEATURE_ALT_CTYPE_CRC = 0x8
147
+ FEATURE_COMPRESSED = 0x10
148
+
149
+
150
+ class FlirtParseFlag(int, Enum):
151
+ PARSE_MORE_PUBLIC_NAMES = 0x1
152
+ PARSE_READ_TAIL_BYTES = 0x2
153
+ PARSE_READ_REFERENCED_FUNCTIONS = 0x4
154
+ PARSE_MORE_MODULES_WITH_SAME_CRC = 0x8
155
+ PARSE_MORE_MODULES = 0x10
156
+
157
+
158
+ class FlirtFunctionFlag(int, Enum):
159
+ FUNCTION_LOCAL = 0x2
160
+ FUNCTION_UNRESOLVED_COLLISION = 0x8
@@ -1,17 +1,19 @@
1
1
  from __future__ import annotations
2
2
  from typing import TYPE_CHECKING
3
- from functools import partial
3
+
4
+ from collections.abc import Generator
4
5
  from collections import defaultdict
6
+ import contextlib
5
7
  import logging
6
8
 
7
- import nampa
8
9
  from archinfo.arch_arm import is_arm_arch
9
10
 
10
11
  from angr.analyses import AnalysesHub
12
+ from angr.analyses.analysis import Analysis
11
13
  from angr.errors import AngrRuntimeError
12
- from angr.flirt import FlirtSignature, STRING_TO_LIBRARIES, LIBRARY_TO_SIGNATURES, FLIRT_SIGNATURES_BY_ARCH
13
- from .analysis import Analysis
14
- import contextlib
14
+ from .flirt_sig import FlirtSignature, FlirtSignatureParsed
15
+ from .flirt_function import FlirtFunction
16
+ from .flirt_matcher import FlirtMatcher
15
17
 
16
18
  if TYPE_CHECKING:
17
19
  from angr.knowledge_plugins.functions import Function
@@ -33,19 +35,22 @@ class FlirtAnalysis(Analysis):
33
35
  current binary, and then match all possible signatures for the architecture.
34
36
  """
35
37
 
36
- def __init__(self, sig: FlirtSignature | str | None = None):
38
+ def __init__(self, sig: FlirtSignature | str | None = None, max_mismatched_bytes: int = 0):
39
+
40
+ from angr.flirt import FLIRT_SIGNATURES_BY_ARCH # pylint:disable=import-outside-toplevel
41
+
37
42
  self._is_arm = is_arm_arch(self.project.arch)
38
43
  self._all_suggestions: dict[str, dict[str, dict[int, str]]] = {}
39
44
  self._suggestions: dict[int, str] = {}
40
45
  self.matched_suggestions: dict[str, tuple[FlirtSignature, dict[int, str]]] = {}
41
46
  self._temporary_sig = False
47
+ self._max_mismatched_bytes = max_mismatched_bytes
42
48
 
43
49
  if sig:
44
50
  if isinstance(sig, str):
45
51
  # this is a file path
46
- sig = FlirtSignature(
47
- self.project.arch.name.lower(), self.project.simos.name.lower(), "Temporary", sig, None
48
- )
52
+ simos_name = self.project.arch.name if self.project.simos.name is not None else "UnknownOS"
53
+ sig = FlirtSignature(simos_name.lower(), simos_name.lower(), "Temporary", sig, None)
49
54
 
50
55
  self.signatures = [sig]
51
56
  self._temporary_sig = True
@@ -87,13 +92,17 @@ class FlirtAnalysis(Analysis):
87
92
 
88
93
  if max_suggestion_sig_path is not None:
89
94
  sig_ = path_to_sig.get(max_suggestion_sig_path)
95
+ assert sig_ is not None
90
96
  _l.info("Applying FLIRT signature %s for library %s.", sig_, lib)
91
97
  self._apply_changes(
92
98
  sig_.sig_name if not self._temporary_sig else None, sig_to_suggestions[max_suggestion_sig_path]
93
99
  )
94
100
  self.matched_suggestions[lib] = (sig_, sig_to_suggestions[max_suggestion_sig_path])
95
101
 
96
- def _find_hits_by_strings(self, regions: list[bytes]) -> list[FlirtSignature]:
102
+ def _find_hits_by_strings(self, regions: list[bytes]) -> Generator[FlirtSignature]:
103
+
104
+ from angr.flirt import STRING_TO_LIBRARIES, LIBRARY_TO_SIGNATURES # pylint:disable=import-outside-toplevel
105
+
97
106
  library_hits: dict[str, int] = defaultdict(int)
98
107
  for s, libs in STRING_TO_LIBRARIES.items():
99
108
  for region in regions:
@@ -117,39 +126,91 @@ class FlirtAnalysis(Analysis):
117
126
  # match each function
118
127
  self._suggestions = {}
119
128
  with open(sig.sig_path, "rb") as sigfile:
120
- flirt = nampa.parse_flirt_file(sigfile)
121
- for func in self.project.kb.functions.values():
122
- func: Function
123
- if func.is_simprocedure or func.is_plt:
124
- continue
125
- if not func.is_default_name:
126
- # it already has a name. skip
127
- continue
128
-
129
- start = func.addr
130
- if self._is_arm:
131
- start = start & 0xFFFF_FFFE
132
-
133
- max_block_addr = max(func.block_addrs_set)
134
- end_block = func.get_block(max_block_addr)
135
- end = max_block_addr + end_block.size
136
-
137
- if self._is_arm:
138
- end = end & 0xFFFF_FFFE
139
-
140
- # load all bytes
141
- func_bytes = self.project.loader.memory.load(start, end - start + 0x100)
142
- _callback = partial(self._on_func_matched, func)
143
- nampa.match_function(flirt, func_bytes, start, _callback)
144
-
145
- def _on_func_matched(self, func: Function, base_addr: int, flirt_func: nampa.FlirtFunction):
129
+ flirt = FlirtSignatureParsed.parse(sigfile)
130
+ tolerances = range(self._max_mismatched_bytes + 1)
131
+ for tolerance in tolerances:
132
+ # we iteratively match until we find no new matches
133
+ updated_funcs = set()
134
+ while True:
135
+ matched = False
136
+
137
+ funcs = (
138
+ self.project.kb.functions.values()
139
+ if not updated_funcs
140
+ else {self.project.kb.functions.get_by_addr(a) for a in self._get_caller_funcs(updated_funcs)}
141
+ )
142
+ updated_funcs = set()
143
+
144
+ for func in funcs:
145
+ func: Function
146
+ if func.is_simprocedure or func.is_plt:
147
+ continue
148
+ if func.addr in self._suggestions:
149
+ # we already have a suggestion for this function
150
+ continue
151
+ if not func.is_default_name:
152
+ # it already has a name. skip
153
+ continue
154
+
155
+ # print(tolerance, repr(func))
156
+ start = func.addr
157
+ if self._is_arm:
158
+ start = start & 0xFFFF_FFFE
159
+
160
+ max_block_addr = max(func.block_addrs_set)
161
+ end_block = func.get_block(max_block_addr)
162
+ end = max_block_addr + end_block.size
163
+
164
+ if self._is_arm:
165
+ end = end & 0xFFFF_FFFE
166
+
167
+ # load all bytes
168
+ func_bytes = self.project.loader.memory.load(start, end - start + 0x100)
169
+ matcher = FlirtMatcher(
170
+ flirt,
171
+ func,
172
+ self._get_callee_name,
173
+ self._on_func_matched,
174
+ mismatch_bytes_tolerance=tolerance,
175
+ )
176
+ func_matched = matcher.match_function(func_bytes, start)
177
+ if func_matched:
178
+ updated_funcs.add(func.addr)
179
+ matched |= func_matched
180
+
181
+ if not matched:
182
+ break
183
+
184
+ def _get_caller_funcs(self, update_func_addrs: set[int]) -> set[int]:
185
+ caller_funcs = set()
186
+ for func_addr in update_func_addrs:
187
+ for pred in self.kb.functions.callgraph.predecessors(func_addr):
188
+ if pred != func_addr:
189
+ caller_funcs.add(pred)
190
+ return caller_funcs
191
+
192
+ def _get_callee_name(
193
+ self, func, func_addr: int, call_addr: int, expected_name: str # pylint:disable=unused-argument
194
+ ) -> str | None:
195
+ for block_addr, (call_target, _) in func._call_sites.items():
196
+ block = func.get_block(block_addr)
197
+ call_ins_addr = (
198
+ block.instruction_addrs[-2] if self.project.arch.branch_delay_slot else block.instruction_addrs[-1]
199
+ )
200
+ if block_addr <= call_addr < block_addr + block.size and call_ins_addr <= call_addr:
201
+ if call_target is None or not self.kb.functions.contains_addr(call_target):
202
+ return None
203
+ callee = self.kb.functions.get_by_addr(call_target)
204
+ return callee.name
205
+ return None
206
+
207
+ def _on_func_matched(self, func: Function, base_addr: int, flirt_func: FlirtFunction):
146
208
  func_addr = base_addr + flirt_func.offset
147
209
  _l.debug(
148
210
  "_on_func_matched() is called with func_addr %#x with a suggested name %s.", func_addr, flirt_func.name
149
211
  )
150
- if func_addr != base_addr:
212
+ if func_addr != base_addr and (self._is_arm and func_addr != base_addr + 1):
151
213
  # get the correct function
152
- func = None
153
214
  try:
154
215
  func = self.kb.functions.get_by_addr(func_addr)
155
216
  except KeyError:
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class FlirtFunction:
5
+ """
6
+ Describes a function object in a FLIRT signature.
7
+ """
8
+
9
+ __slots__ = (
10
+ "collision",
11
+ "local",
12
+ "name",
13
+ "offset",
14
+ )
15
+
16
+ def __init__(self, name: str, offset: int, local: bool, collision: bool):
17
+ self.name = name
18
+ self.offset = offset
19
+ self.local = local
20
+ self.collision = collision