Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ofrak_core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Add a settings pane to the OFRAK GUI that supports theming and changing colors ([#309](https://github.com/redballoonsecurity/ofrak/pull/309))
- Add a button and interface in the OFRAK GUI to specifically select any component to run on a resource ([#287](https://github.com/redballoonsecurity/ofrak/pull/287))

### Changed
- `PatchFromSourceModifier` and `FunctionReplaceModifier` automatically include cross-compile headers for target system

### Fixed
- Fixed a bug where clicking "Unpack" or "Identify" (for example) too quickly after loading a large resource causes an error that freezes up the whole GUI ([#297](https://github.com/redballoonsecurity/ofrak/pull/297))
- Bump `importlib-metadata` version to fix import errors ([#296](https://github.com/redballoonsecurity/ofrak/pull/296))
Expand Down
1 change: 1 addition & 0 deletions ofrak_core/ofrak/core/patch_maker/modifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ async def modify(self, resource: Resource, config: PatchFromSourceModifierConfig
patch_maker = PatchMaker(
toolchain=config.toolchain(program_attributes, config.toolchain_config),
build_dir=build_tmp_dir,
auto_add_linux_headers=True,
)

patch_bom = patch_maker.make_bom(
Expand Down
3 changes: 3 additions & 0 deletions ofrak_patch_maker/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## [Unreleased](https://github.com/redballoonsecurity/ofrak/tree/master)

### Added
- Add PatchMaker option to auto-choose a linux cross-compiler header include path

## [4.0.0](https://github.com/redballoonsecurity/ofrak/compare/ofrak-patch-maker-v.3.0.0...ofrak-patch-maker-v.4.0.0)

### Changed
Expand Down
17 changes: 17 additions & 0 deletions ofrak_patch_maker/Dockerstub
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,20 @@ RUN apt-get update && apt-get install -y gcc-avr binutils-avr avr-libc
RUN if [ "$TARGETARCH" = "amd64" ]; then \
apt-get update && apt-get install -y gcc-10-powerpc-linux-gnu; \
fi;


# General cross-compile headers
RUN apt-get update && apt-get install -y --no-install-recommends \
linux-libc-dev-amd64-cross \
linux-libc-dev-arm64-cross \
linux-libc-dev-armel-cross \
linux-libc-dev-armhf-cross \
linux-libc-dev-i386-cross \
linux-libc-dev-m68k-cross \
linux-libc-dev-mips-cross \
linux-libc-dev-mips64-cross \
linux-libc-dev-mips64el-cross \
linux-libc-dev-powerpc-cross \
linux-libc-dev-ppc64-cross \
linux-libc-dev-ppc64el-cross \
libc6-dev-i386
24 changes: 24 additions & 0 deletions ofrak_patch_maker/ofrak_patch_maker/patch_maker.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ def __init__(
base_symbols: Mapping[str, int] = None,
build_dir: str = ".",
logger: logging.Logger = logging.getLogger(),
*,
auto_add_linux_headers: bool = False,
):
"""
The PatchMaker class is responsible for building and applying FEM instance binary data
Expand Down Expand Up @@ -93,6 +95,8 @@ def __init__(
:param base_symbols: maps symbol name to effective address for patches
:param build_dir: output directory for build artifacts
:param logger:
:param auto_add_linux_headers: If the toolchain is able to determine where to find the
linux headers for the target architecture, and they are installed, add them to platform_includes
"""
self._platform_includes = platform_includes
self.build_dir = build_dir
Expand All @@ -112,6 +116,26 @@ def __init__(

self.logger = logger

if auto_add_linux_headers:
if toolchain._linux_xcompile_headers is None:
self.logger.warning(
f"Toolchain could not automatically determine cross-compile headers. It may "
f"be necessary to supply these manually for successful compilation."
)
elif not os.path.exists(toolchain._linux_xcompile_headers):
self.logger.warning(
f"Toolchain automatically determined cross-compile headers at: "
f"{toolchain._linux_xcompile_headers}, but this path does not exist!"
)
else:
if not self._platform_includes:
self._platform_includes = [toolchain._linux_xcompile_headers]
else:
self._platform_includes = [
*self._platform_includes,
toolchain._linux_xcompile_headers,
]

def _extract_symbols(self, path: str) -> Dict[str, Tuple[int, LinkableSymbolType]]:
"""
:param path: path to a program or library binary with symbols
Expand Down
49 changes: 47 additions & 2 deletions ofrak_patch_maker/ofrak_patch_maker/toolchain/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import subprocess
from abc import ABC, abstractmethod
from os.path import join, split
from typing import Dict, Iterable, List, Optional, Tuple, Mapping, Type
from typing import Dict, Iterable, List, Optional, Tuple, Mapping, Type, Any

from ofrak_type import ArchInfo
from ofrak_type import ArchInfo, Endianness
from ofrak_patch_maker.binary_parser.abstract import AbstractBinaryFileParser
from ofrak_patch_maker.toolchain.model import Segment, ToolchainConfig
from ofrak_patch_maker.toolchain.utils import get_repository_config
Expand Down Expand Up @@ -99,6 +99,7 @@ def __init__(

self._assembler_target = self._get_assembler_target(processor)
self._compiler_target = self._get_compiler_target(processor)
self._linux_xcompile_headers = self._get_linux_headers_path(processor)

def __init_subclass__(cls, **kwargs):
Toolchain.toolchain_implementations.append(cls)
Expand Down Expand Up @@ -126,6 +127,50 @@ def _get_assembler_target(self, processor: ArchInfo) -> Optional[str]:
"""
raise NotImplementedError()

def _get_linux_headers_path(self, processor: ArchInfo) -> Optional[str]:
# Cross-compiling headers for things like linux/can.h, asm/types.h, etc.
header_prefix_table: Dict[Tuple[Any, ...], str] = {
(InstructionSet.ARM,): "arm-linux-gnueabihf"
if self._config.hard_float
else "arm-linux-gnueabi",
(InstructionSet.AARCH64,): "aarch64-linux-gnu",
(InstructionSet.X86, BitWidth.BIT_32): "i686-linux-gnu",
(InstructionSet.X86, BitWidth.BIT_64): "x86_64-linux-gnu",
(InstructionSet.MIPS, BitWidth.BIT_32): "mips-linux-gnu",
(
InstructionSet.MIPS,
BitWidth.BIT_64,
Endianness.LITTLE_ENDIAN,
): "mips64el-linux-gnuabi64",
(InstructionSet.MIPS, BitWidth.BIT_64, Endianness.BIG_ENDIAN): "mips64-linux-gnuabi64",
(InstructionSet.PPC, BitWidth.BIT_32): "powerpc-linux-gnu",
(
InstructionSet.PPC,
BitWidth.BIT_64,
Endianness.LITTLE_ENDIAN,
): "powerpc64le-linux-gnuabi64",
(
InstructionSet.PPC,
BitWidth.BIT_64,
Endianness.BIG_ENDIAN,
): "powerpc64-linux-gnuabi64",
(InstructionSet.M68K,): "m68k-linux-gnu",
}

arch_info_parts = [
processor.isa,
processor.bit_width,
processor.endianness,
processor.sub_isa,
processor.processor,
]
while arch_info_parts:
header_prefix = header_prefix_table.get(tuple(arch_info_parts))
if header_prefix is not None:
return f"/usr/{header_prefix}/include"
arch_info_parts.pop()
return None

@abstractmethod
def _get_compiler_target(self, processor: ArchInfo) -> Optional[str]:
"""
Expand Down