pcode-graph 1.0.0__tar.gz

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.
Files changed (41) hide show
  1. pcode_graph-1.0.0/.github/workflows/release.yml +32 -0
  2. pcode_graph-1.0.0/.gitignore +6 -0
  3. pcode_graph-1.0.0/LICENSE +21 -0
  4. pcode_graph-1.0.0/PKG-INFO +205 -0
  5. pcode_graph-1.0.0/README.md +177 -0
  6. pcode_graph-1.0.0/pcode_graph/__init__.py +0 -0
  7. pcode_graph-1.0.0/pcode_graph/__main__.py +3 -0
  8. pcode_graph-1.0.0/pcode_graph/analysis.py +395 -0
  9. pcode_graph-1.0.0/pcode_graph/arch.py +10 -0
  10. pcode_graph-1.0.0/pcode_graph/asm.py +60 -0
  11. pcode_graph-1.0.0/pcode_graph/charac.py +97 -0
  12. pcode_graph-1.0.0/pcode_graph/checker.py +120 -0
  13. pcode_graph-1.0.0/pcode_graph/cli.py +175 -0
  14. pcode_graph-1.0.0/pcode_graph/emulator.py +419 -0
  15. pcode_graph-1.0.0/pcode_graph/gnn_exporter.py +208 -0
  16. pcode_graph-1.0.0/pcode_graph/graph.py +234 -0
  17. pcode_graph-1.0.0/pcode_graph/lief_importer.py +299 -0
  18. pcode_graph-1.0.0/pcode_graph/log.py +36 -0
  19. pcode_graph-1.0.0/pcode_graph/maker.py +369 -0
  20. pcode_graph-1.0.0/pcode_graph/nx_exporter.py +20 -0
  21. pcode_graph-1.0.0/pcode_graph/pcode.py +187 -0
  22. pcode_graph-1.0.0/pcode_graph/registers.py +2929 -0
  23. pcode_graph-1.0.0/pcode_graph/simplifier.py +185 -0
  24. pcode_graph-1.0.0/pcode_graph/torch_utils.py +30 -0
  25. pcode_graph-1.0.0/pcode_graph/translator.py +235 -0
  26. pcode_graph-1.0.0/pcode_graph/utils.py +15 -0
  27. pcode_graph-1.0.0/pcode_graph/view.py +65 -0
  28. pcode_graph-1.0.0/pcode_graph/visitor.py +103 -0
  29. pcode_graph-1.0.0/pyproject.toml +48 -0
  30. pcode_graph-1.0.0/requirements.txt +12 -0
  31. pcode_graph-1.0.0/tests/__init__.py +0 -0
  32. pcode_graph-1.0.0/tests/analysis_test.py +395 -0
  33. pcode_graph-1.0.0/tests/charac_test.py +69 -0
  34. pcode_graph-1.0.0/tests/conftest.py +7 -0
  35. pcode_graph-1.0.0/tests/emulator_test.py +27 -0
  36. pcode_graph-1.0.0/tests/exporter_test.py +68 -0
  37. pcode_graph-1.0.0/tests/fixtures.py +70 -0
  38. pcode_graph-1.0.0/tests/helpers.py +100 -0
  39. pcode_graph-1.0.0/tests/maker_test.py +1156 -0
  40. pcode_graph-1.0.0/tests/translator_test.py +194 -0
  41. pcode_graph-1.0.0/tests/utils_test.py +54 -0
