Skip to content

Commit 8ab4847

Browse files
committed
feat: add basic support for struct definition through c code
1 parent bf29a86 commit 8ab4847

2 files changed

Lines changed: 81 additions & 1 deletion

File tree

libdestruct/c/struct_parser.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#
2+
# This file is part of libdestruct (https://github.com/mrindeciso/libdestruct).
3+
# Copyright (c) 2024 Roberto Alessandro Bertolini. All rights reserved.
4+
# Licensed under the MIT license. See LICENSE file in the project root for details.
5+
#
6+
7+
from __future__ import annotations
8+
9+
import ctypes
10+
from typing import TYPE_CHECKING
11+
12+
from pycparser import c_ast, c_parser
13+
14+
from libdestruct.common.struct import struct
15+
16+
if TYPE_CHECKING:
17+
from libdestruct.common.obj import obj
18+
19+
20+
def definition_to_type(definition: str) -> type[obj]:
21+
"""Converts a C struct definition to a struct object."""
22+
parser = c_parser.CParser()
23+
24+
ast = parser.parse(definition)
25+
26+
if len(ast.ext) != 1:
27+
raise ValueError("Definition must contain exactly one top object.")
28+
29+
root = ast.ext[0].type
30+
31+
if not isinstance(root, c_ast.Struct):
32+
raise TypeError("Definition must be a struct.")
33+
34+
return struct_to_type(root)
35+
36+
37+
def struct_to_type(struct_node: c_ast.Struct) -> type[struct]:
38+
"""Converts a C struct to a struct object."""
39+
if not isinstance(struct_node, c_ast.Struct):
40+
raise TypeError("Definition must be a struct.")
41+
42+
fields = {}
43+
44+
for decl in struct_node.decls:
45+
name = decl.name
46+
typ = type_decl_to_type(decl.type)
47+
fields[name] = typ
48+
49+
type_name = struct_node.name if struct_node.name else "anon_struct"
50+
51+
return type(type_name, (struct,), {"__annotations__": fields})
52+
53+
54+
def type_decl_to_type(decl: c_ast.TypeDecl) -> type[obj]:
55+
"""Converts a C type declaration to a type."""
56+
if not isinstance(decl, c_ast.TypeDecl):
57+
raise TypeError("Definition must be a type declaration.")
58+
59+
if isinstance(decl.type, c_ast.Struct):
60+
return struct_to_type(decl.type)
61+
62+
if isinstance(decl.type, c_ast.IdentifierType):
63+
return identifier_to_type(decl.type)
64+
65+
raise TypeError("Unsupported type.")
66+
67+
68+
def identifier_to_type(identifier: c_ast.IdentifierType) -> type[obj]:
69+
"""Converts a C identifier to a type."""
70+
if not isinstance(identifier, c_ast.IdentifierType):
71+
raise TypeError("Definition must be an identifier.")
72+
73+
identifier_name = "_".join(identifier.names)
74+
75+
ctypes_name = f"c_{identifier_name}"
76+
77+
if hasattr(ctypes, ctypes_name):
78+
return getattr(ctypes, ctypes_name)
79+
80+
raise ValueError(f"Unsupported identifier: {identifier_name}.")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ classifiers = [
2929
"Typing :: Typed",
3030
]
3131
keywords = ["struct", "c", "debugger", "memory", "reverse", "rev", "reverse-engineering", "script", "libdebug"]
32-
dependencies = ["typing_extensions"]
32+
dependencies = ["typing_extensions", "pycparser"]
3333

3434
[project.optional-dependencies]
3535
dev = [

0 commit comments

Comments
 (0)