Skip to content

ghidra_analyzer.py

ofrak_ghidra.components.ghidra_analyzer

GhidraCodeRegionModifier (Modifier, OfrakGhidraMixin)

modify(self, resource, config=None) async

Modify the given resource.

Users should not call this method directly; rather, they should run Resource.run.

Parameters:

Name Type Description Default
resource Resource required
config

Optional config for modification. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run.

None
Source code in ofrak_ghidra/components/ghidra_analyzer.py
async def modify(self, resource: Resource, config=None):
    code_region = await resource.view_as(CodeRegion)
    ghidra_project = await OfrakGhidraMixin.get_ghidra_project(resource)

    ofrak_code_regions = await ghidra_project.resource.get_descendants_as_view(
        v_type=CodeRegion, r_filter=ResourceFilter(tags=[CodeRegion])
    )

    backend_code_regions_json = await self.get_code_regions_script.call_script(resource)
    backend_code_regions = []

    for cr_j in backend_code_regions_json:
        cr = CodeRegion(cr_j["start"], cr_j["size"])
        backend_code_regions.append(cr)

    ofrak_code_regions = sorted(ofrak_code_regions, key=lambda cr: cr.virtual_address)
    backend_code_regions = sorted(backend_code_regions, key=lambda cr: cr.virtual_address)

    program_r = await resource.get_only_ancestor(ResourceFilter.with_tags(Program))
    # We only want to adjust the address of a CodeRegion if the original binary is position-independent.
    # Implement PIE-detection for other file types as necessary.
    if program_r.has_tag(Elf):
        elf_header = await program_r.get_only_descendant_as_view(
            ElfHeader, r_filter=ResourceFilter(tags=[ElfHeader])
        )
        if elf_header is not None and elf_header.e_type == ElfType.ET_DYN.value:
            ghidra_project_r = await resource.get_only_ancestor(
                ResourceFilter.with_tags(GhidraProject)
            )
            ghidra_project_v = await ghidra_project_r.view_as(GhidraProject)

            code_region = await resource.view_as(CodeRegion)
            if ghidra_project_v.base_address:
                new_cr = CodeRegion(
                    code_region.virtual_address + ghidra_project_v.base_address,
                    code_region.size,
                )
                code_region.resource.add_view(new_cr)
            elif len(ofrak_code_regions) > 0:
                relative_va = (
                    code_region.virtual_address - ofrak_code_regions[0].virtual_address
                )

                for backend_cr in backend_code_regions:
                    backend_relative_va = (
                        backend_cr.virtual_address - backend_code_regions[0].virtual_address
                    )

                    if (
                        backend_relative_va == relative_va
                        and backend_cr.size == code_region.size
                    ):
                        resource.add_view(backend_cr)
                        ghidra_project_r.add_view(
                            GhidraProject(
                                project_url=ghidra_project_v.project_url,
                                ghidra_url=ghidra_project_v.ghidra_url,
                                base_address=backend_cr.virtual_address
                                - code_region.virtual_address,
                            )
                        )
                        await ghidra_project_r.save()

                LOGGER.debug(
                    f"No code region with relative offset {relative_va} and size {code_region.size} found in Ghidra"
                )
            else:
                LOGGER.debug("No OFRAK code regions to match in Ghidra")
            await resource.save()

GhidraCustomLoadAnalyzer (GhidraProjectAnalyzer)

analyze(self, resource, config=None) async

Analyze a resource for to extract specific ResourceAttributes.

Users should not call this method directly; rather, they should run Resource.run or Resource.analyze.

Parameters:

Name Type Description Default
resource Resource

The resource that is being analyzed

required
config Optional[ofrak_ghidra.components.ghidra_analyzer.GhidraProjectConfig]

Optional config for analyzing. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run.

None

Returns:

Type Description
GhidraProject

The analysis results

Source code in ofrak_ghidra/components/ghidra_analyzer.py
async def analyze(
    self, resource: Resource, config: Optional[GhidraProjectConfig] = None
) -> GhidraProject:
    arch_info: ArchInfo = await resource.analyze(ProgramAttributes)
    mem_blocks = await self._get_memory_blocks(await resource.view_as(Program))
    use_existing = config.use_existing if config is not None else False

    async with self._prepare_ghidra_project(resource) as (ghidra_project, full_fname):
        program_name = await self._do_ghidra_import(
            ghidra_project,
            full_fname,
            use_existing=use_existing,
            use_binary_loader=True,
            processor=arch_info,
            blocks=mem_blocks,
        )
        await self._do_ghidra_analyze_and_serve(
            ghidra_project,
            program_name,
            skip_analysis=config is not None,
        )

        return GhidraProject(
            ghidra_project, f"http://{GHIDRA_SERVER_HOST}:{GHIDRA_SERVER_PORT}"
        )

GhidraProgramLoadConfig (ComponentConfig) dataclass

Config for GhidraProjectAnalyzer to pass in a pre-analyzed Ghidra project for a binary as a Ghidra Zip file.

A Ghidra Zip File can be exported from Ghidra's project window, right-clicking on an analyzed file and "Export...". Then select the Ghidra Zip File format and save the file. This will create a .gzf file that you can import with this GhidraProjectConfig.

GhidraProjectAnalyzer (Analyzer)

Use Ghidra backend to create project for and analyze a binary. This analyzer must run before Ghidra analysis can be accessed from OFRAK. This Analyzer can either create a new project and new analysis for a binary or, if a config is passed to it, load an existing Ghidra project.

analyze(self, resource, config=None) async

Analyze a resource for to extract specific ResourceAttributes.

Users should not call this method directly; rather, they should run Resource.run or Resource.analyze.

Parameters:

Name Type Description Default
resource Resource

The resource that is being analyzed

required
config Optional[ofrak_ghidra.components.ghidra_analyzer.GhidraProjectConfig]

Optional config for analyzing. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run.

None

Returns:

Type Description
GhidraProject

The analysis results

Source code in ofrak_ghidra/components/ghidra_analyzer.py
async def analyze(
    self, resource: Resource, config: Optional[GhidraProjectConfig] = None
) -> GhidraProject:
    gzf = config.ghidra_zip_file if config is not None else None
    binary_fname = config.name if config is not None else None

    # if passing a name for the file, by default don't overwrite an existing file
    # of the same name in the ghidra project.
    use_existing = config.use_existing if config is not None else binary_fname is not None

    async with self._prepare_ghidra_project(resource, gzf, binary_fname) as (
        ghidra_project,
        full_fname,
    ):
        program_name = await self._do_ghidra_import(
            ghidra_project, full_fname, use_existing=use_existing, use_binary_loader=False
        )
        await self._do_ghidra_analyze_and_serve(
            ghidra_project,
            program_name,
            skip_analysis=config is not None,
        )

        return GhidraProject(
            ghidra_project, f"http://{GHIDRA_SERVER_HOST}:{GHIDRA_SERVER_PORT}"
        )

GhidraProjectConfig (ComponentConfig) dataclass

Config for GhidraProjectAnalyzer to pass in a pre-analyzed Ghidra project for a binary as a Ghidra Zip file.

A Ghidra Zip File can be exported from Ghidra's project window, right-clicking on an analyzed file and "Export...". Then select the Ghidra Zip File format and save the file. This will create a .gzf file that you can import with this GhidraProjectConfig.