Skip to content
Snippets Groups Projects
user avatar
Anh authored
d2a07554
History

CFI

Licence

See the LICENCE file for the licensing information.

Description

Code that uses the IRDB_SDK and Zipr_SDK to implement two forms of CFI.

  • scfi -- selective CFI allows the user to perform CFI by (selectively) instrumenting jump instructions. The selective nature is appealing for situations when indirect jumps can be proven safe, and thus need no instrumentation and thus can enhance performance.
  • xeon -- eXEcutable nONces are used to place nonces inline after an indirect target. These nonces are executed after the an indirect branch is taken, which takes time, however it allos call/return pairs to be matched.
    Matching call/ret pairs reduces significant branch predictor overheads, overall having better performance in most programs.

Building from source

Step 0: Install the Zipr binary rewriting infrastructure

See https://git.zephyr-software.com/opensrc/zipr. Before continuing, be sure to prepare Zipr's environment by doing the following:

cd /path/to/zipr && . set_env_vars

Step 1: Testing Zipr

Test the binary rewriting infrastructure by rewriting Linux's ls binary:

$PSZ /bin/ls /tmp/ls.zipr -c rida

Your terminal's output should look like this:

Using Zipr backend.
Detected ELF shared object.
Performing step rida [dependencies=mandatory] ...Done.  Successful.
Performing step pdb_register [dependencies=mandatory] ...Done.  Successful.
Performing step fill_in_cfg [dependencies=unknown] ...Done.  Successful.
Performing step fill_in_indtargs [dependencies=unknown] ...Done.  Successful.
Performing step fix_calls [dependencies=unknown] ...Done.  Successful.
Performing step zipr [dependencies=none] ...Done.  Successful.

Invoke the rewritten version and make sure it runs normally:

./tmp/ls.zipr

Step 2: Checkout and build CFI

git clone https://git.zephyr-software.com/opensrc/cfi
cd cfi
scons 
# or scons debug=1
# or scons -j3
# or scons -j3 debug=1

Step 3: Instrument program with CFI

Use ether selective CFI or Xeon

Selective CFI

To instrument a program, you need to ask Zipr to rewrite the binary and include the CFI transform. Here are some examples that have been tested on Ubuntu 20's /bin/ls program.

# 1) Basic CFI with 4-byte nonces.  One nonce is used for all indirect branch targets (IBTs).
$PSZ -c rida -c move_globals -o --elftables-only -c selective_cfi -o "--multimodule --nonce-size 4" -o fix_calls:--fix-all  <in pgm> <out pgm>

# 2) Basic CFI with 4-byte nonces, but separate IBTs into categories (colors), and use a separate 4-byte nonce for each category to prevent more unintentional transfers
$PSZ -c rida -c move_globals -o --elftables-only -c selective_cfi -o "--multimodule --color --nonce-size 4" -o fix_calls:--fix-all <in pgm> <out pgm>

# 3) Option 2, but use (1-byte) executable nonces on call instructions for better performance.  Also, skip intrusmenting any safe indirect branches.
$PSZ -c rida -c move_globals -o --elftables-only -c selective_cfi -o " --multimodule --exe-nonce-size 1 --exe-nonce-for-call " -o fix_calls:--no-fix-safefn <in pgm> <out pgm>

# Or you can do combos....
$PSZ -c rida -c move_globals -o --elftables-only -c selective_cfi -o "--multimodule --exe-nonce-for-call " -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c rida -c move_globals -o --elftables-only -c selective_cfi -o "--multimodule --exe-nonce-for-call --nonce-size 4 --color" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c rida -c move_globals -o --elftables-only -c selective_cfi -o "--multimodule --exe-nonce-for-call --exe-nonce-size 8" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c rida -c move_globals -o --elftables-only -c selective_cfi -o "--multimodule --exe-nonce-for-call --exe-nonce-size 1 --color-exe-nonces " -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c rida -c move_globals -o --elftables-only -c selective_cfi -o "--multimodule --exe-nonce-for-call --color-exe-nonces " -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c rida -c move_globals -o --elftables-only -c selective_cfi -o "--multimodule --exe-nonce-for-call --exe-nonce-size 8 --color-exe-nonces" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c rida -c move_globals -o --elftables-only -c selective_cfi -o "--multimodule --exe-nonce-for-call --nonce-size 4 --color-exe-nonces --color" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>

