angr 9.2.145__py3-none-manylinux2014_aarch64.whl → 9.2.147__py3-none-manylinux2014_aarch64.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.
- angr/__init__.py +1 -1
- angr/analyses/bindiff.py +343 -68
- angr/analyses/cfg/cfg_arch_options.py +10 -0
- angr/analyses/cfg/cfg_base.py +39 -15
- angr/analyses/cfg/cfg_fast.py +19 -3
- angr/analyses/decompiler/optimization_passes/engine_base.py +9 -1
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +11 -0
- angr/analyses/decompiler/ssailification/ssailification.py +1 -1
- angr/analyses/flirt/__init__.py +47 -0
- angr/analyses/flirt/consts.py +160 -0
- angr/analyses/{flirt.py → flirt/flirt.py} +99 -38
- angr/analyses/flirt/flirt_function.py +20 -0
- angr/analyses/flirt/flirt_matcher.py +351 -0
- angr/analyses/flirt/flirt_module.py +32 -0
- angr/analyses/flirt/flirt_node.py +23 -0
- angr/analyses/flirt/flirt_sig.py +356 -0
- angr/analyses/flirt/flirt_utils.py +31 -0
- angr/analyses/stack_pointer_tracker.py +34 -0
- angr/analyses/typehoon/lifter.py +11 -1
- angr/analyses/typehoon/simple_solver.py +9 -0
- angr/analyses/typehoon/translator.py +16 -0
- angr/analyses/typehoon/typeconsts.py +8 -8
- angr/analyses/vfg.py +1 -1
- angr/block.py +6 -6
- angr/engines/vex/heavy/concretizers.py +10 -0
- angr/flirt/__init__.py +15 -44
- angr/knowledge_plugins/functions/function.py +4 -4
- {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/METADATA +6 -7
- {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/RECORD +33 -25
- {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/WHEEL +1 -1
- {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/LICENSE +0 -0
- {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/entry_points.txt +0 -0
- {angr-9.2.145.dist-info → angr-9.2.147.dist-info}/top_level.txt +0 -0
angr/analyses/cfg/cfg_fast.py
CHANGED
|
@@ -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
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
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
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
|
13
|
-
from .
|
|
14
|
-
import
|
|
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
|
-
|
|
47
|
-
|
|
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]) ->
|
|
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 =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|