mirror of
https://github.com/isledecomp/isle.git
synced 2025-10-23 16:34:06 +00:00
Add Ghidra function import script (#909)
* Add draft for Ghidra function import script * feature: Basic PDB analysis [skip ci] This is a draft with a lot of open questions left. Please do not merge * Refactor: Introduce submodules and reload remedy * refactor types and make them Python 3.9 compatible * run black * WIP: save progress * fix types and small type safety violations * fix another Python 3.9 syntax incompatibility * Implement struct imports [skip ci] - This code is still in dire need of refactoring and tests - There are only single-digit issues left, and 2600 functions can be imported - The biggest remaining error is mismatched stacks * Refactor, implement enums, fix lots of bugs * fix Python 3.9 issue * refactor: address review comments Not sure why VS Code suddenly decides to remove some empty spaces, but they don't make sense anyway * add unit tests for new type parsers, fix linter issue * refactor: db access from pdb_extraction.py * Fix stack layout offset error * fix: Undo incorrect reference change * Fix CI issue * Improve READMEs (fix typos, add information) --------- Co-authored-by: jonschz <jonschz@users.noreply.github.com>
This commit is contained in:
100
tools/ghidra_scripts/lego_util/ghidra_helper.py
Normal file
100
tools/ghidra_scripts/lego_util/ghidra_helper.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""A collection of helper functions for the interaction with Ghidra."""
|
||||
|
||||
import logging
|
||||
|
||||
from lego_util.exceptions import (
|
||||
ClassOrNamespaceNotFoundInGhidraError,
|
||||
TypeNotFoundInGhidraError,
|
||||
MultipleTypesFoundInGhidraError,
|
||||
)
|
||||
|
||||
# Disable spurious warnings in vscode / pylance
|
||||
# pyright: reportMissingModuleSource=false
|
||||
|
||||
from ghidra.program.model.data import PointerDataType
|
||||
from ghidra.program.model.data import DataTypeConflictHandler
|
||||
from ghidra.program.flatapi import FlatProgramAPI
|
||||
from ghidra.program.model.data import DataType
|
||||
from ghidra.program.model.symbol import Namespace
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_ghidra_type(api: FlatProgramAPI, type_name: str):
|
||||
"""
|
||||
Searches for the type named `typeName` in Ghidra.
|
||||
|
||||
Raises:
|
||||
- NotFoundInGhidraError
|
||||
- MultipleTypesFoundInGhidraError
|
||||
"""
|
||||
result = api.getDataTypes(type_name)
|
||||
if len(result) == 0:
|
||||
raise TypeNotFoundInGhidraError(type_name)
|
||||
if len(result) == 1:
|
||||
return result[0]
|
||||
|
||||
raise MultipleTypesFoundInGhidraError(type_name, result)
|
||||
|
||||
|
||||
def add_pointer_type(api: FlatProgramAPI, pointee: DataType) -> DataType:
|
||||
new_data_type = PointerDataType(pointee)
|
||||
new_data_type.setCategoryPath(pointee.getCategoryPath())
|
||||
result_data_type = (
|
||||
api.getCurrentProgram()
|
||||
.getDataTypeManager()
|
||||
.addDataType(new_data_type, DataTypeConflictHandler.KEEP_HANDLER)
|
||||
)
|
||||
if result_data_type is not new_data_type:
|
||||
logger.debug(
|
||||
"New pointer replaced by existing one. Fresh pointer: %s (class: %s)",
|
||||
result_data_type,
|
||||
result_data_type.__class__,
|
||||
)
|
||||
return result_data_type
|
||||
|
||||
|
||||
def get_ghidra_namespace(
|
||||
api: FlatProgramAPI, namespace_hierachy: list[str]
|
||||
) -> Namespace:
|
||||
namespace = api.getCurrentProgram().getGlobalNamespace()
|
||||
for part in namespace_hierachy:
|
||||
namespace = api.getNamespace(namespace, part)
|
||||
if namespace is None:
|
||||
raise ClassOrNamespaceNotFoundInGhidraError(namespace_hierachy)
|
||||
return namespace
|
||||
|
||||
|
||||
def create_ghidra_namespace(
|
||||
api: FlatProgramAPI, namespace_hierachy: list[str]
|
||||
) -> Namespace:
|
||||
namespace = api.getCurrentProgram().getGlobalNamespace()
|
||||
for part in namespace_hierachy:
|
||||
namespace = api.getNamespace(namespace, part)
|
||||
if namespace is None:
|
||||
namespace = api.createNamespace(namespace, part)
|
||||
return namespace
|
||||
|
||||
|
||||
def sanitize_name(name: str) -> str:
|
||||
"""
|
||||
Takes a full class or function name and replaces characters not accepted by Ghidra.
|
||||
Applies mostly to templates and names like `vbase destructor`.
|
||||
"""
|
||||
new_class_name = (
|
||||
name.replace("<", "[")
|
||||
.replace(">", "]")
|
||||
.replace("*", "#")
|
||||
.replace(" ", "_")
|
||||
.replace("`", "'")
|
||||
)
|
||||
if "<" in name:
|
||||
new_class_name = "_template_" + new_class_name
|
||||
|
||||
if new_class_name != name:
|
||||
logger.warning(
|
||||
"Class or function name contains characters forbidden by Ghidra, changing from '%s' to '%s'",
|
||||
name,
|
||||
new_class_name,
|
||||
)
|
||||
return new_class_name
|
Reference in New Issue
Block a user