Michael Howard and Matt Thomlinson
Microsoft Corporation
April 2007
Introduction
Windows Vista offers numerous defensive enhancements designed to protect customers from malware. Applications that run on the platform should take full advantage of these defenses as the defenses are essentially free and could transform a coding error from a serious vulnerability into a crashing bug.
In the case of Internet Explorer, some of the Windows Vista defenses only come into play when all components consumed by the browser support the defenses. Because browsers are one of the most attacked (and compromised) software components, it is important that popular components consumed by the browser take full advantage of these defenses. The purpose of this short paper is to detail the defenses, and explain how software vendors should take advantage of the defenses to protect our shared customers.
This paper is highly abridged from "Writing Secure Code for Windows Vista" by Howard and LeBlanc and applies only to unmanaged (non-.NET) C and C++ code.
The Many BO Defenses in Windows Vista
Windows Vista incorporates numerous defensive strategies to protect customers from exploits. Some of these defenses are in the core operating system, and others are offered by the Microsoft Visual C++ compiler. The defenses include:
- /GS Stack buffer overrun detection.
- /SafeSEH exception handling protection.
- No eXecute (NX) / Data Execution Prevention (DEP) / eXecute Disable (XD).
- Address space layout randomization (ASLR).
- Heap randomization.
- Stack randomization.
- Heap corruption detection.
In the rest of this document, we will briefly explain each of these defenses and offer guidance deployment and test guidance.
/GS Stack Buffer Overrun Detection
This capability was introduced in the C/C++ compiler in Visual Studio .NET 2002 and has been updated in Visual Studio .NET 2003 and Visual Studio 2005. /GS is a compiler switch that instructs the compiler to add startup code and function epilog and prolog code to generate and check a random number that is placed in a function's stack. If this value is corrupted, a handler function is called to terminate the application and reduce the chance that shell code that attempts to exploit a buffer overrun will execute correctly.
/GS is enabled by adding the /GS switch to the compiler command line. In Visual C++ this option is enabled by default even if /GS is not included on the command line.
Note The performance impact of /GS is difficult to measure because it is highly dependent on coding style. Code with large quantities of stack-based string buffers and arguments will see an impact; code with none will see none. A ball-park figure may be in the order of 3%, but that is offset in part in later versions of the compiler through other optimizations. With that said, most potentially vulnerable code performs network and disk I/O, which is many orders of magnitude slower than the small /GS checks.
Note that Visual C++ 2005 also moves data around on the stack to make it harder to corrupt that data. Examples include:
- Moving buffers to higher memory than non-buffers. This step can help protect function pointers that reside on the stack.
- Moving pointer and buffer arguments moved to lower memory at runtime to mitigate various buffer overrun attacks.
ISV Requirements
- ISVs should compile their code with the latest possible compiler, Visual C++ 2005.
- ISVs should compile with the /GS flag.
- ISVs should link with libraries that use /GS.
/SafeSEH Exception Handling Protection
An exception handler is a unit of code executed when an exceptional condition, such as a divide by zero, occurs. The address of the handler is held on the stack frame of the function and is therefore subject to corruption and hijacking. The linker included with Visual Studio 2003 and later includes an option to store the list of valid exception handlers in the image's PE header at compile time. When an exception is raised at runtime, the operating system (Windows XP SP2, Windows Server 2003, Windows Vista, and Windows "Longhorn" Server and later) won't dispatch to an address in that image other than the exception handler addresses in the PE header.
Note There is no performance impact of /SafeSEH in the non-exception code path.
ISV Requirements
- ISVs should link their images with /SafeSEH.
- ISVs should link with libraries that are also linked with /SafeSEH.
Data Execution Prevention (DEP) / No eXecute (NX) / eXecute Disable (XD)
Named NX by AMD, DEP by Microsoft, and XD by Intel, this technology requires CPU support that helps prevent code from executing in data segments. Most modern Intel CPUs support this capability today and all current AMD CPUs support NX. DEP support was first introduced in Windows XP SP2 and is a critically important defense in Windows Vista, especially when used with ASLR, which we will explain below.
One caveat with DEP is that if your application has self-modifying code, or performs just-in-time compilation, DEP will cause the application to fail. To alleviate this, application vendors should still opt in to DEP (see the linker switch below) and mark any data that will be used for JITing like this:
PVOID pBuff = VirtualAlloc(NULL,4096,MEM_COMMIT,PAGE_READWRITE );
if (pBuff) {
// Copy executable ASM code to buffer
CopyMemory(pBuff,...)
// Buffer is ready to go so mark as executable and protect from writes
DWORD dwOldProtect = 0;
if (!VirtualProtect(pBuff,sizeof scode,PAGE_EXECUTE_READ,&dwOldProtect))
// oops
else
// Call into pBuff
VirtualFree(pBuff,0,MEM_RELEASE);
} Note There is no performance impact of DEP/NX/XD.
ISV Requirements
- ISVs should test their applications on a DEP-capable CPU and note and fix any failures due to NX.
- ISVs should link their code with /NXCOMPAT once DEP testing has been successfully completed.
Address Space Layout Randomization (ASLR)
ASLR moves images into random locations when a system boots and thus makes it harder for shell code to operate successfully. For a component to support ASLR, all components that it loads must also support ASLR. For example, if A.EXE consumes B.DLL and C.DLL, all three must support ASLR. By default, Windows Vista will randomize system DLLs and EXEs, but DLLs and EXEs created by ISVs must opt in to support ASLR.
Note In general there is no performance impact of ASLR. There is in fact a tiny performance improvement on 32-bit systems. However, it is possible that degradation could occur in highly congested systems loaded with many random images, but it is hard to quantify, because it depends on the quantity and size of the images.
ISV Requirements
- ISVs should test their application on Windows Vista and note and fix failures due to ASLR.
- ISVs should opt in for ASLR support in all images by linking with an updated Microsoft Linker (version 8.00.50727.161 or later).
- ISVs should link with the /DYNAMICBASE linker switch.
Heap Randomization
When an application creates a heap in Windows Vista, the heap manager will create that heap at a random location to help reduce the chance that a heap-based buffer overrun succeeds. Heap randomization is enabled by default for all Windows Vista applications.
Note The performance impact is negligible.
- ISVs should test their application on Windows Vista and note and fix failures due to heap randomization.
Stack Randomization
When a thread starts in a process compiled with /DYNAMICBASE, Windows Vista moves the thread's stack at a random location to help reduce the chance that a stack-based buffer overrun will succeed.
Note The performance impact is negligible.
- ISVs should link their code with /DYNAMICBASE and test their application on Windows Vista and note/fix failures due to stack randomization.
Heap Corruption Detection
Heap corruption detection is the ability to fail an application if the heap manager detects that the application has corrupted the heap, or the heap becomes inconsistent. This setting does not only detect heap-based buffer overruns, but certain illegal operations, for example, freeing a pointer to the wrong heap will also fail the application.
Application compatibility with heap corruption detection requires a small code change that applies only to Windows Vista. The following code will fail gracefully on Windows platforms that do not support the function (most notably Windows 2000) and will enable the heap corruption detection option for the calling application when run on Windows Vista.
BOOL SetHeapOptions() {
HMODULE hLib = LoadLibrary(L"kernel32.dll");
if (hLib == NULL) return FALSE;
typedef BOOL (WINAPI *HSI)
(HANDLE, HEAP_INFORMATION_CLASS ,PVOID, SIZE_T);
HSI pHsi = (HSI)GetProcAddress(hLib,"HeapSetInformation");
if (!pHsi) {
FreeLibrary(hLib);
return FALSE;
}
#ifndef HeapEnableTerminationOnCorruption
# define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
#endif
BOOL fRet = (pHsi)(NULL,HeapEnableTerminationOnCorruption,NULL,0)
? TRUE
: FALSE;
if (hLib) FreeLibrary(hLib);
return fRet;
} What Heap Corruption Detection Looks Like
When an application fails and is terminated because of heap corruption, you will see something like this in a debugger running on Windows Vista.
HEAP[Crash.exe]: Heap block at 001B6758 modified at 001B678E past requested size of 2e
(1770.25ac): Break instruction exception - code 80000003 (first chance)
eax=001b6758 ebx=001b678e ecx=774614cd edx=0012fae9 esi=001b6758 edi=0000002e
eip=77482ea8 esp=0012fd2c ebp=0012fd30 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
ntdll!DbgBreakPoint:
77482ea8 cc int 3
0:000> kb
ChildEBP RetAddr Args to Child
0012fd30 774dc900 001b6758 00000000 001b6758 ntdll!DbgBreakPoint
0012fd48 774c4b6e 00000000 001b6758 7748c67e ntdll!RtlImageRvaToVa+0x1c3
0012fd64 7745894a 001b0000 001b6758 7748c67e ntdll!RtlDeleteAce+0x1355f
0012fdd0 7614b019 001b0000 00000000 001b6760 ntdll!RtlValidateHeap+0x79
0012fde4 6557cb0a 001b0000 00000000 001b6760 kernel32!HeapValidate+0x14
Note There is no performance impact.
ISV Requirements
- ISVs should include this code in all EXEs. If your component is a DLL or an ActiveX control, there is no need to include this code or set the heap corruption detection flag.
Importance and Priority of Defenses
The following table outlines the relative importance of these defenses and the priority with which ISVs should support each defense.
| Defense | Priority |
| Address space layout randomization opt-in | Critical |
| DEP opt-in | Critical |
| /GS stack-based buffer overrun detection | High |
| /SafeSEH exception handler protection | High |
| Stack randomization testing | Moderate |
| Heap randomization testing | Moderate |
| Heap corruption detection | Moderate |
How to Test
Once any code and design changes have been made, it is important to verify that the operating system is configured correctly, and the application has the appropriate code changes.
C++ Compiler Use
Verify that the version of the compiler is 13.10 or later. Version 14.00 or later is highly recommended, as this is the version included with Visual Studio 2005 and offers the best /GS implementation.
Use of /GS
Open up the make file(s) associated with the product. If you are using versions of Visual C++ earlier than Visual C++ 2005, verify that all code is compiled with /GS or –GS. If you are using Visual C++ 2005, verify there are no references to /GS- or –GS-. Compiler switches are case sensitive, and –GS is not the same as –Gs.
Use of /SafeSEH
Open the make files associated with the product and verify that the linker uses /SafeSEH. Linker options are case insensitive.
DEP Compatibility
There are many ways to determine if your BIOS and CPU combination support DEP and has it enabled. The simplest is to go to Control Panel / System / Advanced system settings / Advanced tab / Performance Settings / Data Execution Prevention tab. If DEP is enabled, you will see the words:
Your computer's processor supports hardware-based DEP
If you want to determine if DEP is enabled from the command line, perform the following steps to determine whether the computer used to test your product has DEP enabled.
| Command | Possible Results | Required Result |
wmic OS Get DataExecutionPrevention_
Available | TRUE – DEP is available
FALSE – DEP is not available | TRUE |
wmic OS Get DataExecutionPrevention_
SupportPolicy | 0 – DEP is disabled for all processes
1 – DEP is enabled for all processes 2 – Windows system components are DEP enabled 3 – DEP is enabled for all components, other than those that are opted out | 1 |
wmic OS Get DataExecutionPrevention_
32BitApplications | TRUE – DEP enabled for applications
FALSE – DEP is disabled for applications | TRUE |
wmic OS Get DataExecutionPrevention_
Drivers | TRUE – DEP is enabled for drivers
FALSE – DEP is disabled for drivers | TRUE |
On some computers, you must enable DEP through a BIOS setting. Also note that in some cases NX may still not be enforced for your application or component, and this is often caused when you link your application to a non-NX enabled library, for example, older versions of the abstract type library (ATL) included with Visual Studio. If ATL is required, then use the version shipping with Visual Studio 2003 SP1 (ATL v7.1) or Visual Studio 2005 (ATL v8.0). You can verify the ATL version with debug code like this:
// Version in Visual Studio .NET 2003
assert(AtlGetVersion(NULL) >= 0x0710);
ASLR Compatibility
Verify that your make files use the /DYNAMICBASE (case insensitive) linker flag. Next, verify that your application is dynamically allocated in memory.
Verifying an EXE
If your application is an EXE file, you can perform the following tasks:
- Run a debugger and debug the process in question, and note the load address.
- Reboot.
- Repeat step (1).
If the process is opted in to randomization, you will notice that the load addresses are different across boots. Below is sample output from cdb.exe:
Executable search path is:
ModLoad: 01270000 0128b000 C:\junk\TestRand\TestRand.exe
ModLoad: 77ee0000 77ff5000 C:\Windows\system32\ntdll.dll
ModLoad: 77c30000 77d01000 C:\Windows\system32\kernel32.dll
ModLoad: 769a0000 76a49000 C:\Windows\system32\msvcrt.dll
Executable search path is:
ModLoad: 009e0000 009fb000 C:\junk\TestRand\TestRand.exe
ModLoad: 77a40000 77b55000 C:\Windows\system32\ntdll.dll
ModLoad: 77820000 778f1000 C:\Windows\system32\kernel32.dll
ModLoad: 76620000 766c9000 C:\Windows\system32\msvcrt.dll
Compatibility with Heap Corruption Detection
The best way to verify compatibility is to look for the correct call to HeapSetInformation in the code.
Compatibility with Stack and Heap Randomization Testing
Simply running your application on Windows Vista is sufficient to verify compatibility, as these defenses are enabled by default for all applications.
Summary
Browsers are presently the most attacked software, so it is imperative that browsers take full advantage of the defenses offered by the operating system. This priority extends to components used by the browser. Following the guidance in this paper will allow ISVs to build components that operate more securely when hosted within Internet Explorer.
References