angr 9.2.145__py3-none-manylinux2014_x86_64.whl → 9.2.147__py3-none-manylinux2014_x86_64.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 (33) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/bindiff.py +343 -68
  3. angr/analyses/cfg/cfg_arch_options.py +10 -0
  4. angr/analyses/cfg/cfg_base.py +39 -15
  5. angr/analyses/cfg/cfg_fast.py +19 -3
  6. angr/analyses/decompiler/optimization_passes/engine_base.py +9 -1
  7. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +11 -0
  8. angr/analyses/decompiler/ssailification/ssailification.py +1 -1
  9. angr/analyses/flirt/__init__.py +47 -0
  10. angr/analyses/flirt/consts.py +160 -0
  11. angr/analyses/{flirt.py → flirt/flirt.py} +99 -38
  12. angr/analyses/flirt/flirt_function.py +20 -0
  13. angr/analyses/flirt/flirt_matcher.py +351 -0
  14. angr/analyses/flirt/flirt_module.py +32 -0
  15. angr/analyses/flirt/flirt_node.py +23 -0
  16. angr/analyses/flirt/flirt_sig.py +356 -0
  17. angr/analyses/flirt/flirt_utils.py +31 -0
  18. angr/analyses/stack_pointer_tracker.py +34 -0
  19. angr/analyses/typehoon/lifter.py +11 -1
  20. angr/analyses/typehoon/simple_solver.py +9 -0
  21. angr/analyses/typehoon/translator.py +16 -0
  22. angr/analyses/typehoon/typeconsts.py +8 -8
  23. angr/analyses/vfg.py +1 -1
  24. angr/block.py +6 -6
  25. angr/engines/vex/heavy/concretizers.py +10 -0
  26. angr/flirt/__init__.py +15 -44
  27. angr/knowledge_plugins/functions/function.py +4 -4
  28. {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/METADATA +6 -7
  29. {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/RECORD +33 -25
  30. {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/WHEEL +1 -1
  31. {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/LICENSE +0 -0
  32. {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/entry_points.txt +0 -0
  33. {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/top_level.txt +0 -0
@@ -1649,6 +1649,13 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1649
1649
  if addr is not None:
1650
1650
  # if this is ARM and addr % 4 != 0, it has to be THUMB
1651
1651
  if is_arm_arch(self.project.arch):
1652
+ if (
1653
+ "has_arm_code" in self._arch_options
1654
+ and self._arch_options["has_arm_code"] is False
1655
+ and addr % 2 == 0
1656
+ ):
1657
+ addr |= 1
1658
+
1652
1659
  if addr % 2 == 0 and addr % 4 != 0:
1653
1660
  # it's not aligned by 4, so it's definitely not ARM mode
1654
1661
  addr |= 1
@@ -1968,9 +1975,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1968
1975
 
1969
1976
  # Pre-compile all regexes
1970
1977
  regexes = []
1971
- for ins_regex in self.project.arch.function_prologs:
1972
- r = re.compile(ins_regex)
1973
- regexes.append(r)
1978
+ if "has_arm_code" not in self._arch_options or self._arch_options["has_arm_code"]:
1979
+ for ins_regex in self.project.arch.function_prologs:
1980
+ r = re.compile(ins_regex)
1981
+ regexes.append(r)
1974
1982
  # EDG says: I challenge anyone bothering to read this to come up with a better
1975
1983
  # way to handle CPU modes that affect instruction decoding.
1976
1984
  # Since the only one we care about is ARM/Thumb right now
@@ -2832,6 +2840,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
2832
2840
  and not self._seg_list.is_occupied(v)
2833
2841
  and v % self.project.arch.instruction_alignment == 0
2834
2842
  ):
2843
+ if is_arm_arch(self.project.arch) and not self._arch_options.has_arm_code and v % 2 != 1:
2844
+ # no ARM code in this binary!
2845
+ return
2846
+
2835
2847
  # create a new CFG job
2836
2848
  ce = CFGJob(
2837
2849
  v,
@@ -4430,6 +4442,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4430
4442
  self._cascading_remove_lifted_blocks(cfg_job.src_node.addr & 0xFFFF_FFFE)
4431
4443
  return None, None, None, None
4432
4444
 
4445
+ if not self._arch_options.has_arm_code and addr % 2 == 0:
4446
+ # No ARM code for this architecture!
4447
+ return None, None, None, None
4448
+
4433
4449
  initial_regs = self._get_initial_registers(addr, cfg_job, current_function_addr)
4434
4450
 
4435
4451
  # Let's try to create the pyvex IRSB directly, since it's much faster
@@ -312,7 +312,15 @@ class SimplifierAILEngine(
312
312
  return expr
313
313
 
314
314
  def _handle_expr_ITE(self, expr):
315
- return expr
315
+ return ailment.expression.ITE(
316
+ expr.idx,
317
+ self._expr(expr.cond),
318
+ self._expr(expr.iffalse),
319
+ self._expr(expr.iftrue),
320
+ variable=expr.variable,
321
+ variable_offset=expr.variable_offset,
322
+ **expr.tags,
323
+ )
316
324
 
317
325
  def _handle_expr_Call(self, expr):
318
326
  return expr
@@ -222,6 +222,17 @@ class EagerEvaluation(PeepholeOptimizationExprBase):
222
222
  )
223
223
  return BinaryOp(expr.idx, "Div", (mul, new_const_0), expr.signed, bits=expr.bits, **expr.tags)
224
224
 
225
+ elif expr.op == "Mod":
226
+ op0, op1 = expr.operands
227
+ if (
228
+ isinstance(op0, Const)
229
+ and isinstance(op0.value, int)
230
+ and isinstance(op1, Const)
231
+ and isinstance(op1.value, int)
232
+ and op1.value != 0
233
+ ):
234
+ return Const(expr.idx, None, op0.value % op1.value, expr.bits, **expr.tags)
235
+
225
236
  elif expr.op in {"Shr", "Sar"} and isinstance(expr.operands[1], Const):
226
237
  expr0, expr1 = expr.operands
227
238
  if isinstance(expr0, BinaryOp) and expr0.op == "Shr" and isinstance(expr0.operands[1], Const):
@@ -145,7 +145,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
145
145
  stackvar_locs = {}
146
146
  sorted_stackvar_offs = []
147
147
 
148
- # computer phi node locations for each unified definition
148
+ # compute phi node locations for each unified definition
149
149
  udef_to_defs = defaultdict(set)
150
150
  udef_to_blockkeys = defaultdict(set)
151
151
  for def_, loc in def_to_loc:
@@ -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