angr 9.2.177__cp310-abi3-win_amd64.whl → 9.2.179__cp310-abi3-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.
- angr/__init__.py +1 -1
- angr/analyses/cfg/cfb.py +11 -0
- angr/analyses/cfg/cfg_fast.py +15 -0
- angr/analyses/decompiler/ail_simplifier.py +69 -1
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +45 -7
- angr/analyses/decompiler/clinic.py +24 -10
- angr/analyses/decompiler/dirty_rewriters/__init__.py +7 -0
- angr/analyses/decompiler/dirty_rewriters/amd64_dirty.py +69 -0
- angr/analyses/decompiler/dirty_rewriters/rewriter_base.py +27 -0
- angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +10 -8
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +44 -6
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier_adv.py +198 -0
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +111 -55
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts_around_comparators.py +72 -1
- angr/analyses/decompiler/presets/basic.py +2 -0
- angr/analyses/decompiler/presets/fast.py +2 -0
- angr/analyses/decompiler/presets/full.py +2 -0
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +38 -18
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +10 -4
- angr/analyses/decompiler/structured_codegen/c.py +54 -12
- angr/analyses/decompiler/structuring/phoenix.py +129 -64
- angr/analyses/decompiler/utils.py +26 -8
- angr/analyses/disassembly.py +108 -52
- angr/analyses/proximity_graph.py +20 -19
- angr/analyses/s_propagator.py +23 -21
- angr/analyses/smc.py +2 -3
- angr/flirt/__init__.py +69 -42
- angr/knowledge_plugins/key_definitions/live_definitions.py +2 -1
- angr/knowledge_plugins/labels.py +4 -4
- angr/rustylib.pyd +0 -0
- angr/unicornlib.dll +0 -0
- angr/utils/funcid.py +85 -0
- angr/utils/ssa/__init__.py +2 -6
- angr/utils/types.py +2 -0
- {angr-9.2.177.dist-info → angr-9.2.179.dist-info}/METADATA +9 -8
- {angr-9.2.177.dist-info → angr-9.2.179.dist-info}/RECORD +41 -37
- {angr-9.2.177.dist-info → angr-9.2.179.dist-info}/WHEEL +0 -0
- {angr-9.2.177.dist-info → angr-9.2.179.dist-info}/entry_points.txt +0 -0
- {angr-9.2.177.dist-info → angr-9.2.179.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.177.dist-info → angr-9.2.179.dist-info}/top_level.txt +0 -0
angr/analyses/disassembly.py
CHANGED
|
@@ -34,7 +34,7 @@ class DisassemblyPiece:
|
|
|
34
34
|
addr = None
|
|
35
35
|
ident = float("nan")
|
|
36
36
|
|
|
37
|
-
def render(self, formatting=None):
|
|
37
|
+
def render(self, formatting=None) -> list[str]:
|
|
38
38
|
x = self._render(formatting)
|
|
39
39
|
if len(x) == 1:
|
|
40
40
|
return [self.highlight(x[0], formatting)]
|
|
@@ -73,7 +73,7 @@ class DisassemblyPiece:
|
|
|
73
73
|
pass
|
|
74
74
|
return string
|
|
75
75
|
|
|
76
|
-
def __eq__(self, other):
|
|
76
|
+
def __eq__(self, other) -> bool:
|
|
77
77
|
return False
|
|
78
78
|
|
|
79
79
|
|
|
@@ -175,7 +175,7 @@ class Instruction(DisassemblyPiece):
|
|
|
175
175
|
self.arch = self.project.arch
|
|
176
176
|
self.format = ""
|
|
177
177
|
self.components = ()
|
|
178
|
-
self.opcode = None
|
|
178
|
+
self.opcode: Opcode = None # type:ignore
|
|
179
179
|
self.operands = []
|
|
180
180
|
|
|
181
181
|
# the following members will be filled in after dissecting the instruction
|
|
@@ -261,7 +261,11 @@ class Instruction(DisassemblyPiece):
|
|
|
261
261
|
if i > 0 and opr_pieces[i - 1] == "-":
|
|
262
262
|
v = -v
|
|
263
263
|
cur_operand.pop()
|
|
264
|
-
|
|
264
|
+
with_pound_sign = False
|
|
265
|
+
if i > 0 and opr_pieces[i - 1] == "#":
|
|
266
|
+
with_pound_sign = True
|
|
267
|
+
cur_operand.pop()
|
|
268
|
+
cur_operand.append(Value(v, with_sign, render_with_pound_sign=with_pound_sign))
|
|
265
269
|
elif p[0].isalpha() and p in self.arch.registers:
|
|
266
270
|
cur_operand.append(Register(p))
|
|
267
271
|
else:
|
|
@@ -592,7 +596,7 @@ class Opcode(DisassemblyPiece):
|
|
|
592
596
|
class Operand(DisassemblyPiece):
|
|
593
597
|
def __init__(self, op_num, children, parentinsn):
|
|
594
598
|
self.addr = parentinsn.addr
|
|
595
|
-
self.children = children
|
|
599
|
+
self.children: list = children
|
|
596
600
|
self.parentinsn = parentinsn
|
|
597
601
|
self.op_num = op_num
|
|
598
602
|
self.ident = (self.addr, "operand", self.op_num)
|
|
@@ -639,14 +643,33 @@ class Operand(DisassemblyPiece):
|
|
|
639
643
|
operand = cls(op_num, children, parentinsn)
|
|
640
644
|
|
|
641
645
|
# Post-processing
|
|
642
|
-
if cls is MemoryOperand
|
|
646
|
+
if cls is MemoryOperand:
|
|
647
|
+
operand = Operand._post_process_memory_operand(parentinsn, operand)
|
|
648
|
+
|
|
649
|
+
return operand
|
|
650
|
+
|
|
651
|
+
@staticmethod
|
|
652
|
+
def _post_process_memory_operand(parentinsn: Instruction, operand: MemoryOperand) -> Operand:
|
|
653
|
+
archname = parentinsn.arch.name
|
|
654
|
+
if archname in {"AMD64", "ARM", "ARMEL", "ARMHF", "ARMCortexM"} and len(operand.values) == 2:
|
|
643
655
|
op0, op1 = operand.values
|
|
644
656
|
if type(op0) is Register and op0.is_ip and type(op1) is Value:
|
|
645
|
-
# Indirect addressing in
|
|
657
|
+
# Indirect addressing in x86-64 and ARM
|
|
646
658
|
# 400520 push [rip+0x200782] ==> 400520 push [0x600ca8]
|
|
647
|
-
|
|
659
|
+
# 80410e6 ldr r1, [pc, #0x98] ==> 80410e6 ldr r1, [0x8041182]
|
|
660
|
+
match archname:
|
|
661
|
+
case "AMD64":
|
|
662
|
+
absolute_addr = parentinsn.addr + parentinsn.size + op1.val
|
|
663
|
+
case "ARM" | "ARMEL" | "ARMHF" | "ARMCortexM":
|
|
664
|
+
if parentinsn.addr & 1:
|
|
665
|
+
# thumb mode
|
|
666
|
+
absolute_addr = (parentinsn.addr & 0xFFFF_FFFE) + 4 + op1.val
|
|
667
|
+
else:
|
|
668
|
+
# arm mode
|
|
669
|
+
absolute_addr = parentinsn.addr + 8 + op1.val
|
|
670
|
+
case _:
|
|
671
|
+
raise AngrTypeError(f"Unsupported architecture {archname} for post-processing memory operand.")
|
|
648
672
|
return MemoryOperand(1, [*operand.prefix, "[", Value(absolute_addr, False), "]"], parentinsn)
|
|
649
|
-
|
|
650
673
|
return operand
|
|
651
674
|
|
|
652
675
|
|
|
@@ -680,13 +703,15 @@ class MemoryOperand(Operand):
|
|
|
680
703
|
# [ '[', Register, ']' ]
|
|
681
704
|
# or
|
|
682
705
|
# [ Value, '(', Register, ')' ]
|
|
706
|
+
# or
|
|
707
|
+
# [ Register, ",", Value]
|
|
683
708
|
|
|
684
709
|
# it will be converted into more meaningful and Pythonic properties
|
|
685
710
|
|
|
686
711
|
self.segment_selector = None
|
|
687
712
|
self.prefix = []
|
|
688
713
|
self.suffix_str = "" # could be arm pre index mark "!"
|
|
689
|
-
self.values = []
|
|
714
|
+
self.values: list[str | DisassemblyPiece] = []
|
|
690
715
|
self.offset = []
|
|
691
716
|
# offset_location
|
|
692
717
|
# - prefix: -0xff00($gp)
|
|
@@ -698,6 +723,10 @@ class MemoryOperand(Operand):
|
|
|
698
723
|
# - curly: {rax+0x10}
|
|
699
724
|
# - paren: (rax+0x10)
|
|
700
725
|
self.values_style = "square"
|
|
726
|
+
# separator (between register and value)
|
|
727
|
+
# - "": rax+0x10
|
|
728
|
+
# - "comma": r0, #0x10
|
|
729
|
+
self.separator = ""
|
|
701
730
|
|
|
702
731
|
try:
|
|
703
732
|
if "[" in self.children:
|
|
@@ -711,8 +740,8 @@ class MemoryOperand(Operand):
|
|
|
711
740
|
l.error("Failed to parse operand children %s. Please report to Fish.", self.children)
|
|
712
741
|
|
|
713
742
|
# setup all dummy properties
|
|
714
|
-
self.prefix =
|
|
715
|
-
self.values =
|
|
743
|
+
self.prefix = []
|
|
744
|
+
self.values = []
|
|
716
745
|
|
|
717
746
|
def _parse_memop_squarebracket(self):
|
|
718
747
|
if self.children[0] != "[":
|
|
@@ -746,6 +775,27 @@ class MemoryOperand(Operand):
|
|
|
746
775
|
raise ValueError
|
|
747
776
|
|
|
748
777
|
self.values = self.children[square_bracket_pos + 1 : close_square_pos]
|
|
778
|
+
if len(self.values) >= 3 and self.values[1] == ",":
|
|
779
|
+
# arm style memory operand: [r0, #0x10], or [pc, r3, lsl #1]
|
|
780
|
+
self.separator = "comma"
|
|
781
|
+
# remove all commas and consolidate all remaining strings
|
|
782
|
+
new_values = []
|
|
783
|
+
last_item = None
|
|
784
|
+
for v in self.values:
|
|
785
|
+
if v == ",":
|
|
786
|
+
if last_item is not None:
|
|
787
|
+
new_values.append(last_item)
|
|
788
|
+
last_item = None
|
|
789
|
+
elif isinstance(v, str):
|
|
790
|
+
last_item = v if last_item is None else last_item + v
|
|
791
|
+
else:
|
|
792
|
+
if last_item is not None:
|
|
793
|
+
new_values.append(last_item)
|
|
794
|
+
last_item = None
|
|
795
|
+
new_values.append(v)
|
|
796
|
+
if last_item is not None:
|
|
797
|
+
new_values.append(last_item)
|
|
798
|
+
self.values = new_values
|
|
749
799
|
|
|
750
800
|
def _parse_memop_paren(self):
|
|
751
801
|
offset = []
|
|
@@ -801,7 +851,8 @@ class MemoryOperand(Operand):
|
|
|
801
851
|
if custom_values_str is not None:
|
|
802
852
|
value_str = custom_values_str
|
|
803
853
|
else:
|
|
804
|
-
|
|
854
|
+
sep = "," if self.separator == "comma" else ""
|
|
855
|
+
value_str = sep.join(x.render(formatting)[0] if not isinstance(x, str) else x for x in self.values)
|
|
805
856
|
|
|
806
857
|
if values_style == "curly":
|
|
807
858
|
left_paren, right_paren = "{", "}"
|
|
@@ -811,7 +862,7 @@ class MemoryOperand(Operand):
|
|
|
811
862
|
left_paren, right_paren = "[", "]"
|
|
812
863
|
|
|
813
864
|
if self.offset:
|
|
814
|
-
offset_str = "".join(x.render(formatting)[0] if not isinstance(x,
|
|
865
|
+
offset_str = "".join(x.render(formatting)[0] if not isinstance(x, str) else x for x in self.offset)
|
|
815
866
|
|
|
816
867
|
# combine values and offsets according to self.offset_location
|
|
817
868
|
if self.offset_location == "prefix":
|
|
@@ -829,9 +880,7 @@ class MemoryOperand(Operand):
|
|
|
829
880
|
prefix_str += " "
|
|
830
881
|
|
|
831
882
|
if self.offset:
|
|
832
|
-
offset_str = "".join(
|
|
833
|
-
x.render(formatting)[0] if not isinstance(x, (bytes, str)) else x for x in self.offset
|
|
834
|
-
)
|
|
883
|
+
offset_str = "".join(x.render(formatting)[0] if not isinstance(x, str) else x for x in self.offset)
|
|
835
884
|
|
|
836
885
|
# combine values and offsets according to self.offset_location
|
|
837
886
|
if self.offset_location == "prefix":
|
|
@@ -873,12 +922,14 @@ class Register(OperandPiece):
|
|
|
873
922
|
|
|
874
923
|
|
|
875
924
|
class Value(OperandPiece):
|
|
876
|
-
def __init__(self, val, render_with_sign):
|
|
925
|
+
def __init__(self, val, render_with_sign, render_with_pound_sign: bool = False):
|
|
877
926
|
self.val = val
|
|
878
927
|
self.render_with_sign = render_with_sign
|
|
928
|
+
self.render_with_pound_sign = render_with_pound_sign
|
|
879
929
|
|
|
880
930
|
@property
|
|
881
931
|
def project(self):
|
|
932
|
+
assert self.parentop is not None
|
|
882
933
|
return self.parentop.parentinsn.project
|
|
883
934
|
|
|
884
935
|
def __eq__(self, other):
|
|
@@ -888,26 +939,26 @@ class Value(OperandPiece):
|
|
|
888
939
|
if formatting is not None:
|
|
889
940
|
try:
|
|
890
941
|
style = formatting["int_styles"][self.ident]
|
|
891
|
-
if style[0] == "hex":
|
|
892
|
-
if self.render_with_sign:
|
|
893
|
-
return [f"{self.val:+#x}"]
|
|
894
|
-
return [f"{self.val:#x}"]
|
|
895
|
-
if style[0] == "dec":
|
|
896
|
-
if self.render_with_sign:
|
|
897
|
-
return [f"{self.val:+d}"]
|
|
898
|
-
return [str(self.val)]
|
|
899
942
|
if style[0] == "label":
|
|
900
943
|
labeloffset = style[1]
|
|
901
944
|
if labeloffset == 0:
|
|
902
945
|
lbl = self.project.kb.labels[self.val]
|
|
903
946
|
return [lbl]
|
|
904
|
-
|
|
905
|
-
"
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
947
|
+
s = "{}{}{:#+x}".format(
|
|
948
|
+
"+" if self.render_with_sign else "",
|
|
949
|
+
self.project.kb.labels[self.val + labeloffset],
|
|
950
|
+
labeloffset,
|
|
951
|
+
)
|
|
952
|
+
elif style[0] == "hex":
|
|
953
|
+
s = f"{self.val:+#x}" if self.render_with_sign else f"{self.val:#x}"
|
|
954
|
+
if self.render_with_pound_sign:
|
|
955
|
+
s = "#" + s
|
|
956
|
+
else: # "dec"
|
|
957
|
+
s = f"{self.val:+d}" if self.render_with_sign else str(self.val)
|
|
958
|
+
|
|
959
|
+
if self.render_with_pound_sign:
|
|
960
|
+
s = "#" + s
|
|
961
|
+
return [s]
|
|
911
962
|
except KeyError:
|
|
912
963
|
pass
|
|
913
964
|
|
|
@@ -927,9 +978,10 @@ class Value(OperandPiece):
|
|
|
927
978
|
return [("+" if self.render_with_sign else "") + lbl]
|
|
928
979
|
if func is not None:
|
|
929
980
|
return [func.demangled_name]
|
|
930
|
-
if self.render_with_sign
|
|
931
|
-
|
|
932
|
-
|
|
981
|
+
s = f"{self.val:+#x}" if self.render_with_sign else f"{self.val:#x}"
|
|
982
|
+
if self.render_with_pound_sign:
|
|
983
|
+
s = "#" + s
|
|
984
|
+
return [s]
|
|
933
985
|
|
|
934
986
|
|
|
935
987
|
class Comment(DisassemblyPiece):
|
|
@@ -1079,10 +1131,11 @@ class Disassembly(Analysis):
|
|
|
1079
1131
|
if irsb.statements is not None:
|
|
1080
1132
|
if pcode is not None and isinstance(self.project.factory.default_engine, pcode.HeavyPcodeMixin):
|
|
1081
1133
|
addr = None
|
|
1082
|
-
for stmt_idx, op in enumerate(irsb._ops):
|
|
1134
|
+
for stmt_idx, op in enumerate(irsb._ops): # type:ignore
|
|
1083
1135
|
if op.opcode == pypcode.OpCode.IMARK:
|
|
1084
1136
|
addr = op.inputs[0].offset
|
|
1085
1137
|
else:
|
|
1138
|
+
assert addr is not None
|
|
1086
1139
|
addr_to_ops_map[addr].append(IROp(addr, stmt_idx, op, irsb))
|
|
1087
1140
|
else:
|
|
1088
1141
|
for seq, stmt in enumerate(irsb.statements):
|
|
@@ -1166,22 +1219,23 @@ class Disassembly(Analysis):
|
|
|
1166
1219
|
buf = []
|
|
1167
1220
|
|
|
1168
1221
|
if formatting is None:
|
|
1222
|
+
colors = (
|
|
1223
|
+
{
|
|
1224
|
+
"address": "gray",
|
|
1225
|
+
"bytes": "cyan",
|
|
1226
|
+
"edge": "yellow",
|
|
1227
|
+
Label: "bright_yellow",
|
|
1228
|
+
ConstantOperand: "cyan",
|
|
1229
|
+
MemoryOperand: "yellow",
|
|
1230
|
+
Comment: "gray",
|
|
1231
|
+
Hook: "green",
|
|
1232
|
+
}
|
|
1233
|
+
if ansi_color_enabled and color
|
|
1234
|
+
else {}
|
|
1235
|
+
)
|
|
1169
1236
|
formatting = {
|
|
1170
|
-
"colors":
|
|
1171
|
-
|
|
1172
|
-
"address": "gray",
|
|
1173
|
-
"bytes": "cyan",
|
|
1174
|
-
"edge": "yellow",
|
|
1175
|
-
Label: "bright_yellow",
|
|
1176
|
-
ConstantOperand: "cyan",
|
|
1177
|
-
MemoryOperand: "yellow",
|
|
1178
|
-
Comment: "gray",
|
|
1179
|
-
Hook: "green",
|
|
1180
|
-
}
|
|
1181
|
-
if ansi_color_enabled and color
|
|
1182
|
-
else {}
|
|
1183
|
-
),
|
|
1184
|
-
"format_callback": lambda item, s: ansi_color(s, formatting["colors"].get(type(item), None)),
|
|
1237
|
+
"colors": colors,
|
|
1238
|
+
"format_callback": lambda item, s: ansi_color(s, colors.get(type(item), None)),
|
|
1185
1239
|
}
|
|
1186
1240
|
|
|
1187
1241
|
def col(item: Any) -> str | None:
|
|
@@ -1234,12 +1288,14 @@ class Disassembly(Analysis):
|
|
|
1234
1288
|
# Format the instruction's address, bytes, disassembly, and comment
|
|
1235
1289
|
s_plain = format_address(item.addr, False)
|
|
1236
1290
|
s = format_address(item.addr)
|
|
1291
|
+
bytes_column = 0
|
|
1237
1292
|
if show_bytes:
|
|
1238
1293
|
bytes_column = len(s_plain)
|
|
1239
1294
|
s_plain += format_bytes(insn_bytes[0], False)
|
|
1240
1295
|
s += format_bytes(insn_bytes[0])
|
|
1241
1296
|
s_plain += item.render()[0]
|
|
1242
1297
|
s += item.render(formatting)[0]
|
|
1298
|
+
comment_column = 0
|
|
1243
1299
|
if comment is not None:
|
|
1244
1300
|
comment_column = len(s_plain)
|
|
1245
1301
|
s += format_comment(comment.text[0])
|
angr/analyses/proximity_graph.py
CHANGED
|
@@ -357,26 +357,27 @@ class ProximityGraphAnalysis(Analysis):
|
|
|
357
357
|
def _handle_Call(
|
|
358
358
|
stmt_idx: int, stmt: ailment.Stmt.Call, block: ailment.Block | None # pylint:disable=unused-argument
|
|
359
359
|
): # pylint:disable=unused-argument
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
to_expand
|
|
375
|
-
|
|
376
|
-
|
|
360
|
+
if isinstance(stmt.target, ailment.Expr.Const) and self.kb.functions.contains_addr(stmt.target.value):
|
|
361
|
+
func_node = self.kb.functions[stmt.target.value]
|
|
362
|
+
ref_at = {stmt.ins_addr}
|
|
363
|
+
|
|
364
|
+
# extract arguments
|
|
365
|
+
args = []
|
|
366
|
+
if stmt.args:
|
|
367
|
+
for arg in stmt.args:
|
|
368
|
+
self._arg_handler(arg, args, string_refs)
|
|
369
|
+
|
|
370
|
+
if (
|
|
371
|
+
self._expand_funcs and func_node.addr in self._expand_funcs
|
|
372
|
+
): # pylint:disable=unsupported-membership-test
|
|
373
|
+
new_node = FunctionProxiNode(func_node, ref_at=ref_at)
|
|
374
|
+
if new_node not in to_expand:
|
|
375
|
+
to_expand.append(new_node)
|
|
376
|
+
else:
|
|
377
|
+
new_node = CallProxiNode(func_node, ref_at=ref_at, args=tuple(args) if args is not None else None)
|
|
377
378
|
|
|
378
|
-
|
|
379
|
-
|
|
379
|
+
# stmt has been properly handled, add the proxi node to the list
|
|
380
|
+
self.handled_stmts.append(new_node)
|
|
380
381
|
|
|
381
382
|
# This should have the same functionality as the previous handler
|
|
382
383
|
def _handle_CallExpr(
|
angr/analyses/s_propagator.py
CHANGED
|
@@ -15,6 +15,7 @@ from angr.ailment.expression import (
|
|
|
15
15
|
Load,
|
|
16
16
|
Convert,
|
|
17
17
|
Expression,
|
|
18
|
+
Tmp,
|
|
18
19
|
)
|
|
19
20
|
from angr.ailment.statement import Assignment, Store, Return, Jump, ConditionalJump
|
|
20
21
|
|
|
@@ -164,7 +165,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
164
165
|
assert v is not None
|
|
165
166
|
const_vvars[vvar_id] = v
|
|
166
167
|
for vvar_at_use, useloc in vvar_uselocs[vvar_id]:
|
|
167
|
-
replacements
|
|
168
|
+
self.replace(replacements, useloc, vvar_at_use, v)
|
|
168
169
|
continue
|
|
169
170
|
|
|
170
171
|
v = phi_assignment_get_src(stmt)
|
|
@@ -185,7 +186,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
185
186
|
const_value = const_vvars[next(iter(all_int_src_varids))]
|
|
186
187
|
const_vvars[vvar.varid] = const_value
|
|
187
188
|
for vvar_at_use, useloc in vvar_uselocs[vvar.varid]:
|
|
188
|
-
replacements
|
|
189
|
+
self.replace(replacements, useloc, vvar_at_use, const_value)
|
|
189
190
|
|
|
190
191
|
# function mode only
|
|
191
192
|
if self.mode == "function":
|
|
@@ -226,7 +227,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
226
227
|
if can_replace:
|
|
227
228
|
# we can propagate this load because there is no store between its def and use
|
|
228
229
|
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
229
|
-
replacements
|
|
230
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
230
231
|
continue
|
|
231
232
|
|
|
232
233
|
if (
|
|
@@ -241,7 +242,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
241
242
|
uselocs = {loc for _, loc in vvar_uselocs_set}
|
|
242
243
|
if self.is_vvar_used_for_addr_loading_switch_case(uselocs, blocks) and not has_tmp_expr(stmt.src):
|
|
243
244
|
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
244
|
-
replacements
|
|
245
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
245
246
|
# mark the vvar as dead and should be removed
|
|
246
247
|
self.model.dead_vvar_ids.add(vvar.varid)
|
|
247
248
|
continue
|
|
@@ -255,7 +256,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
255
256
|
and not has_tmp_expr(stmt.src)
|
|
256
257
|
):
|
|
257
258
|
# we can propagate this load because there is no store between its def and use
|
|
258
|
-
replacements
|
|
259
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
259
260
|
continue
|
|
260
261
|
|
|
261
262
|
if is_const_and_vvar_assignment(stmt) and not has_tmp_expr(stmt.src):
|
|
@@ -272,9 +273,9 @@ class SPropagatorAnalysis(Analysis):
|
|
|
272
273
|
and stmt.src.oident == useloc_stmt.dst.oident
|
|
273
274
|
and stmt.src.category == useloc_stmt.dst.category
|
|
274
275
|
):
|
|
275
|
-
replacements
|
|
276
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
276
277
|
else:
|
|
277
|
-
replacements
|
|
278
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
278
279
|
continue
|
|
279
280
|
|
|
280
281
|
else:
|
|
@@ -288,7 +289,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
288
289
|
# this vvar is used once if we exclude its uses at ret sites or jump sites. we can
|
|
289
290
|
# propagate it
|
|
290
291
|
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
291
|
-
replacements
|
|
292
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
292
293
|
continue
|
|
293
294
|
|
|
294
295
|
if (
|
|
@@ -304,7 +305,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
304
305
|
# remove duplicate use locs (e.g., if the variable is used multiple times by the
|
|
305
306
|
# same statement) - but ensure stmt is simple enough
|
|
306
307
|
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
307
|
-
replacements
|
|
308
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
308
309
|
continue
|
|
309
310
|
|
|
310
311
|
# special logic for global variables: if it's used once or multiple times, and the variable is never
|
|
@@ -332,7 +333,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
332
333
|
)
|
|
333
334
|
if not gv_updated:
|
|
334
335
|
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
335
|
-
replacements
|
|
336
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
336
337
|
continue
|
|
337
338
|
|
|
338
339
|
for vvar_id, uselocs in vvar_uselocs.items():
|
|
@@ -353,7 +354,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
353
354
|
if sp_bits is not None and vvar.bits < sp_bits:
|
|
354
355
|
# truncation needed
|
|
355
356
|
v = Convert(None, sp_bits, vvar.bits, False, v)
|
|
356
|
-
replacements
|
|
357
|
+
self.replace(replacements, useloc, vvar_at_use, v)
|
|
357
358
|
continue
|
|
358
359
|
if not self._bp_as_gpr and vvar.oident == self.project.arch.bp_offset:
|
|
359
360
|
bp_bits = (
|
|
@@ -368,7 +369,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
368
369
|
if bp_bits is not None and vvar.bits < bp_bits:
|
|
369
370
|
# truncation needed
|
|
370
371
|
v = Convert(None, bp_bits, vvar.bits, False, v)
|
|
371
|
-
replacements
|
|
372
|
+
self.replace(replacements, useloc, vvar_at_use, v)
|
|
372
373
|
continue
|
|
373
374
|
|
|
374
375
|
# find all tmp definitions
|
|
@@ -390,9 +391,8 @@ class SPropagatorAnalysis(Analysis):
|
|
|
390
391
|
if r:
|
|
391
392
|
# we can propagate it!
|
|
392
393
|
for tmp_used, tmp_use_stmtidx in tmp_uses:
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
][tmp_used] = stmt.src
|
|
394
|
+
loc = CodeLocation(block_loc.block_addr, tmp_use_stmtidx, block_idx=block_loc.block_idx)
|
|
395
|
+
self.replace(replacements, loc, tmp_used, stmt.src)
|
|
396
396
|
continue
|
|
397
397
|
|
|
398
398
|
r = is_const_vvar_tmp_assignment(stmt)
|
|
@@ -404,9 +404,8 @@ class SPropagatorAnalysis(Analysis):
|
|
|
404
404
|
v = stmt.src
|
|
405
405
|
|
|
406
406
|
for tmp_used, tmp_use_stmtidx in tmp_uses:
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
][tmp_used] = v
|
|
407
|
+
loc = CodeLocation(block_loc.block_addr, tmp_use_stmtidx, block_idx=block_loc.block_idx)
|
|
408
|
+
self.replace(replacements, loc, tmp_used, v)
|
|
410
409
|
continue
|
|
411
410
|
|
|
412
411
|
if len(tmp_uses) <= 2 and is_const_vvar_load_dirty_assignment(stmt):
|
|
@@ -422,9 +421,8 @@ class SPropagatorAnalysis(Analysis):
|
|
|
422
421
|
# we can propagate this load because either we do not consider memory aliasing problem
|
|
423
422
|
# within the same instruction (blocks must be originally lifted with
|
|
424
423
|
# CROSS_INSN_OPT=False), or there is no store between its def and use.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
][tmp_used] = stmt.src
|
|
424
|
+
loc = CodeLocation(block_loc.block_addr, tmp_use_stmtidx, block_idx=block_loc.block_idx)
|
|
425
|
+
self.replace(replacements, loc, tmp_used, stmt.src)
|
|
428
426
|
|
|
429
427
|
self.model.replacements = replacements
|
|
430
428
|
|
|
@@ -538,5 +536,9 @@ class SPropagatorAnalysis(Analysis):
|
|
|
538
536
|
|
|
539
537
|
return g
|
|
540
538
|
|
|
539
|
+
@staticmethod
|
|
540
|
+
def replace(replacements: dict, loc, expr: VirtualVariable | Tmp, value: Expression) -> None:
|
|
541
|
+
replacements[loc][expr] = value
|
|
542
|
+
|
|
541
543
|
|
|
542
544
|
register_analysis(SPropagatorAnalysis, "SPropagator")
|
angr/analyses/smc.py
CHANGED
|
@@ -112,9 +112,8 @@ class SelfModifyingCodeAnalysis(Analysis):
|
|
|
112
112
|
if subject is None:
|
|
113
113
|
subject = self.project.entry
|
|
114
114
|
if isinstance(subject, str):
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
except KeyError:
|
|
115
|
+
addr = self.project.kb.labels.lookup(subject)
|
|
116
|
+
if addr is None:
|
|
118
117
|
addr = self.project.kb.functions[subject].addr
|
|
119
118
|
elif isinstance(subject, Function):
|
|
120
119
|
addr = subject.addr
|
angr/flirt/__init__.py
CHANGED
|
@@ -26,6 +26,60 @@ LIBRARY_TO_SIGNATURES: dict[str, list[FlirtSignature]] = defaultdict(list)
|
|
|
26
26
|
STRING_TO_LIBRARIES: dict[str, set[str]] = defaultdict(set)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def load_signature(sig_path: str, meta_path: str | None = None) -> tuple[str, FlirtSignature] | None:
|
|
30
|
+
"""
|
|
31
|
+
Load a single FLIRT signature from a specific path.
|
|
32
|
+
|
|
33
|
+
:param sig_path: Location of the FLIRT signature.
|
|
34
|
+
:return: A FlirtSignature object if loading was successful, None otherwise.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
# parse it
|
|
38
|
+
try:
|
|
39
|
+
with open(sig_path, "rb") as f:
|
|
40
|
+
sig_parsed = FlirtSignatureParsed.parse(f)
|
|
41
|
+
except FlirtSignatureError:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
# is there a meta data file?
|
|
45
|
+
if meta_path is not None and os.path.isfile(meta_path):
|
|
46
|
+
# yes!
|
|
47
|
+
with open(meta_path) as f:
|
|
48
|
+
meta = json.load(f)
|
|
49
|
+
|
|
50
|
+
arch = str(meta.get("arch", "Unknown"))
|
|
51
|
+
platform = str(meta.get("platform", "UnknownOS"))
|
|
52
|
+
os_name = meta.get("os", None)
|
|
53
|
+
os_version = meta.get("os_version", None)
|
|
54
|
+
compiler = meta.get("compiler", None)
|
|
55
|
+
compiler_version = meta.get("compiler_version", None)
|
|
56
|
+
unique_strings = meta.get("unique_strings", None)
|
|
57
|
+
|
|
58
|
+
else:
|
|
59
|
+
# nope... we need to extract information from the signature file
|
|
60
|
+
arch = flirt_arch_to_arch_name(sig_parsed.arch, sig_parsed.app_types)
|
|
61
|
+
platform = flirt_os_type_to_os_name(sig_parsed.os_types)
|
|
62
|
+
os_name = None
|
|
63
|
+
os_version = None
|
|
64
|
+
unique_strings = None
|
|
65
|
+
compiler = None
|
|
66
|
+
compiler_version = None
|
|
67
|
+
|
|
68
|
+
signature = FlirtSignature(
|
|
69
|
+
arch,
|
|
70
|
+
platform,
|
|
71
|
+
sig_parsed.libname,
|
|
72
|
+
sig_path,
|
|
73
|
+
unique_strings=unique_strings,
|
|
74
|
+
compiler=compiler,
|
|
75
|
+
compiler_version=compiler_version,
|
|
76
|
+
os_name=os_name,
|
|
77
|
+
os_version=os_version,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return arch, signature
|
|
81
|
+
|
|
82
|
+
|
|
29
83
|
def load_signatures(path: str) -> None:
|
|
30
84
|
"""
|
|
31
85
|
Recursively load all FLIRT signatures under a specific path.
|
|
@@ -40,52 +94,14 @@ def load_signatures(path: str) -> None:
|
|
|
40
94
|
for root, _, filenames in os.walk(path):
|
|
41
95
|
for filename in filenames:
|
|
42
96
|
if filename.endswith(".sig"):
|
|
43
|
-
# parse it
|
|
44
97
|
sig_path = os.path.join(root, filename)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
except FlirtSignatureError:
|
|
98
|
+
meta_path = os.path.join(root, filename[:-4] + ".meta")
|
|
99
|
+
r = load_signature(sig_path, meta_path=meta_path)
|
|
100
|
+
if r is None:
|
|
49
101
|
_l.warning("Failed to load FLIRT signature file %s.", sig_path)
|
|
50
102
|
continue
|
|
51
103
|
|
|
52
|
-
|
|
53
|
-
meta_path = os.path.join(root, filename[:-4] + ".meta")
|
|
54
|
-
if os.path.isfile(meta_path):
|
|
55
|
-
# yes!
|
|
56
|
-
with open(meta_path) as f:
|
|
57
|
-
meta = json.load(f)
|
|
58
|
-
|
|
59
|
-
arch = str(meta.get("arch", "Unknown"))
|
|
60
|
-
platform = str(meta.get("platform", "UnknownOS"))
|
|
61
|
-
os_name = meta.get("os", None)
|
|
62
|
-
os_version = meta.get("os_version", None)
|
|
63
|
-
compiler = meta.get("compiler", None)
|
|
64
|
-
compiler_version = meta.get("compiler_version", None)
|
|
65
|
-
unique_strings = meta.get("unique_strings", None)
|
|
66
|
-
|
|
67
|
-
else:
|
|
68
|
-
# nope... we need to extract information from the signature file
|
|
69
|
-
arch = flirt_arch_to_arch_name(sig_parsed.arch, sig_parsed.app_types)
|
|
70
|
-
platform = flirt_os_type_to_os_name(sig_parsed.os_types)
|
|
71
|
-
os_name = None
|
|
72
|
-
os_version = None
|
|
73
|
-
unique_strings = None
|
|
74
|
-
compiler = None
|
|
75
|
-
compiler_version = None
|
|
76
|
-
|
|
77
|
-
signature = FlirtSignature(
|
|
78
|
-
arch,
|
|
79
|
-
platform,
|
|
80
|
-
sig_parsed.libname,
|
|
81
|
-
sig_path,
|
|
82
|
-
unique_strings=unique_strings,
|
|
83
|
-
compiler=compiler,
|
|
84
|
-
compiler_version=compiler_version,
|
|
85
|
-
os_name=os_name,
|
|
86
|
-
os_version=os_version,
|
|
87
|
-
)
|
|
88
|
-
|
|
104
|
+
arch, signature = r
|
|
89
105
|
FLIRT_SIGNATURES_BY_ARCH[arch].append(signature)
|
|
90
106
|
|
|
91
107
|
# fill in LIBRARY_TO_SIGNATURES and STRING_TO_LIBRARIES
|
|
@@ -95,3 +111,14 @@ def load_signatures(path: str) -> None:
|
|
|
95
111
|
if sig.unique_strings:
|
|
96
112
|
for us in sig.unique_strings:
|
|
97
113
|
STRING_TO_LIBRARIES[us].add(sig.sig_name)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
__all__ = (
|
|
117
|
+
"FLIRT_SIGNATURES_BY_ARCH",
|
|
118
|
+
"FS",
|
|
119
|
+
"LIBRARY_TO_SIGNATURES",
|
|
120
|
+
"STRING_TO_LIBRARIES",
|
|
121
|
+
"FlirtSignature",
|
|
122
|
+
"load_signature",
|
|
123
|
+
"load_signatures",
|
|
124
|
+
)
|