angr / angr/cle

PE base relocations are dropped on every architecture but x86 and x86-64: the relocation tables are keyed on names archinfo does not produce

Abierto
#752 0 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Python
Estrellas
485
Forks
135
Merge medio
2 d 2 h
PR fusionados (30 d)
15

Descripción

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

### Description

`cle/backends/pe/relocation/__init__.py` keys its relocation tables on strings
that `archinfo` does not produce, so every base relocation in a PE image is
dropped on every architecture except x86 and x86-64:

```python
ALL_RELOCATIONS = {
"AMD64": relocation_table_generic,
"arm": relocation_table_generic | relocation_table_arm,
"X86": relocation_table_generic,
"mips": relocation_table_generic | relocation_table_mips,
"RISCV": relocation_table_generic | relocation_table_riscv,
}
```

and the lookup is `get_relocation(self.arch.name, reloc_type)`.
`"arm"`, `"mips"` and `"RISCV"` are not architecture names — `arch.name` is
`ARMEL`, `MIPS32`, `RISCV64` — and `AARCH64` has no entry at all. Of the fourteen
`IMAGE_FILE_MACHINE_*` values the backend can resolve, only two land in the
table:

```python
import archinfo, pefile
from cle.backends.pe.relocation import ALL_RELOCATIONS

for machine in (0x014C, 0x8664, 0xAA64, 0x01C4, 0x0169, 0x01F0, 0x5064):
name = archinfo.arch_from_id(pefile.MACHINE_TYPE[machine]).name
print(f"{machine:#06x} {name:8} in ALL_RELOCATIONS: {name in ALL_RELOCATIONS}")

# 0x014c X86 in ALL_RELOCATIONS: True
# 0x8664 AMD64 in ALL_RELOCATIONS: True
# 0xaa64 AARCH64 in ALL_RELOCATIONS: False
# 0x01c4 ARMEL in ALL_RELOCATIONS: False
# 0x0169 MIPS32 in ALL_RELOCATIONS: False
# 0x01f0 PPC32 in ALL_RELOCATIONS: False
# 0x5064 RISCV64 in ALL_RELOCATIONS: False
```

`get_relocation` returns `None` for every type on those, `__register_relocs`
skips the entry, and the only thing that survives is `log.warning("Unknown reloc
%d on %s", ...)` — once per `(arch, type)` pair, because `complaint_log` dedupes
it. `IMAGE_REL_BASED_DIR64` and `IMAGE_REL_BASED_HIGHLOW` are in
`relocation_table_generic` and would be handled for any of these architectures if
the key matched; they are dropped along with the rest.

The consequence only shows once an image is rebased, which is exactly when it
matters: `PE.__init__` sets `mapped_base = linked_base = ImageBase`, so a
single-object load happens to be correct, and a load that puts the image
anywhere else — a second DLL with the same preferred base, an explicit
`base_addr` — leaves every absolute pointer in it pointing at the old base with
no error.

### Measurement

Counting `IMAGE_BASE_RELOCATION` entries out of the `.reloc` directory directly
and comparing with `len(loader.main_object.relocs)` after
`cle.Loader(path, auto_load_libs=False, main_opts={"backend": "pe"})`. The
remainder on the first two rows is DLL imports; not one base relocation is kept:

| image | `arch.name` | base relocations in the file | relocations cle built |
|---|---|---|---|
| aarch64 executable | `AARCH64` | 1,770 (all `DIR64`) | 173 |
| ARMNT driver | `ARMEL` | 8,777 (124 `HIGHLOW`, 8,653 `THUMB_MOV32`) | 301 |
| x86-64 executable | `AMD64` | 654 (all `DIR64`) | 801 |
| x86 executable | `X86` | 36,780 (all `HIGHLOW`) | 37,021 |

The warning text is what makes this findable: loading an ARM64 Windows image
prints `Unknown reloc 10 on AARCH64` — type 10 is `IMAGE_REL_BASED_DIR64`, which
`relocation_table_generic` implements.

### Reproduction

Any Windows binary for a non-x86 target; this repository's test corpus has none,
so a recipe rather than a path:

```console
$ clang --target=aarch64-pc-windows-msvc -o hello.exe hello.c
```

```python
import cle
ld = cle.Loader("hello.exe", auto_load_libs=False, main_opts={"backend": "pe"})
print(len(ld.main_object.relocs)) # imports only; no base relocations
```

The snippet at the top needs no binary at all.

### Options

1. Key the table on the names `archinfo` produces: `ARMEL`, `ARMHF` and
`ARMCortexM` for the ARM table, `MIPS32` for the MIPS table, `RISCV64` for the
RISC-V table, and `AARCH64` and `PPC32` for the generic table. This is the
whole fix for `DIR64` and `HIGHLOW`, which is 1,770 of 1,770 relocations on the
ARM64 image above.
2. The same, plus the two ARM64-specific types the generic table does not cover,
`IMAGE_REL_BASED_ARM64_MOV32A` (13) and `IMAGE_REL_BASED_ARM64_MOV32T` (14).
The ARMNT image above needs the existing `THUMB_MOV32` for 8,653 of its 8,777,
so the ARM64 equivalents are likely to matter the same way on ARM64 images
that use them.
3. Fail loudly instead. A dropped base relocation cannot be detected downstream,
and `log.warning` deduplicated per `(arch, type)` is one line for 8,777
dropped fixups.

Filed as an issue rather than a pull request because which architectures should
map to which table, and whether an unhandled relocation type should be fatal, are
calls for a maintainer. Note also that `angr.Project` on any of these images
currently raises before this code matters (angr/angr#6794 is the open fix), so a
CFG-level before/after cannot be measured until that lands — the table above is
measured against the files' own `.reloc` directories instead.

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.