angr 9.2.174__cp310-abi3-macosx_11_0_arm64.whl → 9.2.176__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.
- angr/__init__.py +1 -1
- angr/__main__.py +32 -2
- angr/analyses/calling_convention/calling_convention.py +12 -0
- angr/analyses/cfg/cfg_base.py +1 -1
- angr/analyses/cfg/cfg_fast.py +27 -8
- angr/analyses/complete_calling_conventions.py +39 -26
- angr/analyses/decompiler/ail_simplifier.py +13 -11
- angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +5 -1
- angr/analyses/decompiler/clinic.py +54 -40
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +3 -3
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -4
- angr/analyses/decompiler/peephole_optimizations/{inlined_wstrcpy.py → inlined_wcscpy.py} +16 -8
- angr/analyses/decompiler/peephole_optimizations/{inlined_wstrcpy_consolidation.py → inlined_wcscpy_consolidation.py} +13 -13
- angr/analyses/decompiler/ssailification/rewriting_engine.py +14 -1
- angr/analyses/decompiler/structured_codegen/c.py +6 -5
- angr/analyses/decompiler/structuring/dream.py +2 -2
- angr/analyses/decompiler/structuring/phoenix.py +101 -23
- angr/analyses/decompiler/utils.py +1 -1
- angr/analyses/smc.py +1 -1
- angr/analyses/stack_pointer_tracker.py +4 -3
- angr/analyses/typehoon/lifter.py +29 -18
- angr/analyses/typehoon/simple_solver.py +157 -50
- angr/analyses/typehoon/translator.py +34 -34
- angr/analyses/typehoon/typeconsts.py +33 -15
- angr/analyses/typehoon/typevars.py +9 -2
- angr/analyses/variable_recovery/engine_ail.py +4 -2
- angr/analyses/variable_recovery/engine_base.py +4 -1
- angr/analyses/variable_recovery/variable_recovery_fast.py +3 -1
- angr/calling_conventions.py +2 -1
- angr/engines/icicle.py +4 -4
- angr/engines/vex/claripy/ccall.py +3 -3
- angr/knowledge_plugins/functions/function.py +18 -1
- angr/misc/bug_report.py +11 -2
- angr/procedures/definitions/__init__.py +88 -20
- angr/procedures/definitions/common/glibc.json +3516 -0
- angr/procedures/definitions/parse_glibc.py +78 -0
- angr/procedures/libc/fgets.py +2 -1
- angr/procedures/posix/pthread.py +4 -4
- angr/procedures/stubs/format_parser.py +3 -3
- angr/rustylib.abi3.so +0 -0
- angr/sim_type.py +73 -11
- angr/simos/windows.py +1 -1
- angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +1 -1
- angr/unicornlib.dylib +0 -0
- angr/utils/constants.py +1 -1
- angr/utils/library.py +1 -0
- angr/utils/strings.py +20 -0
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/METADATA +5 -5
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/RECORD +54 -52
- angr/procedures/definitions/glibc.py +0 -8372
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/WHEEL +0 -0
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/entry_points.txt +0 -0
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/top_level.txt +0 -0
angr/__init__.py
CHANGED
angr/__main__.py
CHANGED
|
@@ -6,11 +6,14 @@ import re
|
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
from collections.abc import Generator
|
|
8
8
|
|
|
9
|
+
from rich.syntax import Syntax
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
|
|
9
12
|
import angr
|
|
10
13
|
from angr.analyses.decompiler import DECOMPILATION_PRESETS
|
|
11
14
|
from angr.analyses.decompiler.structuring import STRUCTURER_CLASSES, DEFAULT_STRUCTURER
|
|
12
15
|
from angr.analyses.decompiler.utils import decompile_functions
|
|
13
|
-
|
|
16
|
+
from angr.utils.formatting import ansi_color_enabled
|
|
14
17
|
|
|
15
18
|
if TYPE_CHECKING:
|
|
16
19
|
from angr.knowledge_plugins.functions import Function
|
|
@@ -82,11 +85,27 @@ def decompile(args):
|
|
|
82
85
|
base_address=args.base_addr,
|
|
83
86
|
preset=args.preset,
|
|
84
87
|
)
|
|
85
|
-
|
|
88
|
+
|
|
89
|
+
# Determine if we should use syntax highlighting
|
|
90
|
+
should_highlight = ansi_color_enabled and not args.no_colors
|
|
91
|
+
|
|
92
|
+
if should_highlight:
|
|
93
|
+
try:
|
|
94
|
+
console = Console()
|
|
95
|
+
syntax = Syntax(decompilation, "c", theme=args.theme, line_numbers=False)
|
|
96
|
+
console.print(syntax)
|
|
97
|
+
# pylint: disable=broad-exception-caught
|
|
98
|
+
except Exception as e:
|
|
99
|
+
log.warning("Syntax highlighting failed: %s", e)
|
|
100
|
+
# Fall back to plain text if syntax highlighting fails
|
|
101
|
+
print(decompilation)
|
|
102
|
+
else:
|
|
103
|
+
print(decompilation)
|
|
86
104
|
|
|
87
105
|
|
|
88
106
|
def main():
|
|
89
107
|
parser = argparse.ArgumentParser(description="The angr CLI allows you to decompile and analyze binaries.")
|
|
108
|
+
parser.add_argument("--version", action="version", version=angr.__version__)
|
|
90
109
|
parser.add_argument("binary", help="The path to the binary to analyze.")
|
|
91
110
|
parser.add_argument(
|
|
92
111
|
"--catch-exceptions",
|
|
@@ -133,6 +152,17 @@ def main():
|
|
|
133
152
|
symbols of the binary or as addresses like: 0x401000.""",
|
|
134
153
|
nargs="+",
|
|
135
154
|
)
|
|
155
|
+
decompile_cmd_parser.add_argument(
|
|
156
|
+
"--no-colors",
|
|
157
|
+
help="Disable syntax highlighting in the decompiled output.",
|
|
158
|
+
action="store_true",
|
|
159
|
+
default=False,
|
|
160
|
+
)
|
|
161
|
+
decompile_cmd_parser.add_argument(
|
|
162
|
+
"--theme",
|
|
163
|
+
help="The syntax highlighting theme to use (only if rich is installed and colors are enabled).",
|
|
164
|
+
default="dracula",
|
|
165
|
+
)
|
|
136
166
|
|
|
137
167
|
disassemble_cmd_parser = subparsers.add_parser("disassemble", aliases=["dis"], help=disassemble.__doc__)
|
|
138
168
|
disassemble_cmd_parser.set_defaults(func=disassemble)
|
|
@@ -95,6 +95,8 @@ class CallingConventionAnalysis(Analysis):
|
|
|
95
95
|
calling convention and arguments. This can be time-consuming if there are many call
|
|
96
96
|
sites to analyze.
|
|
97
97
|
:ivar cc: The recovered calling convention for the function.
|
|
98
|
+
:ivar _collect_facts: True if we should run FunctionFactCollector to collect input arguments and return
|
|
99
|
+
value size. False if input arguments and return value size are provided by the user.
|
|
98
100
|
"""
|
|
99
101
|
|
|
100
102
|
def __init__(
|
|
@@ -108,6 +110,7 @@ class CallingConventionAnalysis(Analysis):
|
|
|
108
110
|
func_graph: networkx.DiGraph | None = None,
|
|
109
111
|
input_args: list[SimRegArg | SimStackArg] | None = None,
|
|
110
112
|
retval_size: int | None = None,
|
|
113
|
+
collect_facts: bool = False,
|
|
111
114
|
):
|
|
112
115
|
if func is not None and not isinstance(func, Function):
|
|
113
116
|
func = self.kb.functions[func]
|
|
@@ -121,6 +124,7 @@ class CallingConventionAnalysis(Analysis):
|
|
|
121
124
|
self._func_graph = func_graph
|
|
122
125
|
self._input_args = input_args
|
|
123
126
|
self._retval_size = retval_size
|
|
127
|
+
self._collect_facts = collect_facts
|
|
124
128
|
|
|
125
129
|
if self._retval_size is not None and self._input_args is None:
|
|
126
130
|
# retval size will be ignored if input_args is not specified - user error?
|
|
@@ -132,6 +136,7 @@ class CallingConventionAnalysis(Analysis):
|
|
|
132
136
|
self.cc: SimCC | None = None
|
|
133
137
|
self.prototype: SimTypeFunction | None = None
|
|
134
138
|
self.prototype_libname: str | None = None
|
|
139
|
+
self.proto_from_symbol: bool = False
|
|
135
140
|
|
|
136
141
|
if self._cfg is None and "CFGFast" in self.kb.cfgs:
|
|
137
142
|
self._cfg = self.kb.cfgs["CFGFast"]
|
|
@@ -168,6 +173,7 @@ class CallingConventionAnalysis(Analysis):
|
|
|
168
173
|
r_demangled = self._analyze_demangled_name(demangled_name)
|
|
169
174
|
if r_demangled is not None:
|
|
170
175
|
self.cc, self.prototype, self.prototype_libname = r_demangled
|
|
176
|
+
self.proto_from_symbol = True
|
|
171
177
|
return
|
|
172
178
|
|
|
173
179
|
if self._function.is_simprocedure:
|
|
@@ -242,6 +248,12 @@ class CallingConventionAnalysis(Analysis):
|
|
|
242
248
|
self.cc, self.prototype, self.prototype_libname = r_plt
|
|
243
249
|
return
|
|
244
250
|
|
|
251
|
+
# we gotta analyze the function properly
|
|
252
|
+
if self._collect_facts and self._input_args is None and self._retval_size is None:
|
|
253
|
+
facts = self.project.analyses.FunctionFactCollector(self._function, kb=self.kb)
|
|
254
|
+
self._input_args = facts.input_args
|
|
255
|
+
self._retval_size = facts.retval_size
|
|
256
|
+
|
|
245
257
|
r = self._analyze_function()
|
|
246
258
|
if r is None:
|
|
247
259
|
l.warning("Cannot determine calling convention for %r.", self._function)
|
angr/analyses/cfg/cfg_base.py
CHANGED
|
@@ -937,7 +937,7 @@ class CFGBase(Analysis):
|
|
|
937
937
|
"""
|
|
938
938
|
|
|
939
939
|
addrs = set()
|
|
940
|
-
if isinstance(self._binary, ELF) and self._binary.has_dwarf_info:
|
|
940
|
+
if (isinstance(self._binary, ELF) and self._binary.has_dwarf_info) or isinstance(self._binary, PE):
|
|
941
941
|
for function_hint in self._binary.function_hints:
|
|
942
942
|
if function_hint.source == FunctionHintSource.EH_FRAME:
|
|
943
943
|
addrs.add(function_hint.addr)
|
angr/analyses/cfg/cfg_fast.py
CHANGED
|
@@ -435,6 +435,7 @@ class CFGJobType(Enum):
|
|
|
435
435
|
COMPLETE_SCANNING = 2
|
|
436
436
|
IFUNC_HINTS = 3
|
|
437
437
|
DATAREF_HINTS = 4
|
|
438
|
+
EH_FRAME_HINTS = 5
|
|
438
439
|
|
|
439
440
|
|
|
440
441
|
class CFGJob:
|
|
@@ -612,7 +613,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
612
613
|
low_priority=False,
|
|
613
614
|
cfb=None,
|
|
614
615
|
model=None,
|
|
615
|
-
|
|
616
|
+
eh_frame=True,
|
|
616
617
|
exceptions=True,
|
|
617
618
|
skip_unmapped_addrs=True,
|
|
618
619
|
nodecode_window_size=512,
|
|
@@ -625,6 +626,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
625
626
|
end=None, # deprecated
|
|
626
627
|
collect_data_references=None, # deprecated
|
|
627
628
|
extra_cross_references=None, # deprecated
|
|
629
|
+
elf_eh_frame=None, # deprecated
|
|
628
630
|
**extra_arch_options,
|
|
629
631
|
):
|
|
630
632
|
"""
|
|
@@ -664,8 +666,8 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
664
666
|
types will be loaded.
|
|
665
667
|
:param base_state: A state to use as a backer for all memory loads
|
|
666
668
|
:param bool detect_tail_calls: Enable aggressive tail-call optimization detection.
|
|
667
|
-
:param bool
|
|
668
|
-
binaries.
|
|
669
|
+
:param bool eh_frame: Retrieve function starts (and maybe sizes later) from the .eh_frame of ELF
|
|
670
|
+
binaries or exception records of PE binaries.
|
|
669
671
|
:param skip_unmapped_addrs: Ignore all branches into unmapped regions. True by default. You may want to set
|
|
670
672
|
it to False if you are analyzing manually patched binaries or malware samples.
|
|
671
673
|
:param indirect_calls_always_return: Should CFG assume indirect calls must return or not. Assuming indirect
|
|
@@ -778,13 +780,17 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
778
780
|
)
|
|
779
781
|
force_complete_scan = False
|
|
780
782
|
|
|
783
|
+
if elf_eh_frame is not None:
|
|
784
|
+
l.warning('"elf_eh_frame" is deprecated and will be removed soon. Please use "eh_frame" instead.')
|
|
785
|
+
eh_frame = eh_frame or elf_eh_frame
|
|
786
|
+
|
|
781
787
|
self._pickle_intermediate_results = pickle_intermediate_results
|
|
782
788
|
|
|
783
789
|
self._use_symbols = symbols
|
|
784
790
|
self._use_function_prologues = function_prologues
|
|
785
791
|
self._force_smart_scan = force_smart_scan
|
|
786
792
|
self._force_complete_scan = force_complete_scan
|
|
787
|
-
self.
|
|
793
|
+
self._use_eh_frame = eh_frame
|
|
788
794
|
self._use_exceptions = exceptions
|
|
789
795
|
self._check_funcret_max_job = check_funcret_max_job
|
|
790
796
|
|
|
@@ -841,7 +847,8 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
841
847
|
self._read_addr_to_run = defaultdict(list)
|
|
842
848
|
self._write_addr_to_run = defaultdict(list)
|
|
843
849
|
|
|
844
|
-
self.
|
|
850
|
+
self._remaining_eh_frame_addrs: list[int] | None = None
|
|
851
|
+
self._remaining_function_prologue_addrs: list[int] | None = None
|
|
845
852
|
|
|
846
853
|
# exception handling
|
|
847
854
|
self._exception_handling_by_endaddr = SortedDict()
|
|
@@ -1440,9 +1447,6 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1440
1447
|
if self._use_symbols:
|
|
1441
1448
|
starting_points |= self._function_addresses_from_symbols
|
|
1442
1449
|
|
|
1443
|
-
if self._use_elf_eh_frame:
|
|
1444
|
-
starting_points |= self._function_addresses_from_eh_frame
|
|
1445
|
-
|
|
1446
1450
|
if self._extra_function_starts:
|
|
1447
1451
|
starting_points |= set(self._extra_function_starts)
|
|
1448
1452
|
|
|
@@ -1467,6 +1471,9 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1467
1471
|
|
|
1468
1472
|
self._updated_nonreturning_functions = set()
|
|
1469
1473
|
|
|
1474
|
+
if self._use_eh_frame:
|
|
1475
|
+
self._remaining_eh_frame_addrs = sorted(self._function_addresses_from_eh_frame)
|
|
1476
|
+
|
|
1470
1477
|
if self._use_function_prologues and self.project.concrete_target is None:
|
|
1471
1478
|
self._remaining_function_prologue_addrs = sorted(self._func_addrs_from_prologues())
|
|
1472
1479
|
|
|
@@ -1742,6 +1749,18 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1742
1749
|
self._insert_job(job)
|
|
1743
1750
|
return
|
|
1744
1751
|
|
|
1752
|
+
if self._use_eh_frame and self._remaining_eh_frame_addrs:
|
|
1753
|
+
while self._remaining_eh_frame_addrs:
|
|
1754
|
+
eh_addr = self._remaining_eh_frame_addrs[0]
|
|
1755
|
+
self._remaining_eh_frame_addrs = self._remaining_eh_frame_addrs[1:]
|
|
1756
|
+
if self._seg_list.is_occupied(eh_addr):
|
|
1757
|
+
continue
|
|
1758
|
+
|
|
1759
|
+
job = CFGJob(eh_addr, eh_addr, "Ijk_Boring", job_type=CFGJobType.EH_FRAME_HINTS)
|
|
1760
|
+
self._insert_job(job)
|
|
1761
|
+
self._register_analysis_job(eh_addr, job)
|
|
1762
|
+
return
|
|
1763
|
+
|
|
1745
1764
|
if self._use_function_prologues and self._remaining_function_prologue_addrs:
|
|
1746
1765
|
while self._remaining_function_prologue_addrs:
|
|
1747
1766
|
prolog_addr = self._remaining_function_prologue_addrs[0]
|
|
@@ -17,7 +17,7 @@ from angr.utils.graph import GraphUtils
|
|
|
17
17
|
from angr.simos import SimWindows
|
|
18
18
|
from angr.utils.mp import mp_context, Initializer
|
|
19
19
|
from angr.knowledge_plugins.cfg import CFGModel
|
|
20
|
-
from . import Analysis, register_analysis, VariableRecoveryFast, CallingConventionAnalysis,
|
|
20
|
+
from . import Analysis, register_analysis, VariableRecoveryFast, CallingConventionAnalysis, CFGFast
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
23
|
from angr.calling_conventions import SimCC
|
|
@@ -191,10 +191,15 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
191
191
|
self._prioritize_func_addrs = None # no longer useful
|
|
192
192
|
|
|
193
193
|
def _set_function_prototype(
|
|
194
|
-
self,
|
|
194
|
+
self,
|
|
195
|
+
func: Function,
|
|
196
|
+
prototype: SimTypeFunction | None,
|
|
197
|
+
prototype_libname: str | None,
|
|
198
|
+
prototype_guessed: bool | None,
|
|
195
199
|
) -> None:
|
|
196
200
|
if func.prototype is None or func.is_prototype_guessed or self._force:
|
|
197
|
-
|
|
201
|
+
if prototype_guessed is not None:
|
|
202
|
+
func.is_prototype_guessed = prototype_guessed
|
|
198
203
|
func.prototype = prototype
|
|
199
204
|
func.prototype_libname = prototype_libname
|
|
200
205
|
|
|
@@ -204,12 +209,12 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
204
209
|
if self._workers == 0:
|
|
205
210
|
self._update_progress(0)
|
|
206
211
|
for idx, func_addr in enumerate(self._func_addrs):
|
|
207
|
-
cc, proto, proto_libname, _ = self._analyze_core(func_addr)
|
|
212
|
+
cc, proto, proto_libname, proto_guessed, _ = self._analyze_core(func_addr)
|
|
208
213
|
|
|
209
214
|
func = self.kb.functions.get_by_addr(func_addr)
|
|
210
215
|
if cc is not None or proto is not None:
|
|
211
216
|
func.calling_convention = cc
|
|
212
|
-
self._set_function_prototype(func, proto, proto_libname)
|
|
217
|
+
self._set_function_prototype(func, proto, proto_libname, proto_guessed)
|
|
213
218
|
if proto_libname is not None:
|
|
214
219
|
self.prototype_libnames.add(proto_libname)
|
|
215
220
|
|
|
@@ -269,10 +274,13 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
269
274
|
# update progress
|
|
270
275
|
self._update_progress(0)
|
|
271
276
|
idx = 0
|
|
277
|
+
assert self._results_lock is not None
|
|
272
278
|
while idx < total_funcs:
|
|
273
279
|
try:
|
|
274
280
|
with self._results_lock:
|
|
275
|
-
func_addr, cc, proto, proto_libname, varman = self._results.get(
|
|
281
|
+
func_addr, cc, proto, proto_libname, proto_guessed, varman = self._results.get(
|
|
282
|
+
True, timeout=0.01
|
|
283
|
+
)
|
|
276
284
|
except queue.Empty:
|
|
277
285
|
time.sleep(0.1)
|
|
278
286
|
continue
|
|
@@ -280,7 +288,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
280
288
|
func = self.kb.functions.get_by_addr(func_addr)
|
|
281
289
|
if cc is not None or proto is not None:
|
|
282
290
|
func.calling_convention = cc
|
|
283
|
-
self._set_function_prototype(func, proto, proto_libname)
|
|
291
|
+
self._set_function_prototype(func, proto, proto_libname, proto_guessed)
|
|
284
292
|
if proto_libname is not None:
|
|
285
293
|
self.prototype_libnames.add(proto_libname)
|
|
286
294
|
|
|
@@ -317,6 +325,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
317
325
|
def _worker_routine(self, worker_id: int, initializer: Initializer):
|
|
318
326
|
initializer.initialize()
|
|
319
327
|
idx = 0
|
|
328
|
+
assert self._remaining_funcs is not None and self._func_queue is not None and self._results_lock is not None
|
|
320
329
|
while self._remaining_funcs.value > 0:
|
|
321
330
|
try:
|
|
322
331
|
with self._func_queue_lock:
|
|
@@ -327,33 +336,39 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
327
336
|
continue
|
|
328
337
|
|
|
329
338
|
if callee_info is not None:
|
|
330
|
-
callee_info: dict[int, tuple[SimCC | None, SimTypeFunction | None, str | None]]
|
|
331
|
-
for callee, (
|
|
339
|
+
callee_info: dict[int, tuple[SimCC | None, SimTypeFunction | None, str | None, bool | None]]
|
|
340
|
+
for callee, (
|
|
341
|
+
callee_cc,
|
|
342
|
+
callee_proto,
|
|
343
|
+
callee_proto_libname,
|
|
344
|
+
callee_proto_guessed,
|
|
345
|
+
) in callee_info.items():
|
|
332
346
|
callee_func = self.kb.functions.get_by_addr(callee)
|
|
333
347
|
callee_func.calling_convention = callee_cc
|
|
334
|
-
self._set_function_prototype(callee_func, callee_proto, callee_proto_libname)
|
|
348
|
+
self._set_function_prototype(callee_func, callee_proto, callee_proto_libname, callee_proto_guessed)
|
|
335
349
|
|
|
336
350
|
idx += 1
|
|
337
351
|
if self._low_priority and idx % 3 == 0:
|
|
338
352
|
time.sleep(0.1)
|
|
339
353
|
|
|
340
354
|
try:
|
|
341
|
-
cc, proto, proto_libname, varman = self._analyze_core(func_addr)
|
|
355
|
+
cc, proto, proto_libname, proto_guessed, varman = self._analyze_core(func_addr)
|
|
342
356
|
except Exception: # pylint:disable=broad-except
|
|
343
357
|
_l.error("Worker %d: Exception occurred during _analyze_core().", worker_id, exc_info=True)
|
|
344
|
-
cc, proto, proto_libname, varman = None, None, None, None
|
|
358
|
+
cc, proto, proto_libname, proto_guessed, varman = None, None, None, None, None
|
|
345
359
|
with self._results_lock:
|
|
346
|
-
self._results.put((func_addr, cc, proto, proto_libname, varman))
|
|
360
|
+
self._results.put((func_addr, cc, proto, proto_libname, proto_guessed, varman))
|
|
347
361
|
|
|
348
362
|
def _analyze_core(
|
|
349
363
|
self, func_addr: int
|
|
350
|
-
) -> tuple[SimCC | None, SimTypeFunction | None, str | None, VariableManagerInternal | None]:
|
|
364
|
+
) -> tuple[SimCC | None, SimTypeFunction | None, str | None, bool | None, VariableManagerInternal | None]:
|
|
351
365
|
func = self.kb.functions.get_by_addr(func_addr)
|
|
352
366
|
if func.ran_cca:
|
|
353
367
|
return (
|
|
354
368
|
func.calling_convention,
|
|
355
369
|
func.prototype,
|
|
356
370
|
func.prototype_libname,
|
|
371
|
+
func.is_prototype_guessed,
|
|
357
372
|
self.kb.variables.get_function_manager(func_addr),
|
|
358
373
|
)
|
|
359
374
|
|
|
@@ -365,7 +380,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
365
380
|
# special case: we don't have a PCode-engine variable recovery analysis for PCode architectures!
|
|
366
381
|
if ":" in self.project.arch.name and self._func_graphs and func.addr in self._func_graphs:
|
|
367
382
|
# this is a pcode architecture
|
|
368
|
-
return None, None, None, None
|
|
383
|
+
return None, None, None, None, None
|
|
369
384
|
|
|
370
385
|
_l.info("Performing variable recovery on %r...", func)
|
|
371
386
|
try:
|
|
@@ -378,17 +393,14 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
378
393
|
func.addr,
|
|
379
394
|
exc_info=True,
|
|
380
395
|
)
|
|
381
|
-
return None, None, None, None
|
|
382
|
-
|
|
383
|
-
kwargs = {}
|
|
384
|
-
if self.mode == CallingConventionAnalysisMode.FAST:
|
|
385
|
-
facts = self.project.analyses[FactCollector].prep(kb=self.kb)(func)
|
|
386
|
-
kwargs["input_args"] = facts.input_args
|
|
387
|
-
kwargs["retval_size"] = facts.retval_size
|
|
396
|
+
return None, None, None, None, None
|
|
388
397
|
|
|
389
398
|
# determine the calling convention of each function
|
|
390
399
|
cc_analysis = self.project.analyses[CallingConventionAnalysis].prep(kb=self.kb)(
|
|
391
|
-
func,
|
|
400
|
+
func,
|
|
401
|
+
cfg=self._cfg,
|
|
402
|
+
analyze_callsites=self._analyze_callsites,
|
|
403
|
+
collect_facts=self.mode == CallingConventionAnalysisMode.FAST,
|
|
392
404
|
)
|
|
393
405
|
|
|
394
406
|
if cc_analysis.cc is not None:
|
|
@@ -397,10 +409,11 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
397
409
|
cc_analysis.cc,
|
|
398
410
|
cc_analysis.prototype,
|
|
399
411
|
cc_analysis.prototype_libname if cc_analysis.prototype_libname is not None else func.prototype_libname,
|
|
412
|
+
not cc_analysis.proto_from_symbol,
|
|
400
413
|
self.kb.variables.get_function_manager(func_addr),
|
|
401
414
|
)
|
|
402
415
|
_l.info("Cannot determine calling convention for %r.", func)
|
|
403
|
-
return None, None, None, self.kb.variables.get_function_manager(func_addr)
|
|
416
|
+
return None, None, None, None, self.kb.variables.get_function_manager(func_addr)
|
|
404
417
|
|
|
405
418
|
def prioritize_functions(self, func_addrs_to_prioritize: Iterable[int]):
|
|
406
419
|
"""
|
|
@@ -424,12 +437,12 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
424
437
|
|
|
425
438
|
def _get_callees_cc_prototypes(
|
|
426
439
|
self, caller_func_addr: int
|
|
427
|
-
) -> dict[int, tuple[SimCC | None, SimTypeFunction | None, str | None]]:
|
|
440
|
+
) -> dict[int, tuple[SimCC | None, SimTypeFunction | None, str | None, bool | None]]:
|
|
428
441
|
d = {}
|
|
429
442
|
for callee in self.kb.functions.callgraph.successors(caller_func_addr):
|
|
430
443
|
if callee != caller_func_addr and callee not in d:
|
|
431
444
|
func = self.kb.functions.get_by_addr(callee)
|
|
432
|
-
tpl = func.calling_convention, func.prototype, func.prototype_libname
|
|
445
|
+
tpl = func.calling_convention, func.prototype, func.prototype_libname, func.is_prototype_guessed
|
|
433
446
|
d[callee] = tpl
|
|
434
447
|
return d
|
|
435
448
|
|
|
@@ -168,6 +168,7 @@ class AILSimplifier(Analysis):
|
|
|
168
168
|
fold_callexprs_into_conditions=False,
|
|
169
169
|
use_callee_saved_regs_at_return=True,
|
|
170
170
|
rewrite_ccalls=True,
|
|
171
|
+
rename_ccalls=True,
|
|
171
172
|
removed_vvar_ids: set[int] | None = None,
|
|
172
173
|
arg_vvars: dict[int, tuple[VirtualVariable, SimVariable]] | None = None,
|
|
173
174
|
avoid_vvar_ids: set[int] | None = None,
|
|
@@ -188,6 +189,7 @@ class AILSimplifier(Analysis):
|
|
|
188
189
|
self._fold_callexprs_into_conditions = fold_callexprs_into_conditions
|
|
189
190
|
self._use_callee_saved_regs_at_return = use_callee_saved_regs_at_return
|
|
190
191
|
self._should_rewrite_ccalls = rewrite_ccalls
|
|
192
|
+
self._should_rename_ccalls = rename_ccalls
|
|
191
193
|
self._removed_vvar_ids = removed_vvar_ids if removed_vvar_ids is not None else set()
|
|
192
194
|
self._arg_vvars = arg_vvars
|
|
193
195
|
self._avoid_vvar_ids = avoid_vvar_ids if avoid_vvar_ids is not None else set()
|
|
@@ -1992,20 +1994,20 @@ class AILSimplifier(Analysis):
|
|
|
1992
1994
|
|
|
1993
1995
|
v = False
|
|
1994
1996
|
|
|
1995
|
-
def
|
|
1996
|
-
expr_idx: int, expr:
|
|
1997
|
+
def _handle_VEXCCallExpression(
|
|
1998
|
+
expr_idx: int, expr: VEXCCallExpression, stmt_idx: int, stmt: Statement, block: Block | None
|
|
1997
1999
|
) -> Expression | None:
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
return
|
|
2000
|
+
r_expr = AILBlockWalker._handle_VEXCCallExpression(walker, expr_idx, expr, stmt_idx, stmt, block)
|
|
2001
|
+
if r_expr is None:
|
|
2002
|
+
r_expr = expr
|
|
2003
|
+
rewriter = rewriter_cls(r_expr, self.project.arch, rename_ccalls=self._should_rename_ccalls)
|
|
2004
|
+
if rewriter.result is not None:
|
|
2005
|
+
_any_update.v = True
|
|
2006
|
+
return rewriter.result
|
|
2007
|
+
return r_expr if r_expr is not expr else None
|
|
2006
2008
|
|
|
2007
2009
|
blocks_by_addr_and_idx = {(node.addr, node.idx): node for node in self.func_graph.nodes()}
|
|
2008
|
-
walker.
|
|
2010
|
+
walker.expr_handlers[VEXCCallExpression] = _handle_VEXCCallExpression
|
|
2009
2011
|
|
|
2010
2012
|
updated = False
|
|
2011
2013
|
for block in blocks_by_addr_and_idx.values():
|
|
@@ -12,9 +12,13 @@ class CCallRewriterBase:
|
|
|
12
12
|
"result",
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
def __init__(self, ccall: ailment.Expr.VEXCCallExpression, arch):
|
|
15
|
+
def __init__(self, ccall: ailment.Expr.VEXCCallExpression, arch, rename_ccalls: bool = False):
|
|
16
16
|
self.arch = arch
|
|
17
17
|
self.result: ailment.Expr.Expression | None = self._rewrite(ccall)
|
|
18
|
+
if rename_ccalls and self.result is None and ccall.callee != "_ccall":
|
|
19
|
+
renamed = ccall.copy()
|
|
20
|
+
renamed.callee = "_ccall"
|
|
21
|
+
self.result = renamed
|
|
18
22
|
|
|
19
23
|
def _rewrite(self, ccall: ailment.Expr.VEXCCallExpression) -> ailment.Expr.Expression | None:
|
|
20
24
|
raise NotImplementedError
|
|
@@ -1231,52 +1231,62 @@ class Clinic(Analysis):
|
|
|
1231
1231
|
@timethis
|
|
1232
1232
|
def _replace_tail_jumps_with_calls(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
1233
1233
|
"""
|
|
1234
|
-
|
|
1234
|
+
Rewrite tail jumps to functions as call statements.
|
|
1235
1235
|
"""
|
|
1236
1236
|
for block in list(ail_graph.nodes()):
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
if out_degree != 0:
|
|
1237
|
+
if ail_graph.out_degree[block] > 1:
|
|
1240
1238
|
continue
|
|
1241
1239
|
|
|
1242
1240
|
last_stmt = block.statements[-1]
|
|
1243
1241
|
if isinstance(last_stmt, ailment.Stmt.Jump):
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1242
|
+
targets = [last_stmt.target]
|
|
1243
|
+
replace_last_stmt = True
|
|
1244
|
+
elif isinstance(last_stmt, ailment.Stmt.ConditionalJump):
|
|
1245
|
+
targets = [last_stmt.true_target, last_stmt.false_target]
|
|
1246
|
+
replace_last_stmt = False
|
|
1247
|
+
else:
|
|
1248
|
+
continue
|
|
1249
|
+
|
|
1250
|
+
for target in targets:
|
|
1251
|
+
if not isinstance(target, ailment.Const) or not self.kb.functions.contains_addr(target.value):
|
|
1252
|
+
continue
|
|
1253
|
+
|
|
1254
|
+
target_func = self.kb.functions.get_by_addr(target.value)
|
|
1255
|
+
|
|
1256
|
+
ret_reg_offset = self.project.arch.ret_offset
|
|
1257
|
+
if target_func.returning and ret_reg_offset is not None:
|
|
1258
|
+
ret_expr = ailment.Expr.Register(
|
|
1259
|
+
None,
|
|
1260
|
+
None,
|
|
1261
|
+
ret_reg_offset,
|
|
1262
|
+
self.project.arch.bits,
|
|
1263
|
+
reg_name=self.project.arch.translate_register_name(ret_reg_offset, size=self.project.arch.bits),
|
|
1264
|
+
**target.tags,
|
|
1265
|
+
)
|
|
1266
|
+
else:
|
|
1267
|
+
ret_expr = None
|
|
1268
|
+
|
|
1269
|
+
call_stmt = ailment.Stmt.Call(
|
|
1270
|
+
None,
|
|
1271
|
+
target.copy(),
|
|
1272
|
+
calling_convention=None, # target_func.calling_convention,
|
|
1273
|
+
prototype=None, # target_func.prototype,
|
|
1274
|
+
ret_expr=ret_expr,
|
|
1275
|
+
**last_stmt.tags,
|
|
1276
|
+
)
|
|
1277
|
+
|
|
1278
|
+
if replace_last_stmt:
|
|
1279
|
+
call_block = block
|
|
1280
|
+
block.statements[-1] = call_stmt
|
|
1281
|
+
else:
|
|
1282
|
+
call_block = ailment.Block(self.new_block_addr(), 1, statements=[call_stmt])
|
|
1283
|
+
ail_graph.add_edge(block, call_block)
|
|
1284
|
+
target.value = call_block.addr
|
|
1285
|
+
|
|
1286
|
+
if target_func.returning:
|
|
1287
|
+
ret_stmt = ailment.Stmt.Return(None, [], **last_stmt.tags)
|
|
1288
|
+
ret_block = ailment.Block(self.new_block_addr(), 1, statements=[ret_stmt])
|
|
1289
|
+
ail_graph.add_edge(call_block, ret_block, type="fake_return")
|
|
1280
1290
|
|
|
1281
1291
|
return ail_graph
|
|
1282
1292
|
|
|
@@ -1443,6 +1453,7 @@ class Clinic(Analysis):
|
|
|
1443
1453
|
only_consts=False,
|
|
1444
1454
|
fold_callexprs_into_conditions=False,
|
|
1445
1455
|
rewrite_ccalls=True,
|
|
1456
|
+
rename_ccalls=True,
|
|
1446
1457
|
removed_vvar_ids: set[int] | None = None,
|
|
1447
1458
|
arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None,
|
|
1448
1459
|
preserve_vvar_ids: set[int] | None = None,
|
|
@@ -1462,6 +1473,7 @@ class Clinic(Analysis):
|
|
|
1462
1473
|
only_consts=only_consts,
|
|
1463
1474
|
fold_callexprs_into_conditions=fold_callexprs_into_conditions,
|
|
1464
1475
|
rewrite_ccalls=rewrite_ccalls,
|
|
1476
|
+
rename_ccalls=rename_ccalls,
|
|
1465
1477
|
removed_vvar_ids=removed_vvar_ids,
|
|
1466
1478
|
arg_vvars=arg_vvars,
|
|
1467
1479
|
preserve_vvar_ids=preserve_vvar_ids,
|
|
@@ -1480,6 +1492,7 @@ class Clinic(Analysis):
|
|
|
1480
1492
|
only_consts=False,
|
|
1481
1493
|
fold_callexprs_into_conditions=False,
|
|
1482
1494
|
rewrite_ccalls=True,
|
|
1495
|
+
rename_ccalls=True,
|
|
1483
1496
|
removed_vvar_ids: set[int] | None = None,
|
|
1484
1497
|
arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None,
|
|
1485
1498
|
preserve_vvar_ids: set[int] | None = None,
|
|
@@ -1504,6 +1517,7 @@ class Clinic(Analysis):
|
|
|
1504
1517
|
fold_callexprs_into_conditions=fold_callexprs_into_conditions,
|
|
1505
1518
|
use_callee_saved_regs_at_return=not self._register_save_areas_removed,
|
|
1506
1519
|
rewrite_ccalls=rewrite_ccalls,
|
|
1520
|
+
rename_ccalls=rename_ccalls,
|
|
1507
1521
|
removed_vvar_ids=removed_vvar_ids,
|
|
1508
1522
|
arg_vvars=arg_vvars,
|
|
1509
1523
|
secondary_stackvars=self.secondary_stackvars,
|
|
@@ -296,7 +296,7 @@ class ITERegionConverter(OptimizationPass):
|
|
|
296
296
|
)
|
|
297
297
|
|
|
298
298
|
if len(new_src_and_vvars) == 1:
|
|
299
|
-
|
|
299
|
+
new_stmt = Assignment(
|
|
300
300
|
stmt.idx,
|
|
301
301
|
stmt.dst,
|
|
302
302
|
new_src_and_vvars[0][1],
|
|
@@ -309,13 +309,13 @@ class ITERegionConverter(OptimizationPass):
|
|
|
309
309
|
new_src_and_vvars,
|
|
310
310
|
**stmt.src.tags,
|
|
311
311
|
)
|
|
312
|
-
|
|
312
|
+
new_stmt = Assignment(
|
|
313
313
|
stmt.idx,
|
|
314
314
|
stmt.dst,
|
|
315
315
|
new_phi,
|
|
316
316
|
**stmt.tags,
|
|
317
317
|
)
|
|
318
|
-
stmts.append(
|
|
318
|
+
stmts.append(new_stmt)
|
|
319
319
|
new_region_tail = Block(region_tail.addr, region_tail.original_size, statements=stmts, idx=region_tail.idx)
|
|
320
320
|
|
|
321
321
|
#
|
|
@@ -864,7 +864,7 @@ class LoweredSwitchSimplifier(StructuringOptimizationPass):
|
|
|
864
864
|
next_node_addr = last_block.addr
|
|
865
865
|
|
|
866
866
|
while next_node_addr is not None and next_node_addr in ca_others:
|
|
867
|
-
onode,
|
|
867
|
+
onode, _value, target, target_idx, next_node_addr = ca_others[next_node_addr]
|
|
868
868
|
onode: Block
|
|
869
869
|
|
|
870
870
|
if first_nonlabel_nonphi_statement(onode) is not onode.statements[-1]:
|
|
@@ -893,7 +893,7 @@ class LoweredSwitchSimplifier(StructuringOptimizationPass):
|
|
|
893
893
|
|
|
894
894
|
# default nodes
|
|
895
895
|
if ca_default:
|
|
896
|
-
onode,
|
|
896
|
+
onode, _value, target, target_idx, _ = ca_default[0]
|
|
897
897
|
default_target = next(
|
|
898
898
|
iter(
|
|
899
899
|
nn
|