* feat(binary-layout): admit ELF32 i386, ARM and MIPS layout profiles inspect_binary_layout refused every ELF32 object, so router and IoT firmware (ARM32, MIPS32) got no layout facts at all. The pwntools adapter now admits EXEC/DYN/REL objects in these profiles: ELF64 x86-64, ELF32 i386, ELF32 little-endian ARM and ELF32 little- or big-endian MIPS. Header fields are read in the declared byte order, the ELF32 header is bounds-checked at 52 bytes, and pwntools' own reported architecture, class and byte order must match the admitted profile. Other machines, classes and byte orders keep an unsupported_target refusal that names the admitted profiles. The output schema admits exactly those architecture combinations, and the Evidence subject now names the reported machine instead of always claiming x86_64. verify:binary:layout cross-compiles i386, ARM, MIPS and MIPSel executables and relocatable objects (never executed) and checks every section name, address, offset and size against GNU readelf through CLI and MCP. The real-binary-layout workflow installs those toolchains. Refs #6934 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(binary-layout): install target libc headers for ELF32 fixtures --no-install-recommends skips the libc6-dev-*-cross packages the cross compilers only recommend, so the i686 fixture fell back to the host's /usr/include and failed to find bits/libc-header-start.h. Install the four target libc development packages explicitly. Refs #6934 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(binary-layout): size the minimum symbol entry by ELF class The relocation reference check required a 24-byte Elf64_Sym entry, so every positive symbol reference in an ELF32 report was refused as unreadable output. Elf32_Sym is 16 bytes; take the minimum from the reported class. A 64-bit table with 16-byte entries is still refused. Refs #6934 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(binary-layout): compare ELF32 symbols and relocations with readelf --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
79 lines
3.4 KiB
YAML
79 lines
3.4 KiB
YAML
name: Real offline binary layout
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
large_mcp:
|
|
description: "Also verify the complete 65,281-section result over MCP (slow)"
|
|
type: boolean
|
|
default: false
|
|
workflow_call:
|
|
inputs:
|
|
build_cache_artifact:
|
|
description: Portable build cache artifact from the calling CI run.
|
|
required: false
|
|
type: string
|
|
default: ""
|
|
remote_cache_write:
|
|
description: Trusted release qualification may publish the remote cache.
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
secrets:
|
|
TURBO_TOKEN:
|
|
description: Remote cache token. Only trusted callers pass it.
|
|
required: false
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
NODE_OPTIONS: --max-old-space-size=768
|
|
HUSKY: "0"
|
|
# A called workflow sees the caller event, so workflow_call is not an event
|
|
# name here. Writes when remote_cache_write is set, or on the default branch
|
|
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
|
# and use the local cache only.
|
|
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
|
REA_VERIFY_LARGE_ELF_MCP: ${{ inputs.large_mcp && '1' || '0' }}
|
|
|
|
jobs:
|
|
real-binary-layout:
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 15
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
with:
|
|
node-version-file: .nvmrc
|
|
- name: Cache npm package downloads
|
|
id: npm-cache
|
|
uses: ./.github/actions/npm-cache
|
|
- run: npm ci
|
|
- uses: ./.github/actions/portable-build-cache
|
|
with:
|
|
build_cache_artifact: ${{ inputs.build_cache_artifact }}
|
|
- name: Install ELF32 cross toolchains for source-owned fixtures
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install --yes --no-install-recommends \
|
|
gcc-i686-linux-gnu gcc-arm-linux-gnueabi gcc-mips-linux-gnu gcc-mipsel-linux-gnu \
|
|
libc6-dev-i386-cross libc6-dev-armel-cross libc6-dev-mips-cross libc6-dev-mipsel-cross
|
|
- name: Acquire exact upstream libraries in an isolated runner environment
|
|
shell: bash
|
|
run: |
|
|
layout_python_root="${RUNNER_TEMP}/rea-pwntools-layout"
|
|
python3 -m venv "${layout_python_root}"
|
|
"${layout_python_root}/bin/python" -m pip install --disable-pip-version-check \
|
|
'pwntools==4.15.0' 'pyelftools==0.33' 'unicorn==2.1.2'
|
|
command -v gcc
|
|
command -v ld
|
|
command -v strip
|
|
command -v strace
|
|
echo "REA_PWNTOOLS_PYTHON=${layout_python_root}/bin/python" >> "${GITHUB_ENV}"
|
|
echo "REA_VERIFY_STRACE_COMMAND=$(command -v strace)" >> "${GITHUB_ENV}"
|
|
- name: Verify source-owned fixtures through public CLI and MCP
|
|
run: npm run verify:binary:layout
|