Skip to content

E9Patch and Intel Control-Flow Enforcement Technology (CET) #55

@GJDuck

Description

@GJDuck

Currently, E9Patch will disable all Intel Control-Flow Enforcement Technology (CET) features in the rewritten binary. This is because there are several problems with the interaction of E9Tool/E9Patch and Intel CET, specifically:

Indirect Branch Tracking (IBT):

  • E9Patch cannot rewrite the endbr64 instruction. If the endbr64 is replaced by a jump-to-trampoline, then an indirect call/jump to this location will fail.
  • E9Tool's if f() goto instrumentation will fail unless the goto happens to target an endbr64 instruction. This is because the goto instrumentation is compiled down into an indirect jump in the rewritten binary.
    Edit: the indirect jump can use the NOTRACK prefix, which disables IBT for the instruction.

Shadow Stack (SHSTK):

  • In trampoline code, E9Patch translates call instructions into an explicit push+jump combination. This ensures the "original" return address (i.e., as if the program were uninstrumented) is pushed onto the stack, as opposed to a return address belonging to the trampoline. However, this will break SHSTK since the push+jump will not push anything on to the shadow stack.

Possible Solutions

  • Never rewrite/replace endbr64 instructions.
  • Disable CET only if if f() goto (or other incompatible feature) is used.
  • Implement calls as calls in trampoline code (as a user option). This may break transparency, but most programs do not care.

Intel CET Opportunities

Intel CET also helps the E9Tool/E9Patch toolchain:

  • E9Tool's control-flow recovery analysis is more accurate since some indirect jump/call targets are explicitly annotated with the endbr64 instruction. This is already implemented.
  • E9Tool can feasibly detect when an instruction is never a jump/call target. This information could be used by E9Patch to further optimize the coverage/speed of the rewritten binary. For example, if the current instruction cannot be patched and is definitely not a target, then the previous instruction could be patched instead. Similarly, if the next instruction is not a target, it can be overwritten without the need for instruction punning.
    Edit: unfortunately this will not work if the program uses the NOTRACK prefix, which seems to be common for things like switches (to avoid endbr64 proliferation).

Update (18/06/22): I had overlooked the optional NOTRACK prefix for indirect calls/jumps, so the comment is updated accordingly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions