From e0a060660b5e77d79a25cb8d59d8d0469fad17d0 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Fri, 29 Aug 2025 13:45:36 -0400 Subject: [PATCH] GP-5961: Deprecating pyghidra.open_program() and pyghidra.run_script() Also fixed a recent regression with calling pyghidra.run_script() --- .../Features/PyGhidra/src/main/py/README.md | 10 +++-- .../PyGhidra/src/main/py/src/pyghidra/api.py | 23 +++++++++++ .../PyGhidra/src/main/py/src/pyghidra/core.py | 39 +++++++------------ .../src/main/py/src/pyghidra/script.py | 7 ++++ 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/Ghidra/Features/PyGhidra/src/main/py/README.md b/Ghidra/Features/PyGhidra/src/main/py/README.md index cb3df45fb8..65e193e2c0 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/README.md +++ b/Ghidra/Features/PyGhidra/src/main/py/README.md @@ -311,9 +311,11 @@ with pyghidra.open_project(os.environ["GHIDRA_PROJECT_DIR"], "ExampleProject", c pyghidra.ghidra_script(f"{os.environ['GHIDRA_SCRIPTS_DIR']}/HelloWorldScript.java", project) ``` -## Legacy API +## Legacy API (deprecated) ### pyghidra.open_program() +__NOTE:__ This function has been deprecated. + To have PyGhidra setup a binary file for you, use the `open_program()` function. This will setup a Ghidra project and import the given binary file as a program for you. @@ -401,6 +403,8 @@ with pyghidra.open_program("binary_file.exe", project_name="MyProject", project_ ``` ### pyghidra.run_script() +__NOTE:__ This function has been deprecated. + PyGhidra can also be used to run an existing Ghidra Python script directly in your native CPython interpreter using the `run_script()` function. However, while you can technically run an existing Ghidra script unmodified, you may run into issues due to differences between Jython 2 and @@ -547,8 +551,8 @@ import pdb_ # imports Ghidra's pdb ``` ## Change History __3.0.0:__ -* Introduced many new functions to the PyGhidra API. PyGhidra 3.0.0 requires Ghidra 12.0 or later - to run. +* Revised the the PyGhidra API. See the [API section](#api) for more details. +* PyGhidra 3.0.0 requires Ghidra 12.0 or later to run. __2.2.1:__ * PyGhidra now launches with the current working directory removed from `sys.path` to prevent diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/api.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/api.py index 723342c353..41efff2346 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/api.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/api.py @@ -20,6 +20,7 @@ from typing import Union, TYPE_CHECKING, Tuple, List, Callable, Any from pyghidra.converters import * # pylint: disable=wildcard-import, unused-wildcard-import if TYPE_CHECKING: + from pyghidra.launcher import PyGhidraLauncher from ghidra.program.model.listing import Program from ghidra.program.util import GhidraProgramUtilities from ghidra.framework.model import Project, DomainFile @@ -32,6 +33,28 @@ if TYPE_CHECKING: from generic.jar import ResourceFile from java.lang import Object # type:ignore @UnresolvedImport +def start(verbose=False, *, install_dir: Path = None) -> "PyGhidraLauncher": + """ + Starts the JVM and fully initializes Ghidra in Headless mode. + + :param verbose: Enable verbose output during JVM startup (Defaults to False) + :param install_dir: The path to the Ghidra installation directory. + (Defaults to the GHIDRA_INSTALL_DIR environment variable or "lastrun" file) + :return: The PyGhidraLauncher used to start the JVM + """ + from pyghidra.launcher import HeadlessPyGhidraLauncher + launcher = HeadlessPyGhidraLauncher(verbose=verbose, install_dir=install_dir) + launcher.start() + return launcher + + +def started() -> bool: + """ + Whether the PyGhidraLauncher has already started. + """ + from pyghidra.launcher import PyGhidraLauncher + return PyGhidraLauncher.has_launched() + def open_project( path: Union[str, Path], name: str, diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/core.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/core.py index 3c426dd02b..04938d7034 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/core.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/core.py @@ -14,42 +14,18 @@ # limitations under the License. ## import contextlib +import warnings from typing import Union, TYPE_CHECKING, Tuple, ContextManager, List, Optional from pyghidra.converters import * # pylint: disable=wildcard-import, unused-wildcard-import if TYPE_CHECKING: - from pyghidra.launcher import PyGhidraLauncher from ghidra.base.project import GhidraProject from ghidra.program.flatapi import FlatProgramAPI from ghidra.program.model.lang import CompilerSpec, Language, LanguageService from ghidra.program.model.listing import Program - -def start(verbose=False, *, install_dir: Path = None) -> "PyGhidraLauncher": - """ - Starts the JVM and fully initializes Ghidra in Headless mode. - - :param verbose: Enable verbose output during JVM startup (Defaults to False) - :param install_dir: The path to the Ghidra installation directory. - (Defaults to the GHIDRA_INSTALL_DIR environment variable or "lastrun" file) - :return: The PyGhidraLauncher used to start the JVM - """ - from pyghidra.launcher import HeadlessPyGhidraLauncher - launcher = HeadlessPyGhidraLauncher(verbose=verbose, install_dir=install_dir) - launcher.start() - return launcher - - -def started() -> bool: - """ - Whether the PyGhidraLauncher has already started. - """ - from pyghidra.launcher import PyGhidraLauncher - return PyGhidraLauncher.has_launched() - - def _get_language(lang_id: str) -> "Language": from ghidra.program.util import DefaultLanguageService from ghidra.program.model.lang import LanguageID, LanguageNotFoundException @@ -230,7 +206,12 @@ def open_program( :raises ValueError: If the provided language, compiler or loader is invalid. :raises TypeError: If the provided loader does not implement `ghidra.app.util.opinion.Loader`. """ - + warnings.warn( + "open_program() is deprecated, use open_project() and program_context() or program_loader() instead.", + DeprecationWarning, + stacklevel=3 + ) + from pyghidra.launcher import PyGhidraLauncher, HeadlessPyGhidraLauncher if not PyGhidraLauncher.has_launched(): @@ -386,6 +367,12 @@ def run_script( :raises ValueError: If the provided language, compiler or loader is invalid. :raises TypeError: If the provided loader does not implement `ghidra.app.util.opinion.Loader`. """ + warnings.warn( + "run_script() is deprecated, use open_project() and ghidra_script() instead.", + DeprecationWarning, + stacklevel=3 + ) + script_path = str(script_path) args = binary_path, project_location, project_name, verbose, analyze, lang, compiler, loader, program_name, nested_project_location with _flat_api(*args, install_dir=install_dir) as script: diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/script.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/script.py index 70cadcd7c4..3bda666a8b 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/script.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/script.py @@ -204,6 +204,13 @@ class PyGhidraScript(dict): def get_static_view(self): return _StaticMap(self) + + def set(self, state, monitor, writer, error_writer): + """ + see GhidraScript.set + """ + from ghidra.app.script import ScriptControls + self._script.set(state, ScriptControls(writer, error_writer, monitor)) def run(self, script_path: str = None, script_args: List[str] = None): """