Global options:

  • Reduce size of output binary: -o zipr:"--add-sections false"
  • Skip instrumentation of calls proven safe: -o fix_calls:--no-fix-safefn
  • -c rida -- use freely available Rida front end to "replace IDA".

Selective CFI options:

  • [--nonce-size [1|2|4|8]] -- Specify the size for non-executable nonces.
  • [--color|--no-color] -- Use color for non-executable nonces.
  • [--exe-nonce-size [1|2|4|8]] -- Specify the size for executable nonces.
  • [--color-exe-nonces|--no-color-exe-nonces] -- Use coloring for executable nonces.
  • [--exe-nonce-for-call|--no-exe-nonce-for-call] -- Determine whether to use exe nonces for calls or not.
  • [--protect-jumps|--no-protect-jumps] -- Specify whether to protect intel jmp instructions.
  • [--protect-calls|--no-protect-calls] -- Specify whether to protect intel call instructions.
  • [--protect-rets|--no-protect-rets] -- Specify whether to protect intel ret instructions.
  • [--protect-safefn|--no-protect-safefn] -- Protect indirect branches marked safe (default no)
  • [ --common-slow-path | --no-common-slow-path ] -- Use one slow path for all indirect branches that require a slow path (smaller) or one per color (safer).
  • [ --multimodule | --no-multimodule ] -- Add code for dynamically linked programs to support multiple module protection.

Notes:

  • The move global step is a prerequisite for selective CFI. The --elftables-only option create the IR necessary for adding PLT/GOT entries for dynamic linking support.

Support:

  • Linux X86-64 executables and shared libraries, tested on Ubuntu 16, 18, 20, and CentOS7.
  • No Windows support at this time.
  • Linux x86-32 was supported at one time, but is unsupported now. Your mileage may vary. :w

Xeon

To instrument a program, you need to ask Zipr to rewrite the binary and include the Xeon transform. Here are some examples that have been tested on Ubuntu 20's /bin/ls program.

# Use default Xeon settings to instrument unsafe indirect jumps
$PSZ -c move_globals -o --elftables-only  -c xeon -o "--multimodule" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>

# specify (executable) nonce size 
$PSZ -c move_globals -o --elftables-only  -c xeon -o "--nonce-size 1 --multimodule" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c move_globals -o --elftables-only  -c xeon -o "--nonce-size 2 --multimodule" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c move_globals -o --elftables-only  -c xeon -o "--nonce-size 8 --multimodule" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>

# specify to use coloring with nonce size diffs.
$PSZ -c move_globals -o --elftables-only  -c xeon -o "--color --multimodule" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c move_globals -o --elftables-only  -c xeon -o "--color --nonce-size 8 --multimodule" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c move_globals -o --elftables-only  -c xeon -o "--color --nonce-size 2 --multimodule" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>
$PSZ -c move_globals -o --elftables-only  -c xeon -o "--color --nonce-size 1 --multimodule" -o fix_calls:--no-fix-safefn <in pgm> <out pgm>

Global options:

  • Reduce size of output binary: -o zipr:"--add-sections false"
  • Skip instrumentation of calls proven safe: -o fix_calls:--no-fix-safefn
  • -c rida -- use freely available Rida front end to "replace IDA".

Xeon Options:

  • [--nonce-size [1|2|4|8]] -- Specify the (executable) nonce size to use for all branches
  • [--color|--no-color] -- Specify whether separate nonces for branch groups (colors).
  • [--multimodule|--no-multimodule] -- Include multimodule support for dynamically linked programs.