@@ -0,0 +1,32 @@
1
+ name: Publish to PyPI
2
+ on:
3
+ push:
4
+ tags: ["v*"]
5
+
6
+ jobs:
7
+ build:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/checkout@v4
11
+ - uses: actions/setup-python@v5
12
+ with:
13
+ python-version: "3.12"
14
+ - run: pip install build
15
+ - run: python -m build
16
+ - uses: actions/upload-artifact@v4
17
+ with:
18
+ name: dist
19
+ path: dist/
20
+
21
+ publish:
22
+ needs: build
23
+ runs-on: ubuntu-latest
24
+ environment: pypi
25
+ permissions:
26
+ id-token: write
27
+ steps:
28
+ - uses: actions/download-artifact@v4
29
+ with:
30
+ name: dist
31
+ path: dist/
32
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,6 @@
1
+ /venv
2
+ /.venv
3
+ /lib
4
+ /out
5
+ __pycache__
6
+ /.vscode
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Quarkslab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: pcode_graph
3
+ Version: 1.0.0
4
+ Summary: Build, display and vectorize semantic graphs from binary code
5
+ Project-URL: Homepage, https://github.com/quarkslab/pcode_graph
6
+ Project-URL: Issues, https://github.com/quarkslab/pcode_graph/issues
7
+ Author-email: Samuel Hangouët <samuel.hangouet@quarkslab.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Security
13
+ Requires-Python: >=3.12
14
+ Requires-Dist: docopt
15
+ Requires-Dist: keystone-engine~=0.9.2
16
+ Requires-Dist: lief~=1.0.0
17
+ Requires-Dist: loguru
18
+ Requires-Dist: magika
19
+ Requires-Dist: pypcode>=4.0.0
20
+ Requires-Dist: pyvis
21
+ Provides-Extra: test
22
+ Requires-Dist: pytest; extra == 'test'
23
+ Requires-Dist: pytest-timeout; extra == 'test'
24
+ Provides-Extra: torch
25
+ Requires-Dist: torch-geometric~=2.8.0; extra == 'torch'
26
+ Requires-Dist: torch~=2.13.0; extra == 'torch'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # P-Code Graph
30
+
31
+ This library helps to build, display and vectorize the Control-Data Graph (CDG) of a
32
+ chunk of binary code, using Ghidra P-Code representation.
33
+
34
+ ## Command-line interface
35
+
36
+ A `cdg` tool is provided to play with binary of assembly files.
37
+
38
+ For example to dump the CDG of a function in markdown format:
39
+
40
+ ```console
41
+ (venv) $ cdg mermaid test.o -f func -o graph.md
42
+ ```
43
+
44
+ Or to display the graph of the whole content of an assembly file in HTML:
45
+
46
+ ```console
47
+ (venv) $ cdg html test.asm -o graph.html
48
+ ```
49
+
50
+ ## Tests
51
+
52
+ ### Run tests
53
+
54
+ ```console
55
+ $ python3.14 -m venv venv
56
+ $ source venv/bin/activate
57
+ (venv) $ pip install .[test,torch]
58
+ (venv) $ python -m pytest tests
59
+ ```
60
+
61
+ ### Test output
62
+
63
+ Tests using the `outdir` fixture generate their files in a dedicated output
64
+ directory, in which you can usually find:
65
+
66
+ - the log file
67
+ - the dump of P-Code operations
68
+ - the dump of generated graph in markdown+mermaid format
69
+ - a HTML file to display the related graph in a browser
70
+
71
+ You can display the dataflow graph resulting from a test in its output directory:
72
+
73
+ ```console
74
+ $ firefox out/tests/maker_test/test_call/graph.html
75
+ ```
76
+
77
+ ## API
78
+
79
+ ### Graph representation
80
+
81
+ The `graph.py` module contains the data classes defining a CDG.
82
+
83
+ ```mermaid
84
+ classDiagram
85
+ Graph *-- Node : nodes
86
+ Graph *-- Edge : edges
87
+ Graph: +list[Node] nodes
88
+ Graph: +list[Edge] edges
89
+
90
+ Node: +NodeKinds kind
91
+ Node: +int size
92
+ Node: +int value
93
+ Node: +int register_id
94
+ Node: +str register_name
95
+ Node: +OpCodes opcode
96
+
97
+ Edge: +EdgeKinds kind
98
+ Edge: +int source_node
99
+ Edge: +int destination_node
100
+ Edge: +Optional[int] operand_number
101
+ ```
102
+
103
+ ### Build a graph
104
+
105
+
106
+ We use LIEF library as a default binary loader. To build a graph from binary code:
107
+
108
+ ```python
109
+ from pcode_graph.maker import make_graph_from_binary
110
+ from pcode_graph.pcode import Translator
111
+ from pcode_graph.arch import Arch
112
+ from pcode_graph.lief_importer import iter_functions
113
+
114
+ translator = Translator(Arch.x86_64)
115
+
116
+ for function in iter_functions(Path("a.out")):
117
+ graph = make_graph_from_binary(translator,
118
+ function.content,
119
+ function.address,
120
+ ignore_flag_outputs=True)
121
+ ```
122
+
123
+ Note that you can alternatively collect basic blocks (see lief_importer.py module), or use your own loader and make a graph from an arbitrary piece of binary code.
124
+
125
+ For testing purpose, you can also build it from assembly:
126
+
127
+ ```python
128
+ from pcode_graph.maker import make_graph_from_asm
129
+ from pcode_graph.asm import Assembler
130
+ from pcode_graph.pcode import Translator
131
+ from pcode_graph.arch import Arch
132
+
133
+ translator = Translator(Arch.arm_64)
134
+ assembler = Assembler(Arch.arm_64)
135
+ asm = """
136
+ add x0, x1, x2
137
+ blr x16
138
+ mul x1, x0, x0
139
+ """
140
+
141
+ graph = make_graph_from_asm(assembler, translator, asm)
142
+ ```
143
+
144
+ ### View the graph
145
+
146
+ You can unparse the graph into markdown using the mermaid syntax:
147
+
148
+ ```python
149
+ with open("graph.md", "w") as f:
150
+ f.write(str(graph))
151
+ ```
152
+
153
+ The module `pcode_graph.view` wraps the `pyvis` library to display graphs in a browser:
154
+
155
+ ```python
156
+ from pcode_graph.view import draw_graph
157
+
158
+ draw_graph(graph, "out/graph.html", open_browser=True)
159
+ ```
160
+
161
+ ### Vectorize the graph
162
+
163
+ To convert a graph to tensors, you will need the list of the registers
164
+ you expect your model to know, in order to convert the references to
165
+ these registers into tensors.
166
+
167
+ If you know in advance a list of interesting registers, the `map_registers` function will create a `dict` that maps these registers and their alias to an hot-encoded vector:
168
+
169
+ ```python
170
+ from pcode_graph.gnn_exporter import map_registers
171
+
172
+ reg_map = map_registers("rsp", "rax", "rbx", "rsi", "rdi")
173
+ ```
174
+
175
+ Then you can export your graph to a GNN as a torch_geometric Data object:
176
+
177
+ ```python
178
+ data = graph_to_data(graph, reg_map)
179
+ assert data.x is not None
180
+ ```
181
+
182
+ If you want to help your GNN to deal with different architectures, a helper is provided to encode registers as a mask, depending on their role regarding the calling convention:
183
+
184
+ ```python
185
+ from pcode_graph.gnn_exporter import map_calling_convention_registers
186
+
187
+ reg_map = map_registers(Arch.x86_64)
188
+ print(reg_map)
189
+ {'rax': tensor([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
190
+ 'rdx': tensor([0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
191
+ 'rdi': tensor([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
192
+ 'rsi': tensor([0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
193
+ ...
194
+ ```
195
+
196
+ Many options are provided by the `graph_to_data` function depending on how you want to export graph information into node and edges features. Look at the function documentation for more details.
197
+
198
+
199
+ ### Converting into networkx
200
+
201
+ In case you want to apply some classical graph-theory algorithms, you can use `export_to_networkx` from `nx_exporter` module:
202
+
203
+ ```python
204
+ def export_to_networkx(graph: CDG, ignore_cfg: bool) -> nx.MultiDiGraph: ...
205
+ ```
@@ -0,0 +1,177 @@
1
+ # P-Code Graph
2
+
3
+ This library helps to build, display and vectorize the Control-Data Graph (CDG) of a
4
+ chunk of binary code, using Ghidra P-Code representation.
5
+
6
+ ## Command-line interface
7
+
8
+ A `cdg` tool is provided to play with binary of assembly files.
9
+
10
+ For example to dump the CDG of a function in markdown format:
11
+
12
+ ```console
13
+ (venv) $ cdg mermaid test.o -f func -o graph.md
14
+ ```
15
+
16
+ Or to display the graph of the whole content of an assembly file in HTML:
17
+
18
+ ```console
19
+ (venv) $ cdg html test.asm -o graph.html
20
+ ```
21
+
22
+ ## Tests
23
+
24
+ ### Run tests
25
+
26
+ ```console
27
+ $ python3.14 -m venv venv
28
+ $ source venv/bin/activate
29
+ (venv) $ pip install .[test,torch]
30
+ (venv) $ python -m pytest tests
31
+ ```
32
+
33
+ ### Test output
34
+
35
+ Tests using the `outdir` fixture generate their files in a dedicated output
36
+ directory, in which you can usually find:
37
+
38
+ - the log file
39
+ - the dump of P-Code operations
40
+ - the dump of generated graph in markdown+mermaid format
41
+ - a HTML file to display the related graph in a browser
42
+
43
+ You can display the dataflow graph resulting from a test in its output directory:
44
+
45
+ ```console
46
+ $ firefox out/tests/maker_test/test_call/graph.html
47
+ ```
48
+
49
+ ## API
50
+
51
+ ### Graph representation
52
+
53
+ The `graph.py` module contains the data classes defining a CDG.
54
+
55
+ ```mermaid
56
+ classDiagram
57
+ Graph *-- Node : nodes
58
+ Graph *-- Edge : edges
59
+ Graph: +list[Node] nodes
60
+ Graph: +list[Edge] edges
61
+
62
+ Node: +NodeKinds kind
63
+ Node: +int size
64
+ Node: +int value
65
+ Node: +int register_id
66
+ Node: +str register_name
67
+ Node: +OpCodes opcode
68
+
69
+ Edge: +EdgeKinds kind
70
+ Edge: +int source_node
71
+ Edge: +int destination_node
72
+ Edge: +Optional[int] operand_number
73
+ ```
74
+
75
+ ### Build a graph
76
+
77
+
78
+ We use LIEF library as a default binary loader. To build a graph from binary code:
79
+
80
+ ```python
81
+ from pcode_graph.maker import make_graph_from_binary
82
+ from pcode_graph.pcode import Translator
83
+ from pcode_graph.arch import Arch
84
+ from pcode_graph.lief_importer import iter_functions
85
+
86
+ translator = Translator(Arch.x86_64)
87
+
88
+ for function in iter_functions(Path("a.out")):
89
+ graph = make_graph_from_binary(translator,
90
+ function.content,
91
+ function.address,
92
+ ignore_flag_outputs=True)
93
+ ```
94
+
95
+ Note that you can alternatively collect basic blocks (see lief_importer.py module), or use your own loader and make a graph from an arbitrary piece of binary code.
96
+
97
+ For testing purpose, you can also build it from assembly:
98
+
99
+ ```python
100
+ from pcode_graph.maker import make_graph_from_asm
101
+ from pcode_graph.asm import Assembler
102
+ from pcode_graph.pcode import Translator
103
+ from pcode_graph.arch import Arch
104
+
105
+ translator = Translator(Arch.arm_64)
106
+ assembler = Assembler(Arch.arm_64)
107
+ asm = """
108
+ add x0, x1, x2
109
+ blr x16
110
+ mul x1, x0, x0
111
+ """
112
+
113
+ graph = make_graph_from_asm(assembler, translator, asm)
114
+ ```
115
+
116
+ ### View the graph
117
+
118
+ You can unparse the graph into markdown using the mermaid syntax:
119
+
120
+ ```python
121
+ with open("graph.md", "w") as f:
122
+ f.write(str(graph))
123
+ ```
124
+
125
+ The module `pcode_graph.view` wraps the `pyvis` library to display graphs in a browser:
126
+
127
+ ```python
128
+ from pcode_graph.view import draw_graph
129
+
130
+ draw_graph(graph, "out/graph.html", open_browser=True)
131
+ ```
132
+
133
+ ### Vectorize the graph
134
+
135
+ To convert a graph to tensors, you will need the list of the registers
136
+ you expect your model to know, in order to convert the references to
137
+ these registers into tensors.
138
+
139
+ If you know in advance a list of interesting registers, the `map_registers` function will create a `dict` that maps these registers and their alias to an hot-encoded vector:
140
+
141
+ ```python
142
+ from pcode_graph.gnn_exporter import map_registers
143
+
144
+ reg_map = map_registers("rsp", "rax", "rbx", "rsi", "rdi")
145
+ ```
146
+
147
+ Then you can export your graph to a GNN as a torch_geometric Data object:
148
+
149
+ ```python
150
+ data = graph_to_data(graph, reg_map)
151
+ assert data.x is not None
152
+ ```
153
+
154
+ If you want to help your GNN to deal with different architectures, a helper is provided to encode registers as a mask, depending on their role regarding the calling convention:
155
+
156
+ ```python
157
+ from pcode_graph.gnn_exporter import map_calling_convention_registers
158
+
159
+ reg_map = map_registers(Arch.x86_64)
160
+ print(reg_map)
161
+ {'rax': tensor([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
162
+ 'rdx': tensor([0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
163
+ 'rdi': tensor([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
164
+ 'rsi': tensor([0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
165
+ ...
166
+ ```
167
+
168
+ Many options are provided by the `graph_to_data` function depending on how you want to export graph information into node and edges features. Look at the function documentation for more details.
169
+
170
+
171
+ ### Converting into networkx
172
+
173
+ In case you want to apply some classical graph-theory algorithms, you can use `export_to_networkx` from `nx_exporter` module:
174
+
175
+ ```python
176
+ def export_to_networkx(graph: CDG, ignore_cfg: bool) -> nx.MultiDiGraph: ...
177
+ ```
File without changes
@@ -0,0 +1,3 @@
1
+ from pcode_graph.cli import main
2
+
3
+ main()