pcode-graph 1.0.0__py3-none-any.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.
- pcode_graph/__init__.py +0 -0
- pcode_graph/__main__.py +3 -0
- pcode_graph/analysis.py +395 -0
- pcode_graph/arch.py +10 -0
- pcode_graph/asm.py +60 -0
- pcode_graph/charac.py +97 -0
- pcode_graph/checker.py +120 -0
- pcode_graph/cli.py +175 -0
- pcode_graph/emulator.py +419 -0
- pcode_graph/gnn_exporter.py +208 -0
- pcode_graph/graph.py +234 -0
- pcode_graph/lief_importer.py +299 -0
- pcode_graph/log.py +36 -0
- pcode_graph/maker.py +369 -0
- pcode_graph/nx_exporter.py +20 -0
- pcode_graph/pcode.py +187 -0
- pcode_graph/registers.py +2929 -0
- pcode_graph/simplifier.py +185 -0
- pcode_graph/torch_utils.py +30 -0
- pcode_graph/translator.py +235 -0
- pcode_graph/utils.py +15 -0
- pcode_graph/view.py +65 -0
- pcode_graph/visitor.py +103 -0
- pcode_graph-1.0.0.dist-info/METADATA +205 -0
- pcode_graph-1.0.0.dist-info/RECORD +28 -0
- pcode_graph-1.0.0.dist-info/WHEEL +4 -0
- pcode_graph-1.0.0.dist-info/entry_points.txt +2 -0
- pcode_graph-1.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from dataclasses import asdict
|
|
2
|
+
from pcode_graph.graph import CDG, EdgeKinds
|
|
3
|
+
import networkx as nx
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def export_to_networkx(graph: CDG, ignore_cfg: bool) -> nx.MultiDiGraph:
|
|
7
|
+
"""Export the graph to network X format, keeping attributes."""
|
|
8
|
+
|
|
9
|
+
ng = nx.MultiDiGraph()
|
|
10
|
+
for n, node in enumerate(graph.nodes):
|
|
11
|
+
ng.add_node(n, **asdict(node))
|
|
12
|
+
for e in graph.edges:
|
|
13
|
+
if ignore_cfg and e.kind == EdgeKinds.Control:
|
|
14
|
+
continue
|
|
15
|
+
ng.add_edge(e.source_node, e.destination_node, kind=e.kind.name, operand_number=e.operand_number,)
|
|
16
|
+
return ng
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
pcode_graph/pcode.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
from enum import Enum, auto
|
|
2
|
+
import pypcode
|
|
3
|
+
from pcode_graph.arch import Arch
|
|
4
|
+
from pcode_graph.utils import unsigned_to_signed
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Replacement for native pypcode opcode (to ease pickling and dumping)
|
|
8
|
+
# Raw P-Code only
|
|
9
|
+
class OpCodes(Enum):
|
|
10
|
+
BOOL_AND = 0
|
|
11
|
+
BOOL_NEGATE = auto()
|
|
12
|
+
BOOL_OR = auto()
|
|
13
|
+
BOOL_XOR = auto()
|
|
14
|
+
BRANCH = auto()
|
|
15
|
+
BRANCHIND = auto()
|
|
16
|
+
CALL = auto()
|
|
17
|
+
CALLIND = auto()
|
|
18
|
+
CALLOTHER = auto()
|
|
19
|
+
CBRANCH = auto()
|
|
20
|
+
COPY = auto()
|
|
21
|
+
FLOAT_ABS = auto()
|
|
22
|
+
FLOAT_ADD = auto()
|
|
23
|
+
FLOAT_CEIL = auto()
|
|
24
|
+
FLOAT_DIV = auto()
|
|
25
|
+
FLOAT_EQUAL = auto()
|
|
26
|
+
FLOAT_FLOAT2FLOAT = auto()
|
|
27
|
+
FLOAT_FLOOR = auto()
|
|
28
|
+
FLOAT_INT2FLOAT = auto()
|
|
29
|
+
FLOAT_LESS = auto()
|
|
30
|
+
FLOAT_LESSEQUAL = auto()
|
|
31
|
+
FLOAT_MULT = auto()
|
|
32
|
+
FLOAT_NAN = auto()
|
|
33
|
+
FLOAT_NEG = auto()
|
|
34
|
+
FLOAT_NOTEQUAL = auto()
|
|
35
|
+
FLOAT_ROUND = auto()
|
|
36
|
+
FLOAT_SQRT = auto()
|
|
37
|
+
FLOAT_SUB = auto()
|
|
38
|
+
FLOAT_TRUNC = auto()
|
|
39
|
+
INT_2COMP = auto()
|
|
40
|
+
INT_ADD = auto()
|
|
41
|
+
INT_AND = auto()
|
|
42
|
+
INT_CARRY = auto()
|
|
43
|
+
INT_DIV = auto()
|
|
44
|
+
INT_EQUAL = auto()
|
|
45
|
+
INT_LEFT = auto()
|
|
46
|
+
INT_LESS = auto()
|
|
47
|
+
INT_LESSEQUAL = auto()
|
|
48
|
+
INT_MULT = auto()
|
|
49
|
+
INT_NEGATE = auto()
|
|
50
|
+
INT_NOTEQUAL = auto()
|
|
51
|
+
INT_OR = auto()
|
|
52
|
+
INT_REM = auto()
|
|
53
|
+
INT_RIGHT = auto()
|
|
54
|
+
INT_SBORROW = auto()
|
|
55
|
+
INT_SCARRY = auto()
|
|
56
|
+
INT_SDIV = auto()
|
|
57
|
+
INT_SEXT = auto()
|
|
58
|
+
INT_SLESS = auto()
|
|
59
|
+
INT_SLESSEQUAL = auto()
|
|
60
|
+
INT_SREM = auto()
|
|
61
|
+
INT_SRIGHT = auto()
|
|
62
|
+
INT_SUB = auto()
|
|
63
|
+
INT_XOR = auto()
|
|
64
|
+
INT_ZEXT = auto()
|
|
65
|
+
LOAD = auto()
|
|
66
|
+
LZCOUNT = auto()
|
|
67
|
+
PIECE = auto()
|
|
68
|
+
POPCOUNT = auto()
|
|
69
|
+
RETURN = auto()
|
|
70
|
+
SEGMENTOP = auto()
|
|
71
|
+
STORE = auto()
|
|
72
|
+
SUBPIECE = auto()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def from_native_opcode(opcode: pypcode.OpCode) -> OpCodes | None:
|
|
76
|
+
if opcode.name == "IMARK":
|
|
77
|
+
return None
|
|
78
|
+
return OpCodes[opcode.name]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
JUMP_OPCODES = {
|
|
82
|
+
OpCodes.BRANCH,
|
|
83
|
+
OpCodes.BRANCHIND,
|
|
84
|
+
OpCodes.CALL,
|
|
85
|
+
OpCodes.CALLIND,
|
|
86
|
+
OpCodes.CBRANCH,
|
|
87
|
+
OpCodes.RETURN,
|
|
88
|
+
OpCodes.CALLOTHER,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
NUM_OPERANDS: dict[OpCodes, int] = {
|
|
93
|
+
OpCodes.BOOL_AND: 2,
|
|
94
|
+
OpCodes.BOOL_NEGATE: 1,
|
|
95
|
+
OpCodes.BOOL_OR: 2,
|
|
96
|
+
OpCodes.BOOL_XOR: 2,
|
|
97
|
+
OpCodes.BRANCH: 0,
|
|
98
|
+
OpCodes.BRANCHIND: 1,
|
|
99
|
+
OpCodes.CALL: 0,
|
|
100
|
+
OpCodes.CALLIND: 1,
|
|
101
|
+
OpCodes.CALLOTHER: -1,
|
|
102
|
+
OpCodes.CBRANCH: 1,
|
|
103
|
+
OpCodes.COPY: 1,
|
|
104
|
+
OpCodes.FLOAT_ABS: 1,
|
|
105
|
+
OpCodes.FLOAT_ADD: 2,
|
|
106
|
+
OpCodes.FLOAT_CEIL: 1,
|
|
107
|
+
OpCodes.FLOAT_DIV: 2,
|
|
108
|
+
OpCodes.FLOAT_EQUAL: 2,
|
|
109
|
+
OpCodes.FLOAT_FLOAT2FLOAT: 1,
|
|
110
|
+
OpCodes.FLOAT_FLOOR: 1,
|
|
111
|
+
OpCodes.FLOAT_INT2FLOAT: 1,
|
|
112
|
+
OpCodes.FLOAT_LESS: 2,
|
|
113
|
+
OpCodes.FLOAT_LESSEQUAL: 2,
|
|
114
|
+
OpCodes.FLOAT_MULT: 2,
|
|
115
|
+
OpCodes.FLOAT_NAN: 1,
|
|
116
|
+
OpCodes.FLOAT_NEG: 1,
|
|
117
|
+
OpCodes.FLOAT_NOTEQUAL: 2,
|
|
118
|
+
OpCodes.FLOAT_ROUND: 1,
|
|
119
|
+
OpCodes.FLOAT_SQRT: 1,
|
|
120
|
+
OpCodes.FLOAT_SUB: 2,
|
|
121
|
+
OpCodes.FLOAT_TRUNC: 1,
|
|
122
|
+
OpCodes.INT_2COMP: 1,
|
|
123
|
+
OpCodes.INT_ADD: 2,
|
|
124
|
+
OpCodes.INT_AND: 2,
|
|
125
|
+
OpCodes.INT_CARRY: 2,
|
|
126
|
+
OpCodes.INT_DIV: 2,
|
|
127
|
+
OpCodes.INT_EQUAL: 2,
|
|
128
|
+
OpCodes.INT_LEFT: 2,
|
|
129
|
+
OpCodes.INT_LESS: 2,
|
|
130
|
+
OpCodes.INT_LESSEQUAL: 2,
|
|
131
|
+
OpCodes.INT_MULT: 2,
|
|
132
|
+
OpCodes.INT_NEGATE: 1,
|
|
133
|
+
OpCodes.INT_NOTEQUAL: 2,
|
|
134
|
+
OpCodes.INT_OR: 2,
|
|
135
|
+
OpCodes.INT_REM: 2,
|
|
136
|
+
OpCodes.INT_RIGHT: 2,
|
|
137
|
+
OpCodes.INT_SBORROW: 2,
|
|
138
|
+
OpCodes.INT_SCARRY: 2,
|
|
139
|
+
OpCodes.INT_SDIV: 2,
|
|
140
|
+
OpCodes.INT_SEXT: 1,
|
|
141
|
+
OpCodes.INT_SLESS: 2,
|
|
142
|
+
OpCodes.INT_SLESSEQUAL: 2,
|
|
143
|
+
OpCodes.INT_SREM: 2,
|
|
144
|
+
OpCodes.INT_SRIGHT: 2,
|
|
145
|
+
OpCodes.INT_SUB: 2,
|
|
146
|
+
OpCodes.INT_XOR: 2,
|
|
147
|
+
OpCodes.INT_ZEXT: 1,
|
|
148
|
+
OpCodes.LOAD: 2, # Memory node added
|
|
149
|
+
OpCodes.LZCOUNT: 1,
|
|
150
|
+
OpCodes.PIECE: 2,
|
|
151
|
+
OpCodes.POPCOUNT: 1,
|
|
152
|
+
OpCodes.RETURN: 1,
|
|
153
|
+
OpCodes.SEGMENTOP: 2,
|
|
154
|
+
OpCodes.STORE: 2,
|
|
155
|
+
OpCodes.SUBPIECE: 2,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def dump_variable(var: pypcode.Varnode | None):
|
|
160
|
+
if var is None:
|
|
161
|
+
return "none"
|
|
162
|
+
reg = var.getRegisterName()
|
|
163
|
+
if reg:
|
|
164
|
+
assert var.space.name == "register"
|
|
165
|
+
return reg
|
|
166
|
+
match var.space.name:
|
|
167
|
+
case "unique":
|
|
168
|
+
return f"${var.offset}"
|
|
169
|
+
case "const":
|
|
170
|
+
# Consider all constants as signed to reduce id length and improve
|
|
171
|
+
# readability
|
|
172
|
+
value = unsigned_to_signed(var.offset, var.size)
|
|
173
|
+
return "#" + hex(value)
|
|
174
|
+
case "ram":
|
|
175
|
+
return f"[0x{var.offset:x}]"
|
|
176
|
+
return f"{var.space.name}:{var.offset}"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def dump_operation(op: pypcode.PcodeOp) -> str:
|
|
180
|
+
s = ""
|
|
181
|
+
if op.output is not None:
|
|
182
|
+
s += dump_variable(op.output) + " = "
|
|
183
|
+
if op.opcode != pypcode.OpCode.COPY:
|
|
184
|
+
s += op.opcode.name.lower() + " "
|
|
185
|
+
s += ", ".join(dump_variable(v) for v in op.inputs)
|
|
186
|
+
return s
|
|
187
|
+
|