1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
| from helper import FAIL import traceback import atexit from config import GHIDRA_BASE
import contextlib from pathlib import Path from typing import Union, ContextManager from pyhidra.core import _analyze_program, _get_compiler_spec, _get_language from pyhidra.converters import * from ghidra.base.project import GhidraProject from typing import Union, Tuple, ContextManager from ghidra.program.model.listing import Program
_project: GhidraProject | None = None _program_pool: dict[str, "Program"] = {}
def _setup_project2( binary_path: Union[str, Path], language: str | None = None, compiler: str | None = None ) -> Tuple["GhidraProject", "Program"]: from java.io import IOException global _project assert _project is not None if binary_path is not None: binary_path = Path(binary_path)
program: "Program" | None = None project = _project if binary_path is not None: try: program = project.openProgram("/", binary_path.name, False) except IOException: program = None pass
if binary_path is not None and program is None: if language is None: program = project.importProgram(binary_path) if program is None: raise RuntimeError(f"Ghidra failed to import '{binary_path}'. Try providing a language manually.") else: lang = _get_language(language) comp = _get_compiler_spec(lang, compiler) program = project.importProgram(binary_path, lang, comp) if program is None: message = f"Ghidra failed to import '{binary_path}'. " if compiler: message += f"The provided language/compiler pair ({language} / {compiler}) may be invalid." else: message += f"The provided language ({language}) may be invalid." raise ValueError(message) project.saveAs(program, "/", binary_path.name, False) assert program is not None return program
def _setup_project( binary_path: Union[str, Path], project_location: Union[str, Path] = None, project_name: str = None, language: str = None, compiler: str = None ) -> Tuple["GhidraProject", "Program"]: from ghidra.base.project import GhidraProject from java.io import IOException if binary_path is not None: binary_path = Path(binary_path) if project_location: project_location = Path(project_location) else: project_location = binary_path.parent if not project_name: project_name = f"{binary_path.name}_ghidra" project_location = project_location / project_name project_location.mkdir(exist_ok=True, parents=True)
program: "Program" = None try: project = GhidraProject.openProject(project_location, project_name, True) if binary_path is not None: program = project.openProgram("/", binary_path.name, False) except IOException: project = GhidraProject.createProject(project_location, project_name, False)
if binary_path is not None and program is None: if language is None: program = project.importProgram(binary_path) if program is None: raise RuntimeError(f"Ghidra failed to import '{binary_path}'. Try providing a language manually.") else: lang = _get_language(language) comp = _get_compiler_spec(lang, compiler) program = project.importProgram(binary_path, lang, comp) if program is None: message = f"Ghidra failed to import '{binary_path}'. " if compiler: message += f"The provided language/compiler pair ({language} / {compiler}) may be invalid." else: message += f"The provided language ({language}) may be invalid." raise ValueError(message) project.saveAs(program, "/", binary_path.name, False)
return project, program
@contextlib.contextmanager def open_program( binary_path: Path, project_location: Union[str, Path] = None, project_name: str = None, analyze=True, language: str = None, compiler: str = None, ) -> ContextManager["FlatProgramAPI"]: """ Opens given binary path in Ghidra and returns FlatProgramAPI object.
:param binary_path: Path to binary file, may be None. :param project_location: Location of Ghidra project to open/create. (Defaults to same directory as binary file) :param project_name: Name of Ghidra project to open/create. (Defaults to name of binary file suffixed with "_ghidra") :param analyze: Whether to run analysis before returning. :param language: The LanguageID to use for the program. (Defaults to Ghidra's detected LanguageID) :param compiler: The CompilerSpecID to use for the program. Requires a provided language. (Defaults to the Language's default compiler) :return: A Ghidra FlatProgramAPI object. :raises ValueError: If the provided language or compiler is invalid. """ global _project, _program_pool
from pyhidra.launcher import PyhidraLauncher, HeadlessPyhidraLauncher
if not PyhidraLauncher.has_launched(): HeadlessPyhidraLauncher().start()
from ghidra.app.script import GhidraScriptUtil from ghidra.program.flatapi import FlatProgramAPI program = _program_pool.get(binary_path.name, None) if program == None: if _project == None: project, program = _setup_project( binary_path, project_location, project_name, language, compiler ) _project = project atexit.register(_project.close) else: program = _setup_project2( binary_path, language, compiler ) _program_pool[binary_path.name] = program GhidraScriptUtil.acquireBundleHostReference()
try: flat_api = FlatProgramAPI(program)
if analyze: _analyze_program(flat_api, program) base_addr = flat_api.toAddr(GHIDRA_BASE) if flat_api.currentProgram.getImageBase() != base_addr: flat_api.currentProgram.setImageBase(base_addr, False) assert flat_api.currentProgram.getImageBase() == base_addr
yield flat_api except Exception as e: FAIL("--- Error while opening program:", e) FAIL(f"binary {binary_path} in {project_location}") traceback.print_exc() finally: GhidraScriptUtil.releaseBundleHostReference()
|