The Preview for Visual Studio 2015 was announced on November 12, 2014. It includes a new, work-in-progress feature, called Control Flow Guard. By simply adding a new option to your Project, the Visual C++ compiler will inject extra security checks into your binaries. These will detect attempts to hijack your code. The check will stop execution of your code, before the hijacker can do damage to your data or PC.
This blog explains how you can experiment with Control Flow Guard in the Preview. Your feedback will determine how we move forward in our release planning.
For the rest of this post, I’ll abbreviate Control Flow Guard to CFG.
How to Enable CFG
If you are building your project from the command-line, as in: cl test.cpp then tell both compiler (via /d2guard4) and linker (via /guard:cf) to add CFG instrumentation, as follows: cl /d2guard4 test.cpp /link /guard:cf
(Yes, /d2guard4 is a strange name. Going forward, we will change it – likely to /guard:cf, where “cf” stands for “Control Flow”. But that is for the future)
If you are building your project within Visual Studio, just make the corresponding changes in your Project’s Property Pages:
So, for the compiler, click through the sequence: PROJECT|Properties|Configuration Properties|C/C++|Command Line|Additional Options. In the resulting window, add /d2guard4
Similarly, for the linker, click through the sequence: PROJECT|Properties|Configuration Properties|Linker|Command Line|Additional Options. In the resulting window, add /guard:cf
That’s all there is to it. You don’t need to change any source code – no restructuring, no annotations, nothing. The compiler and linker do all of the heavy lifting required – you simply direct them to do so with these new switches.
How to Tell If a Binary Is CFG’d?
Run the dumpbin tool, and specify the /headers and /loadconfig options. With our running example, we would say: dumpbin /headers /loadconfig test.exe. I have extracted the relevant sections of output and highlighted the 3 flags to check-for in blue, below:
OPTIONAL HEADER VALUES
10B magic # (PE32)
// skipped fields here
C140 DLL characteristics
Section contains the following load config:
004271C0 Security Cookie
00425120 Safe Exception Handler Table
19 Safe Exception Handler Count
0041D128 Guard CF address of check-function pointer
0041D1A8 Guard CF function table
A8 Guard CF function count
00003500 Guard Flags
FID table present
Protect delayload IAT
Delayload IAT in its own section
We would like you to try out this feature, and give us feedback on what you find:
- Is CFG easy to use?
- Is CFG easy to understand?
- What’s the impact? Larger binaries? Slight hit on runtime speed? Did it trap any attacks?
- How can we improve CFG?
How Does CFG Work?
By supplying unlikely/unusual/extreme data to a running program, an attacker attempts to make it do something that was never intended. For example, providing more input than expected, an attacker may over-run the area reserved by a program to hold the response. (This is an attack called “buffer overrun”, or “buffer overflow”). Thereafter, the program might execute an indirect-call instruction – but instead of jumping to the location originally intended, it now jumps to a different location whose code is controlled by the attacker.
Note that such an attack exploits a vulnerability – a bug – in the original program. This vulnerability is not the computer’s fault; it’s a fault on the part of the team who wrote the code. That said, spotting and closing such vulnerabilities can be very difficult and time-consuming.
CFG comes to our rescue in these situations, as follows. Whilst compiling and linking code, it analyzes and discovers every location that any indirect-call instruction can reach. It builds that knowledge into the binaries (in extra data structures – the ones mentioned in a dumpbin /loadconfig display). It also injects a check, before every indirect-call in your code, that ensures the target is one of those expected, safe, locations. If that check fails at runtime, the Operating System closes the program.
So, even though the original code contained a bug that was exploitable by an attacker; and even though the authors of the code were not aware of that bug, and had not fixed it; and even though an attacker succeeded in his first steps to exploit the security hole; nonetheless, CFG will stop the attack going on to cause subsequent damage.
Must I CFG Everything?
You don’t need to CFG every part of your code. A mixture of CFG’d and non-CFG’d code will execute just fine, but the non-CFG’d parts won’t, of course, include any injected checks – so won’t stop attack vectors through their indirect-calls.
Remember the adage: “a chain is only as strong as its weakest link”. So try to CFG as much of your code as you have access to.
What Versions of Windows Do I Need?
CFG operation depends upon it running on a “CFG-Aware” version of the Windows Operating System. At the current time, CFG is present in the x86 and x64 versions, for Desktop and Server, of the following releases:
- Windows 10 Technical Preview
- Windows 8.1 Update
What If I Run on a CFG-Unaware Version of Windows?
Your guarded code will execute ok. But of course it won’t benefit from CFG security – you need to be running on a CFG-Aware version of the Windows Operating System for that. In fact, the extra checks in your guarded code will burn a few cycles before automatically being turned around as successful, so you might even detect a small dip in performance in these scenarios.
Are Libraries CFG’d?
For this experimental release of CFG, we have not provided CFG’d versions of the C or C++ libraries. These will be included into future releases.
The technology that supports CFG is conceptually simple: ensure that all indirect-calls result in a jump to legal targets. Similar techniques have been prototyped, analyzed and reported over the past few years. For example, the team from Microsoft’s Cambridge Research center described an early variant of their work, which later became CFG.
Minimising the runtime impact of CFG checks, while ensuring backwards compatibility, is a major software-engineering challenge. Not only do we hand-craft the very best sequence of instructions for each check, we also invoke the compiler’s analysis to elide (ie, optimize-away) any checks where it can prove that a specific indirect-call is inherently safe.
In addition, the Operating System – both Memory Manager and Runtime Loader – play a crucial role in guaranteeing the correctness of CFG. Any bug in CFG itself would of course defeat the entire feature! We have lost count of how many dozens, or hundreds, of researchers and engineers within Microsoft have been impacted by CFG. We hope you like the outcome of all this hard work, carried on in secret, over the past several years!
CFG monitors and checks certain aspects of a program’s “Control Flow” – ie, where execution changes from straight sequential, one-after-another instruction. That explains the “CF” abbreviation you may have noticed in the notes above.
We are hopeful that this technology will raise the bar of difficulty faced by hackers trying to attack your PCs and code.
In future, we may extend the technique to provide even stronger runtime checking.