diff --git a/Ghidra/Features/PyGhidra/src/main/py/README.md b/Ghidra/Features/PyGhidra/src/main/py/README.md index d39b8840e2..3561e6379d 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/README.md +++ b/Ghidra/Features/PyGhidra/src/main/py/README.md @@ -305,3 +305,12 @@ underscore appended to the package name: import pdb # imports Python's pdb import pdb_ # imports Ghidra's pdb ``` +## Change History +__2.0.1:__ +* PyGhidra now respects the `application.settingsdir` property set in Ghidra's `launch.properties` + file. +* Fixed an issue that prevented accessing Java getters/setters as properties on non-public classes. +* PyGhidra can now find modules that live in directories specified by Ghidra's _"Bundle Manager"_. + +__2.0.0:__ +* Initial Release. \ No newline at end of file diff --git a/Ghidra/Features/PyGhidra/support/pyghidra_launcher.py b/Ghidra/Features/PyGhidra/support/pyghidra_launcher.py index 8994a821de..7ed7bef0fd 100644 --- a/Ghidra/Features/PyGhidra/support/pyghidra_launcher.py +++ b/Ghidra/Features/PyGhidra/support/pyghidra_launcher.py @@ -29,24 +29,49 @@ def get_application_properties(install_dir: Path) -> Dict[str, str]: with open(app_properties_path, 'r') as f: for line in f: line = line.strip() - if line.startswith('#') or line.startswith('!'): + if not line or line.startswith('#') or line.startswith('!'): continue key, value = line.split('=', 1) if key: props[key] = value return props +def get_launch_properties(install_dir: Path, dev: bool) -> List[str]: + if dev: + launch_properties_path: Path = install_dir / 'Ghidra' / 'RuntimeScripts' / 'Common' / 'support' / 'launch.properties' + else: + launch_properties_path: Path = install_dir / 'support' / 'launch.properties' + props: List[str] = [] + with open(launch_properties_path, 'r') as f: + for line in f: + line = line.strip() + if not line or line.startswith('#') or line.startswith('!'): + continue + props.append(line) + return props + def get_user_settings_dir(install_dir: Path, dev: bool) -> Path: - props: Dict[str, str] = get_application_properties(install_dir) - app_name: str = props['application.name'].replace(' ', '').lower() - app_version: str = props['application.version'] - app_release_name: str = props['application.release.name'] + app_props: Dict[str, str] = get_application_properties(install_dir) + app_name: str = app_props['application.name'].replace(' ', '').lower() + app_version: str = app_props['application.version'] + app_release_name: str = app_props['application.release.name'] versioned_name: str = f'{app_name}_{app_version}_{app_release_name}' if dev: versioned_name += f'_location_{install_dir.parent.name}' + + # Check for application.settingsdir in launch.properties + for launch_prop in get_launch_properties(install_dir, dev): + if launch_prop.startswith('VMARGS=-Dapplication.settingsdir='): + application_settingsdir = launch_prop[launch_prop.rindex('=')+1:] + if application_settingsdir: + return Path(application_settingsdir) / app_name / versioned_name + + # Check for XDG_CONFIG_HOME environment variable xdg_config_home: str = os.environ.get('XDG_CONFIG_HOME') if xdg_config_home: return Path(xdg_config_home) / app_name / versioned_name + + # Default to platform-specific locations if platform.system() == 'Windows': return Path(os.environ['APPDATA']) / app_name / versioned_name if platform.system() == 'Darwin': @@ -136,6 +161,8 @@ def get_saved_python_cmd(install_dir: Path, dev: bool) -> List[str]: def save_python_cmd(install_dir: Path, python_cmd: List[str], dev: bool) -> None: user_settings_dir: Path = get_user_settings_dir(install_dir, dev) + if not user_settings_dir.is_dir(): + user_settings_dir.mkdir(parents=True, exist_ok=True) save_file: Path = user_settings_dir / 'python_command.save' with open(save_file, 'w') as f: f.write('\n'.join(python_cmd) + '\n')