03 Feb 2015

Control Flow Guard in Windows 8.1 and VS2015


Control Flow Guard (CFG) is less-known security feature in Microsoft Windows 8.1+. It is designed to prevent exploitation of indirect calls in executables.
For CFG protection to be effective, it must be enabled in both compiler/linker and operating system.

As for operating systems, Windows 8.1 Update 3 (released in November) and Windows 10 have enabled CFG. Older versions of Win8.1 had support for CFG, but it was disabled.

Currently, none of officially released versions of Visual Studio have support for CFG. In the preview version of VS2015, you can enable it manually by:

  1. Project Properties|Configuration Properties|C/C++|Command Line|Additional Options -> add /d2guard4
  2. Project Properties|Configuration Properties|Linker|Command Line|Additional Options -> add /guard:cf

Then build your project and you're done.

How it works

When compiler is about to emit indirect call like

it checks if Control Flow Guard is enabled. If it is, the following sequence is emitted instead:

When linker generates executable with enabled Control Flow Guard, it creates special entries in LoadConfig table.

BTW, IDA 6.6+ correctly analyzes LoadConfig tables. smile

___guard_fids_table is array of all functions defined in the EXE:

___guard_check_icall_fptr is a pointer to GuardCFCheckFunction. By default it points to simple "retn" instruction.

So, this executable will run without any problems on older OS.

But when a newer OS loads such executable, it will overwrite pointer to GuardCFCheckFunction with pointer to ntdll!LdrpValidateUserCallTarget:
Control Flow Guard on Win 8.1

Now every indirect call from this module will be checked by LdrpValidateUserCallTarget before execution. If called address is not present in GuardCFFunctionTable, OS will terminate process with:

What does it mean for reversers

As mj0011 said on Twitter

Control Flow Guard is awesome! Wish it could completely enable in RTM.@NTarakanov @JohnLaTwC @j00ru @WTFuzz

Maybe I'm not that excited but still think that CFG is a very useful feature.

List of all procedures in EXE

You don't need to guess anymore if this is code or data, EXE comes with a nice list of all functions.

One breakpoint to rule them all

No more guessing which bloody indirect call is the one you're interested in. Just put breakpoint on GuardCFCheckFunction and any indirect call will break. You even have an option to break on all indirect calls in EXE (putting breakpoint in EXE) or all modules (putting breakpoint in NTDLL). Hell yeah!

Packing your executable can break CFG

If your protector does not support CFG properly, your EXE will not work at all. Or it will work, but will not benefit from CFG. For example, packing my test EXE with standard UPX 3.91 produces an EXE file that will work in all OS until Win7, but will not run on Win8/Win10.

Simple workaround is to zero out LoadConfiguration directory RVA/Size in PE header, or to remove IMAGE_DLL_CHARACTERISTICS_GUARD_CF flag (value: 0x4000) from DllCharacteristics field in PE Optional Header.

Currently I'm not aware of any common protector that would correctly support CFG.

Pay more attention with patching EXE

If you're hooking some functions by overwriting entries in vtable (quite a few game cheats do that), you must pay special attention to CFG. You can either update GuardCFFunctionTable or remove CFG support from the patched module using one of the methods mentioned above.

Further reading

Visual Studio 2015 Preview: Work-in-Progress Security Feature
Exploring Control Flow Guard in Windows 10
Windows New Security Features - Control Flow Guard
How Control Flow Guard Drastically Caused Windows 8.1 Address Space and Behavior Changes

Control Flow Guard enabled file for testing: https://www.mediafire.com/?3dbg7wyihbyn8pj

2 thoughts on “Control Flow Guard in Windows 8.1 and VS2015

Leave a Reply

  • Be nice to me and everyone else.
  • If you are reporting a problem in my tool, please upload the file which causes the problem.
    I can`t help you without seeing the file.
  • Links in comments are visible only to me. Other visitors cannot see them.

Your email address will not be published.

 −  2  =  five