Skip to content

Commit

Permalink
Feat: Support Typedefs
Browse files Browse the repository at this point in the history
  • Loading branch information
mahaloz committed Jul 28, 2024
1 parent 0b1d0d7 commit 5ebf17b
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 4 deletions.
3 changes: 2 additions & 1 deletion libbs/api/artifact_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from libbs.artifacts import (
Artifact, Comment, Enum, FunctionHeader, Function, FunctionArgument,
GlobalVariable, Patch, StackVariable, Struct, StructMember
GlobalVariable, Patch, StackVariable, Struct, StructMember, Typedef
)

if typing.TYPE_CHECKING:
Expand Down Expand Up @@ -44,6 +44,7 @@ def __init__(self, artifact_cls, deci: "DecompilerInterface", error_on_duplicate
GlobalVariable: (self._deci._set_global_variable, self._deci._get_global_var, self._deci._global_vars, self._deci._del_global_var),
Struct: (self._deci._set_struct, self._deci._get_struct, self._deci._structs, self._deci._del_struct),
Enum: (self._deci._set_enum, self._deci._get_enum, self._deci._enums, self._deci._del_enum),
Typedef: (self._deci._set_typedef, self._deci._get_typedef, self._deci._typedefs, self._deci._del_typedef),
Comment: (self._deci._set_comment, self._deci._get_comment, self._deci._comments, self._deci._del_comment),
Patch: (self._deci._set_patch, self._deci._get_patch, self._deci._patches, self._deci._del_patch)
}
Expand Down
23 changes: 22 additions & 1 deletion libbs/api/decompiler_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
Artifact,
Function, FunctionHeader, StackVariable,
Comment, GlobalVariable, Patch,
Enum, Struct, FunctionArgument, Context, Decompilation
Enum, Struct, FunctionArgument, Context, Decompilation, Typedef
)
from libbs.decompilers import SUPPORTED_DECOMPILERS, ANGR_DECOMPILER, \
BINJA_DECOMPILER, IDA_DECOMPILER, GHIDRA_DECOMPILER
Expand Down Expand Up @@ -97,6 +97,7 @@ def __init__(
self.structs = ArtifactDict(Struct, self, error_on_duplicate=error_on_artifact_duplicates)
self.patches = ArtifactDict(Patch, self, error_on_duplicate=error_on_artifact_duplicates)
self.global_vars = ArtifactDict(GlobalVariable, self, error_on_duplicate=error_on_artifact_duplicates)
self.typedefs = ArtifactDict(Typedef, self, error_on_duplicate=error_on_artifact_duplicates)

self._decompiler_available = decompiler_available
# override the file-saved config when one is passed in manually, otherwise
Expand Down Expand Up @@ -545,6 +546,26 @@ def _enums(self) -> Dict[str, Enum]:
"""
return {}

# typedefs
def _set_typedef(self, typedef: Typedef, **kwargs) -> bool:
return False

def _get_typedef(self, name) -> Optional[Typedef]:
return None

def _del_typedef(self, name) -> bool:
return False

def _typedefs(self) -> Dict[str, Typedef]:
"""
Returns a dict of libbs.Typedef that contain the name of the typedefs in the decompiler.
Note: this does not contain the live artifacts of the Artifact, only the minimum knowledge to that the Artifact
exists. To get live artifacts, use the singleton function of the same name.
@return:
"""
return {}

# patches
def _set_patch(self, patch: Patch, **kwargs) -> bool:
return False
Expand Down
3 changes: 3 additions & 0 deletions libbs/artifacts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .stack_variable import StackVariable
from .struct import Struct, StructMember
from .context import Context
from .typedef import Typedef

ART_NAME_TO_CLS = {
Function.__name__: Function,
Expand All @@ -22,4 +23,6 @@
StructMember.__name__: StructMember,
Patch.__name__: Patch,
Decompilation.__name__: Decompilation,
Context.__name__: Context,
Typedef.__name__: Typedef,
}
40 changes: 40 additions & 0 deletions libbs/artifacts/typedef.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from typing import Optional

from .artifact import Artifact


class Typedef(Artifact):
"""
Describe a typedef. As an example:
typedef struct MyStruct {
int a;
int b;
} my_struct_t;
name="my_struct_t"
type="MyStruct"
Another example:
typedef int my_int_t;
name="my_int_t"
type="int"
"""

__slots__ = Artifact.__slots__ + (
"name",
"type",
)

def __init__(
self,
name: str = None,
type_: Optional[str] = None,
**kwargs,
):
super().__init__(**kwargs)
self.name: str = name
self.type: str = type_

def __str__(self):
return f"<TypeDef: {self.name}={self.type}>"
54 changes: 53 additions & 1 deletion libbs/decompilers/ida/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import libbs
from libbs.artifacts import (
Struct, FunctionHeader, FunctionArgument, StackVariable, Function, GlobalVariable, Enum, Artifact, Context
Struct, FunctionHeader, FunctionArgument, StackVariable, Function, GlobalVariable, Enum, Artifact, Context, Typedef
)

from PyQt5.Qt import QObject
Expand Down Expand Up @@ -941,6 +941,58 @@ def set_enum(bs_enum: Enum):

return True

#
# Typedefs
#

@execute_write
def typedefs() -> typing.Dict[str, Typedef]:
typedefs = {}
idati = idaapi.get_idati()
for ord_num in range(ida_typeinf.get_ordinal_qty(idati)):
tif = ida_typeinf.tinfo_t()
success = tif.get_numbered_type(idati, ord_num)
if not success:
continue

# TODO: this is incorrect!
if not tif.is_typeref():
continue

name = tif.get_type_name()
if not name:
continue

type_name = tif.get_next_type_name()
if not type_name:
continue

typedefs[name] = Typedef(name=name, type_=type_name)

return typedefs

@execute_write
def typedef(name) -> typing.Optional[Typedef]:
idati = idaapi.get_idati()
tif = ida_typeinf.tinfo_t()
success = tif.get_named_type(idati, name)
if not success:
return None

type_name = tif.get_final_type_name()
return Typedef(name=name, type_=type_name)

@execute_write
def set_typedef(bs_typedef: Typedef):
base_type_tif = convert_type_str_to_ida_type(bs_typedef.type)
if base_type_tif is None:
return False

idati = idaapi.get_idati()
typedef_tif = ida_typeinf.tinfo_t()
typedef_tif.create_typedef(idati, base_type_tif.get_final_type_name())
typedef_tif.set_named_type(idati, bs_typedef.name, ida_typeinf.NTF_REPLACE)
return True

#
# IDA GUI r/w
Expand Down
12 changes: 11 additions & 1 deletion libbs/decompilers/ida/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from libbs.api.decompiler_interface import DecompilerInterface
from libbs.artifacts import (
StackVariable, Function, FunctionHeader, Struct, Comment, GlobalVariable, Enum, Patch, Artifact, Decompilation,
Context
Context, Typedef
)
from libbs.api.decompiler_interface import requires_decompilation
from . import compat
Expand Down Expand Up @@ -334,6 +334,16 @@ def _enums(self) -> Dict[str, Enum]:
"""
return compat.enums()

# typedefs
def _set_typedef(self, typedef: Typedef, **kwargs) -> bool:
return compat.set_typedef(typedef)

def _get_typedef(self, name) -> Optional[Typedef]:
return compat.typedef(name)

def _typedefs(self) -> Dict[str, Typedef]:
return compat.typedefs()

# patches
def _set_patch(self, patch: Patch, **kwargs) -> bool:
idaapi.patch_bytes(patch.addr, patch.bytes)
Expand Down

0 comments on commit 5ebf17b

Please sign in to comment.