PE base relocations are dropped on every architecture but x86 and x86-64: the relocation tables are keyed on names archinfo does not produce
- 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.