Merge remote-tracking branch 'origin/Ghidra_11.4'

This commit is contained in:
Ryan Kurtz 2025-05-27 13:06:32 -04:00
commit 3f8fe98862
4 changed files with 55 additions and 7 deletions

View file

@ -16,6 +16,8 @@
package ghidra; package ghidra;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import javax.swing.ToolTipManager; import javax.swing.ToolTipManager;
@ -23,6 +25,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import docking.framework.SplashScreen; import docking.framework.SplashScreen;
import generic.jar.ResourceFile;
import ghidra.base.help.GhidraHelpService; import ghidra.base.help.GhidraHelpService;
import ghidra.framework.Application; import ghidra.framework.Application;
import ghidra.framework.GhidraApplicationConfiguration; import ghidra.framework.GhidraApplicationConfiguration;
@ -77,6 +80,8 @@ public class GhidraRun implements GhidraLaunchable {
log.info("User temp directory: " + Application.getUserTempDirectory()); log.info("User temp directory: " + Application.getUserTempDirectory());
log.info("User cache directory: " + Application.getUserCacheDirectory()); log.info("User cache directory: " + Application.getUserCacheDirectory());
writeLastRun();
initializeTooltips(); initializeTooltips();
updateSplashScreenStatusMessage("Populating Ghidra help..."); updateSplashScreenStatusMessage("Populating Ghidra help...");
@ -99,6 +104,22 @@ public class GhidraRun implements GhidraLaunchable {
mainThread.start(); mainThread.start();
} }
private void writeLastRun() {
// Write the Ghidra installation location to the "lastrun" file
File settingsDir = Application.getUserSettingsDirectory();
ResourceFile rootDir = Application.getApplicationRootDirectory();
if (settingsDir != null && rootDir != null) {
File lastRunFile = new File(settingsDir.getParentFile(), "lastrun");
try {
Files.writeString(lastRunFile.toPath(),
rootDir.getParentFile().getCanonicalPath() + "\n");
}
catch (IOException e) {
log.error("Failed to write 'lastrun' file", e);
}
}
}
private String processArguments(String[] args) { private String processArguments(String[] args) {
//TODO remove this special handling when possible //TODO remove this special handling when possible
if (args.length == 1 && (args[0].startsWith("-D") || args[0].indexOf(" -D") >= 0)) { if (args.length == 1 && (args[0].startsWith("-D") || args[0].indexOf(" -D") >= 0)) {

View file

@ -55,7 +55,7 @@ def start(verbose=False, *, install_dir: Path = None) -> "PyGhidraLauncher":
:param verbose: Enable verbose output during JVM startup (Defaults to False) :param verbose: Enable verbose output during JVM startup (Defaults to False)
:param install_dir: The path to the Ghidra installation directory. :param install_dir: The path to the Ghidra installation directory.
(Defaults to the GHIDRA_INSTALL_DIR environment variable) (Defaults to the GHIDRA_INSTALL_DIR environment variable or "lastrun" file)
:return: The PyGhidraLauncher used to start the JVM :return: The PyGhidraLauncher used to start the JVM
""" """
``` ```
@ -222,7 +222,7 @@ def run_script(
This may be either a Java class or its path. (Defaults to None) This may be either a Java class or its path. (Defaults to None)
:param install_dir: The path to the Ghidra installation directory. This parameter is only :param install_dir: The path to the Ghidra installation directory. This parameter is only
used if Ghidra has not been started yet. used if Ghidra has not been started yet.
(Defaults to the GHIDRA_INSTALL_DIR environment variable) (Defaults to the GHIDRA_INSTALL_DIR environment variable or "lastrun" file)
:param program_name: The name of the program to open in Ghidra. :param program_name: The name of the program to open in Ghidra.
(Defaults to None, which results in the name being derived from "binary_path") (Defaults to None, which results in the name being derived from "binary_path")
:param nested_project_location: If True, assumes "project_location" contains an extra nested :param nested_project_location: If True, assumes "project_location" contains an extra nested
@ -330,6 +330,10 @@ __2.2.0:__
[`pyghidra.run_script()`](#pyghidrarun_script) now accept a `nested_project_location` parameter [`pyghidra.run_script()`](#pyghidrarun_script) now accept a `nested_project_location` parameter
which can be set to `False` to open existing Ghidra projects that were created with the which can be set to `False` to open existing Ghidra projects that were created with the
Ghidra GUI. Ghidra GUI.
* If a Ghidra installation directory is not specified by the `install_dir` parameter or
`GHIDRA_INSTALL_DIR` environment variable, PyGhidra will look for a `lastrun` file in the
Ghidra user settings parent directory, and use the installation directory it specifies. The
`lastrun` file is created by Ghidra 11.4 and later.
__2.1.0:__ __2.1.0:__
* [`pyghidra.open_program()`](#pyghidraopen_program) now accepts a `program_name` parameter, which * [`pyghidra.open_program()`](#pyghidraopen_program) now accepts a `program_name` parameter, which

View file

@ -33,7 +33,7 @@ def start(verbose=False, *, install_dir: Path = None) -> "PyGhidraLauncher":
:param verbose: Enable verbose output during JVM startup (Defaults to False) :param verbose: Enable verbose output during JVM startup (Defaults to False)
:param install_dir: The path to the Ghidra installation directory. :param install_dir: The path to the Ghidra installation directory.
(Defaults to the GHIDRA_INSTALL_DIR environment variable) (Defaults to the GHIDRA_INSTALL_DIR environment variable or "lastrun" file)
:return: The PyGhidraLauncher used to start the JVM :return: The PyGhidraLauncher used to start the JVM
""" """
from pyghidra.launcher import HeadlessPyGhidraLauncher from pyghidra.launcher import HeadlessPyGhidraLauncher
@ -302,7 +302,7 @@ def _flat_api(
This may be either a Java class or its path. (Defaults to None) This may be either a Java class or its path. (Defaults to None)
:param install_dir: The path to the Ghidra installation directory. This parameter is only :param install_dir: The path to the Ghidra installation directory. This parameter is only
used if Ghidra has not been started yet. used if Ghidra has not been started yet.
(Defaults to the GHIDRA_INSTALL_DIR environment variable) (Defaults to the GHIDRA_INSTALL_DIR environment variable or "lastrun" file)
:raises ValueError: If the provided language, compiler or loader is invalid. :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`. :raises TypeError: If the provided loader does not implement `ghidra.app.util.opinion.Loader`.
""" """
@ -378,7 +378,7 @@ def run_script(
This may be either a Java class or its path. (Defaults to None) This may be either a Java class or its path. (Defaults to None)
:param install_dir: The path to the Ghidra installation directory. This parameter is only :param install_dir: The path to the Ghidra installation directory. This parameter is only
used if Ghidra has not been started yet. used if Ghidra has not been started yet.
(Defaults to the GHIDRA_INSTALL_DIR environment variable) (Defaults to the GHIDRA_INSTALL_DIR environment variable or "lastrun" file)
:param program_name: The name of the program to open in Ghidra. :param program_name: The name of the program to open in Ghidra.
(Defaults to None, which results in the name being derived from "binary_path") (Defaults to None, which results in the name being derived from "binary_path")
:param nested_project_location: If True, assumes "project_location" contains an extra nested :param nested_project_location: If True, assumes "project_location" contains an extra nested

View file

@ -152,6 +152,29 @@ def _plugin_lock():
# it will be removed by said process when done # it will be removed by said process when done
pass pass
def _lastrun() -> Path:
lastrun_file: Path = None
lastrun_rel: Path = Path('ghidra/lastrun')
# Check for XDG_CONFIG_HOME environment variable
xdg_config_home: str = os.environ.get('XDG_CONFIG_HOME')
if xdg_config_home:
lastrun_file = Path(xdg_config_home) / lastrun_rel
else:
# Default to platform-specific locations
if platform.system() == 'Windows':
lastrun_file = Path(os.environ['APPDATA']) / lastrun_rel
elif platform.system() == 'Darwin':
lastrun_file = Path.home() / 'Library' / lastrun_rel
else:
lastrun_file = Path.home() / '.config' / lastrun_rel
if lastrun_file is not None and lastrun_file.is_file():
with open(lastrun_file, "r") as file:
return Path(file.readline().strip())
return None
class PyGhidraLauncher: class PyGhidraLauncher:
""" """
@ -164,7 +187,7 @@ class PyGhidraLauncher:
:param verbose: True to enable verbose output when starting Ghidra. :param verbose: True to enable verbose output when starting Ghidra.
:param install_dir: Ghidra installation directory. :param install_dir: Ghidra installation directory.
(Defaults to the GHIDRA_INSTALL_DIR environment variable) (Defaults to the GHIDRA_INSTALL_DIR environment variable or "lastrun" file)
:raises ValueError: If the Ghidra installation directory is invalid. :raises ValueError: If the Ghidra installation directory is invalid.
""" """
self._layout = None self._layout = None
@ -173,7 +196,7 @@ class PyGhidraLauncher:
self._dev_mode = False self._dev_mode = False
self._extension_path = None self._extension_path = None
install_dir = install_dir or os.getenv("GHIDRA_INSTALL_DIR") install_dir = install_dir or os.getenv("GHIDRA_INSTALL_DIR") or _lastrun()
self._install_dir = self._validate_install_dir(install_dir) self._install_dir = self._validate_install_dir(install_dir)
java_home_override = os.getenv("JAVA_HOME_OVERRIDE") java_home_override = os.getenv("JAVA_HOME_OVERRIDE")