Skip to content

COFF undefined externals never reach self.imports, so angr never hooks them #746

Description

@zardus

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Coff._add_relocs resolves a relocation against an undefined external symbol by
allocating it an extern address:

cle_symbol = self.get_symbol(sym_name, produce_extern_symbols=True)
self.relocs.append(reloc_class(self, cle_symbol, patch_offset))

but nothing is ever added to self.imports; the loader body ends with

# FIXME: Expose __imp_* symbols through self.imports

angr.Project._register_object iterates obj.imports and nothing else, so for
a COFF object it iterates an empty dictionary and hooks nothing. The extern
addresses stay raw zero fill, and a call to an undefined external is decoded as
instructions.

extern int one(int);
extern int two(int);
int entry(int x) { return one(x) + two(x); }
x86_64-w64-mingw32-gcc -c -o coff2.o coff2.c
p = angr.Project("coff2.o", auto_load_libs=False, main_opts={"backend": "COFF"})
print(p.loader.main_object.imports)                       # {}
for s in p.loader.extern_object.symbols:
    print(hex(s.rebased_addr), s.name, p.is_hooked(s.rebased_addr))
{}
0x500000 one False
0x500008 two False

CFGFast() then produces a function one at 0x500000 whose single block is the
eight zero bytes of the extern slot, reached by call 0x500000 at 0x400117.
The same happens under symbolic execution: calling the import executes zero fill
instead of a ReturnUnconstrained stub.

For contrast, a relocatable ELF populates imports, so _register_object sees
the same undefined externals and hooks them: the equivalent object built with
gcc -c gets a ReturnUnconstrained at each extern address and CFGFast decodes
nothing there.

In a block-level sweep of a large mixed corpus this accounts for 17 x86 and
x86_64 COFF objects placing CFG blocks in loader-invented memory. Real objects
show it as clearly as the reduced case: on
sha256 69ba352d244c317e01aa7ca99d5391e0785f8198f46691039482607b7032cea7
(main_opts={"backend": "COFF"}) the extern object holds malloc,
FcSerializeAlloc and FcSerializePtr at 0x500000, 0x500008 and 0x500010, none
of them hooked, and CFGFast makes a function of each. On
sha256 df12b231e916f618c8ceab0e2b2a6832bd0f9c34602b81cf48e20372e888a740 there
are 84 of them.

This is a report rather than a pull request because filling in imports changes
what every COFF analysis does — every undefined external becomes a
ReturnUnconstrained stub rather than decodable memory — and because the FIXME
names __imp_* specifically, which suggests a maintainer already has a view on
whether the indirection should be represented and how.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions