Wednesday, October 14, 2009

Unmanaged C++



Chapter 18 -
C++ Improvements
Microsoft Visual Studio 2008 Programming
by Jamie Plenderleith and Steve Bunn 
McGraw-Hill/Osborne © 2009























Unmanaged C++



The rest of this chapter focuses on the improvements made to the unmanaged (also called native) C++ language, C++ compiler, tools, and libraries in Visual Studio 2008 and in Visual Studio 2008 Service Pack 1. You might be wondering why we want to call out SP1! The reason is that SP1 includes a major update to the libraries that, surprisingly, is a much bigger change than the update from Visual Studio 2005 to Visual Studio 2008.


In some cases, the technologies outlined may have been available in Visual Studio 2005, but we will explain the technology as it applies to the newer toolset.


The topics explained in this chapter include the following:




  • Improvements to the compiler and linker




  • Improvements to the ATL, MFC, and STL libraries




  • Static analysis




  • Standard Source Code Annotation Language (SAL)




  • Security improvements to the compiled code






Improvements to the C Compiler and Linker


There are many small changes to the compiler and linker, but most of them are highly specialized, so we’ll focus on some of the changes that will affect most users, most notably:




  • /Wp64 and __w64 are now deprecated.




  • User Account Control support has been added.




  • Address Space Layout Randomization (ASLR) support has been added.






/Wp64 and __w64 Deprecated


The /Wp64 compiler option and _w64 keyword are not often used; they were used to help find 64-bit portability issues. These options are now deprecated in Visual C++ 2008 because the 64-bit compiler detects issues automatically, and they will be removed in a future version of the compiler. If you use them, you’ll get a D9035 warning, so you should start removing their use from your projects.





User Account Control Support Added


Windows Vista is the first version of Windows that can be operated relatively easily as a nonadministrator. But some applications require that they be run by administrators; for example, an application that configures a computer’s clock or loads device drivers would require administrative privileges. So if a user is logged on as a nonadministrator, how does she elevate to administrator status to perform privileged tasks like those just mentioned? The easiest way by far to allow a user to elevate their status is to mark the application as “requiring administrative privileges” so that when the operating system loads the application, it will prompt the user to enter administrative credentials if they are not logged on as an administrator. Of course, the user will need to know administrative credentials for this scenario to work; the operating system does not blindly elevate a nonadministrative user.


There are many ways to mark an application’s UAC requirements, but the way to do it in Visual Studio 2008 is to set the /MANIFESTUAC linker option. You can set it in the Visual Studio user interface by performing these steps:




  1. Right-click the project name in Solution Explorer (or press ALT-F7) and click Properties.




  2. Expand the Configuration Properties node.




  3. Expand the Linker node.




  4. Select the Manifest File property page.




  5. Set the Enable User Account Control (UAC), UAC Execution Level, and UAC Bypass UI Protection properties to the settings you want. For example, if you want your application to run as an administrator, set Enable User Account Control (UAC) to Yes and UAC Execution Level to requireAdministrator.




Now run your application and you’ll be prompted to elevate. If you run the application from within Visual Studio 2008 (for example, by selecting Start with Debugging), you’ll be prompted to elevate from within the Visual Studio environment.


Of course, you can do this from the linker command line too, as follows,




/MANIFEST:option


where the valid options are





  • asInvoker   The application will run with the same permissions as the user or process that started it.





  • highestAvailable   The application will run with the highest permission level possible for that user. If the user starting the application is a member of the local Administrators group, this option is the same as requireAdministrator. If the highest available permission level is higher than the level of the user, the system will prompt for credentials.





  • requireAdministrator   The application will run with administrator permissions. The user who starts the application must be a member of the Administrators group. If the user or process is not running as a member of the local Administrators groups, the system will prompt for credentials.







Address Space Layout Randomization Support Added


ASLR is a security defense included with Windows Vista and Windows Server 2008 and later. It is explained in much more detail at the end of this chapter.






Improvements to the MFC and STL Libraries


Other than the usual set of bug fixes that comes with updated libraries, the libraries in Visual C++ 2008 SP1 offer a wealth of new functionality. In April 2008, Microsoft released the Visual C++ 2008 Feature Pack, which adds many new features to the various libraries such as Standard Template Library (STL) and Microsoft Foundation Classes (MFC). Then, in August 2008, Microsoft released Visual Studio 2008 SP1, which includes the functionality in the Feature Pack as well as other bug fixes. If you want to use the new functionality, you should simply download SP1. In fact, the library updates are so major, you really should install SP1.


MFC’s claim to fame has always been that it removes much of the tedious and extremely complex UI code by providing wizards and wrappers over the Windows UI APIs; this allows the developer to focus more on the application logic and less on the UI logic.


Most of the MFC changes in Service Pack 1 are UI changes that add support for things like:




  • Office 2007 Fluent UI, Ribbon Bar support, and Application button (CMFCVisualManagerOffice2007 class)




  • Office 2003 and Office XP support for toolbars and menus and the Microsoft Outlook shortcut bar (CMFCVisualManagerOffice2003 class)




  • Windows Vista theme support (CMFCVisualManagerWindows class)




  • Shell navigation (CMFCEditBrowseCtrl class)




Accessing this new functionality is easy; simply create a new MFC project, and you are given the option of selecting a project style and visual style, as shown in Figure 18-1.






Figure 18-1: Creating a visual style for an MFC application

When you build and run the application for the first time, you are presented with a great deal of complex and highly useful UI functionality (see Figure 18-2), none of which you had to write!







Figure 18-2: A default MFC application using the Microsoft Office 2007 style

MFC also adds a small number of classes, such as CNetAddressCtrl, which is a network address control that allows a user to enter and verify IPv4, IPv6, and DNS names. CPagerCtrl is a scrollable control container. CSplitButton is a button with the BS_SPLITBUTTON style, which gives it drop-down capability. The following existing controls have one or more new methods, mostly to support new Windows Vista UI capabilities:




  • CAnimateCtrl




  • CButton




  • CComboBox




  • CDateTimeCtrl




  • CEdit




  • CHeaderCtrl




  • CLinkCtrl




  • CListCtrl




  • CMonthCalCtrl




  • CProgressCtrl




  • CReBarCtrl




  • CSliderCtrl




  • CStatusBarCtrl




  • CToolBarCtrl




  • CToolTipCtrl




  • CTreeCtrl







The Big One—Support for TR1


Perhaps the biggest change in Visual C++ 2008 SP1 is the inclusion of libraries from Technical Report 1 (TR1), which is the common name for ISO/IEC TR 19768, C++ Library Extensions. Technically, TR1 is not a standard; it’s still a draft, so it is subject to change. But, for what it’s worth, the author of this chapter uses the new TR1 classes regularly because there are only a small number of new classes and they are very useful. If you are familiar with the various Boost C++ libraries, you will recognize some of the classes in TR1.


The major additions in the TR1 library include




  • Regular expression support (<regex> header)




  • Shareable pointer support (<memory> header)




  • Random number generation (<random> header)




  • Array container support (<array> header)




  • Unordered container support (<unordered_map> and <unordered_set> headers)




There is support for other functionality, but the preceding list, arguably, includes the most useful functionality. (Note that the mathematical function support is not included in the current Microsoft implementation of TR1.) The new template classes reside in the std:: tr1 namespace.


So let’s look at each of the classes with some sample code.





Regular Expression Support


Most, if not all, class libraries for other development languages include support for regular expressions, but until now there was no standard regular expression library for C++. The Boost Regular Expression library was the de facto standard, and the TR1 library is essentially a copy of the Boost functionality.


A regular expression is a way to match data, such as a series of numbers or an e-mail address, to make sure the data format is correct. A full explanation of regular expression grammar is beyond the scope of this book, but one thing you need to know is that TR1 regex supports many different regex grammars—the most commonly used and full-featured is the ECMA grammar, but you can also use POSIX, awk, grep, and egrep grammars if you so wish. The samples in this chapter assume use of ECMA grammar.


The following code shows how to use the TR1 regex support to match a United States federal social security number (SSN) and capture each set of digits.




#include <regex>
#include <iostream>

using namespace std::tr1;
using namespace std;

int main(int argc, char* argv[]) {
string ssn = "123-45-6789";
regex rx("\\d{3}-(\\d{2})-\\d{4}");
cmatch res;
if (regex_search(ssn.c_str(),res,rx)) {
cout << "Match!" << endl;
cout << "Middle #'s: " << res[1] << endl;
}
else
cout <<"No Match :(" << endl;
return 0;
}


Note that regex is actually a typedef,




typedef basic_regex<char> regex;


and is used to match 8-bit chars. There’s also a wide-character version, wregex, that supports Unicode characters:




typedef basic_regex<wchar_t> wregex;


In this example, we’re attempting to match the social security number, which has the form ddd-dd-dddd (where d is a digit). There is no other valid form of SSN today, so any other representation is invalid.


The regex object defines a pattern to look for, which in this case is one of the following:





  • \d{3}-   Means “digits, three of them, followed by a dash.”





  • (\d{2})-   Means “digits, two of them, followed by a dash, and capture the number.” We’ll explain capturing in a moment, but that’s why there is a set of parentheses around the pattern.





  • \d{4}   Means “digits, four of them.”




The regex_search() method takes the string argument, a result argument, and the regular expression to determine if there is a match; if there is a match, the method returns true. Note that there are many overloaded versions of regex_search(), and you should make yourself aware of all the variants because some versions might be more appropriate for the work you’re doing.


The result class cmatch (which is a specialization of the match_results class) holds capture information from the comparison operation. In this case we want to know not only that the incoming string is correctly formatted, but also what the middle two digits are. cmatch[0] holds the entire string, and cmatch[1] .. cmatch[n] hold the captured data. If you’re familiar with Perl, think of the captured indexes as analogs to $1, $2, and so on. So, in this example, res[0] holds “123-45-6789” and res[1] holds “45.”


A somewhat more useful regular expression could be used to parse the four octets of an IPv4 address:





regex rx("^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\."
"(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\."
"(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\."
"(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$");

string addr("192.168.100.2");
cmatch res;

if (regex_search(addr.c_str(),res, rx)) {
cout << res[1] << "." << res[2] << "." << res[3] << "." << res[4] <<
endl;
}
else
cout <<"Not an IPv4 address" << endl;




You can also replace strings using regular expressions:




string str = "The Lord of the Rings";
regex rx("rings", regex_constants::icase);
string replacement = "flies";
string str2 = regex_replace(str, rx, replacement);


Note that this code uses the case-insensitive flag option (regex_constants::icase) and that, by default, the replace() method does a global replace, replacing all instances. If you want to limit the change to the first instance only, set the format_first_only option when





calling regex_replace:
string str2 = regex_replace(str,
rx,
replacement,
regex_constants::format_first_only);



In our opinion, any good programmer knows how to use regular expressions well, and it is important that, irrespective of regular expression library, you should learn a set of regular expression patterns. In fact, if you have a spare three to four hours, spend the time learning to use regular expressions—the knowledge will pay dividends in years to come.





Shareable Pointer Support


Perhaps one of the biggest downfalls of using C++ is memory management, and perhaps the biggest issue is leaking memory, a situation that arises when a developer allocates memory but fails to free it or, worse, attempts to free the memory twice. If a person has a memory leak in their code, you can almost guarantee they are using C or C++. Admittedly, this is a big advantage of using any of the managed languages such as C# and VB.NET, because the run-time environment takes care of managing memory, instead of the developer.


The new classes help remove numerous memory leaks. For example, look at the following code; can you spot the bug?




int *i = new int(5);
string *s = new string();

// use i and s

delete i;
delete s;


If the call to create the new string fails, it raises an exception, so the object i leaks. Interestingly, the higher-end versions of Visual C++ 2008 create the following warning if compiled with /analyze:





warning C6211: Leaking memory 'i' due to an exception. Consider using a local
catch block to clean up memory: Lines: 113, 114




Using the new shareable pointer support in TR1 removes the chance that such errors will occur. You can replace the preceding code with




shared_ptr<int> i(new int(5));
shared_ptr<string> s(new string());


and then continue to use i and s as you would in any other C++ code. Note that the calls to delete are gone—there is no need for them! It’s almost like having .NET garbage collection.


You can also use shared pointers with other STL objects:




vector<shared_ptr<ifstream> > v;
shared_ptr<ifstream> f(new ifstream("a.txt",ifstream::in));
v.push_back(f);


If you are familiar with the auto_ptr class in prior versions of STL, you know that placing auto_ptr objects in STL containers such as vector and queue is not guaranteed to work correctly. This restriction does not apply to shared_ptr objects.


Some well-known C++ experts have made comments to the effect that the biggest single reason to use TR1 is to use shared_ptr.





Random Number Generation


TR1 offers new support for generating and distributing random numbers. When people think of random numbers in C and C++, they usually think of rand(), which produces even output distribution (in theory), but sometimes you might want the numbers distributed in different ways, for specific statistical or mathematical reasons. To this end, TR1 allows a series of random number probability to be distributed using specific probability distribution mechanisms, such as (among many others):




  • Uniform distribution (somewhat like rand())




  • Normal distribution (also called Gaussian distribution, the classic “bell curve”)




  • Poisson distribution




  • Geometric distribution




  • Bernoulli distribution




Realistically, if you don’t know what these distribution names mean, you probably don’t need the distribution mechanism, and should just stick with uniform distribution.


Here’s some code to show how to select a distribution algorithm and a random number generation algorithm:




#include <random>
#include <iostream>

using namespace std::tr1;
using namespace std;

int main(int argc, char* argv[]) {
minstd_rand0 eng;
normal_distribution<double> normal(0.0, 3.0);
for (int i = 0; i < 20; i++)
cout << normal(eng) << std::endl;
}



First, we create a random number generator or engine. In this case we use minstd_rand0, which generates numbers similarly to rand(), using a process called linear congruence. minstd_rand0 is really a typedef:





typedef linear_congruential< i-type, 16807, 0, 2147483647> minstd_rand0;



Then we create a distribution mechanism, in this case a classic bell curve, or normal distribution using a mean () of 0.0 and standard deviation () of 3.0.



Figure 18-3 shows a graph after 20,000 runs of the prior code; as you can see, it’s a classic normal distribution chart.






Figure 18-3: Normal distribution of random numbers using the normal_distribution class

If you change the code to read




int main(int argc, char* argv[]) {
minstd_rand0 eng;
uniform_int<int> normal(-10, 10);
for (int i = 0; i < 20; i++)
cout << normal(eng) << std::endl;
}


you end up with an even distribution of numbers across the range of valid values, as shown in Figure 18-4.






Figure 18-4: Uniform distribution of random numbers using the uniform_int class

It is important to note that none of the TR1 random number generators is suitable for generating cryptographic keys.





Array Container Support


STL has a number of common container types today, such as vector, list, and deque, but there has never been a good ol’ arrayuntil now. Unlike a vector or a list, an array is a fixed-length object. Here’s a quick code sample:





#include <array>
#include <iostream>
#include <algorithm>
...
array<int,5> primes = {5,11,3,2,7};
sort(primes.begin(),primes.end());
for (array<int,5>::const_iterator i = primes.begin(); i != primes.end(); ++i)
cout << *i << endl;




Of course, you could stick with using C-style arrays, but then you would not have access to the various STL iterators and algorithms such as sort, count, and search. Essentially, the array template class wraps a C array with an STL interface.





Unordered Container Support


Prior to TR1, STL provided four associative container template classes: map, multimap, set, and multiset. They differ in how they compare objects and handle object insertion.


An associative data structure is a structure that allows you to associate some meaningful index to a value. For example, imagine a structure that stores world populations; it might looking something like this pseudocode:




Population["USA"] = 304,000,000;
Population["Zambia"] = 11,668,000;
Population["New Zealand"] = 4,100,000;
Population["Brazil"] = 190,000,000;
Print Population["Zambia"];


Each element in a map and multimap is a pair of objects. The first part of the pair is a key, and the second part of the pair is the value. You cannot insert a new element into a map whose key is already present; a multimap allows multiple elements with the same key. A set uses the entire element as the key; a multiset allows multiple elements with the same key.


TR1 adds a new container type, the unordered container, also known as a hash table. The big difference between the new container types in TR1 and the other associative container types pre-TR1 is that unordered containers don’t support the classic comparison operators (<, >, =, and so on), because they are unordered.


On the surface, the older map and the newer unordered_map look similar:





unordered_map<int, string> uport;
uport[80] = "HTTP";
uport[21] = "FTP";
uport[443] = "HTTPS";
map<int, string> port;
port[80] = uport[80];
port[21] = uport[21];
port[443] = uport[443];

for (unordered_map<int, string>::const_iterator ui = uport.begin();
ui != uport.end();
ui++)
cout << ui->second << "=" << ui->first << endl;

for (map<int, string>::const_iterator i = port.begin();
i != port.end();
i++)
cout << i->second << "=" << i->first << endl;



The major difference is obvious if you dump the contents of each collection—a map is automatically ordered, and an unordered map is not. The TR1 unordered_set, multiset, map, and multimap containers are hashed containers, unlike set, multiset, map, and multimap, which are balanced binary trees. There is extra overhead from maintaining order, so there is a small performance gain when using the unordered classes.


While this may all seem confusing to you at this point, you will come to appreciate how much flexibility these new classes offer to you as a developer.






Static Analysis


Some versions of Visual Studio 2008, such as Team System 2008 Development Edition, include static analysis support. Static analysis is a way to analyze software for bugs without executing the application and is a staple for finding some hard-to-find errors.


You can run the static analysis capability on your C or C++ project by adding the /analyze option to the command line, or by navigating as follows in Solution Explorer:




  1. Right-click the project and click Properties.




  2. Expand Configuration Properties.




  3. Expand C/C++.




  4. Click Advanced.




  5. Set Enable Code Analysis for C/C++ on Build to Yes.




Your build times will roughly double, so don’t use this option for every compile, but make sure it’s enabled regularly so that you can catch and fix bugs quickly without too much code churn.


Let’s look at a very simple example. If you compile the following code snippet at warning level 4 (/W4), you’ll get no warnings or errors. Note that the line numbers have been added to this code sample to facilitate its subsequent discussion.




7 int main(int argc, char* argv[])
8 {
9 char *p = NULL;
10
11 if (argc == 2)
12 p = argv[1];
13
14 printf("%c",p[0]);
15 return 0;
16 }


However, if you compile this code using the /analyze option, you’ll get the following warning:





warning C6011: Dereferencing NULL pointer 'p': Lines: 9, 11, 14



The warning indicates that in some conditions (in this case, the control flow over lines 9, 11, and 14) p is NULL, and that an attempt is being made to dereference it, which is an invalid condition that will cause an application to crash.


/analyze can find some hard-to-find bugs that might lead to security vulnerabilities. There’s a list of static analysis warnings at http://msdn.microsoft.com/en-us/library/a5b9aa09.aspx.





Standard Source Code Annotation Language


The concept of adding annotations to the code so that the analysis tools can find more bugs is relatively new to many software engineers. The Standard Source Code Annotation Language (SAL), a technology from Microsoft Research that is actively used within Microsoft, is a powerful addition to C/C++ source code to help find bugs, most notably, security vulnerabilities. Think of SAL as a way to help tools find bugs by enabling them to know more about a function interface. SAL is extremely useful for annotating buffer information because buffer mismanagement is a common source of security bugs. Using SAL you can describe buffer characteristics, such as:




  • Whether a pointer can be NULL or not




  • How much space can be written to the buffer




  • How much space can be read from the buffer




  • Whether the buffer is NULL-terminated or not




To complicate things a little, there are two flavors of SAL: declspec SAL and attribute SAL. Visual C++ 2005 supports declspec SAL, and Visual C++ 2008 supports both declspec and attribute SAL. However, because future improvements will be made only to attribute SAL, the rest of this chapter uses attribute SAL.


Probably the best way to show SAL is by way of an example. Take a look at the following function:




void FillString(
char* pBuf,
int cchBuf,
char ch) {
for (int i = 0; i < cchBuf; i++)
pBuf[i] = ch;
}



This code is a classic example of a function that takes a buffer and a buffer size as arguments; there are many functions that you write that follow this pattern. You know that the two arguments pBuf and cchBuf are related, but the compiler and the source code analysis tools do not know the relationship.


You can demonstrate that the tools don’t know pBuf and cchBuf are related by calling the function like this:




char dst[32];
FillString(dst,40,'*');


If you compile this code with the following options,




/W4 /analyze


you’ll get no warnings. Yet there is a buffer overflow in the code; the code is trying to write 40 asterisks to a 32-byte buffer. If you don’t believe this, run the code and watch it crash.


This is where SAL enters the picture—it can be used to define the relationship between pBuf and cchBuf:




void FillString(
_Out_bytecap_(cchBuf) char* pBuf,
int cchBuf,
char ch) {
for (int i = 0; i < cchBuf; i++)
pBuf[i] = ch;
}


When you compile this code with the same compiler command-line arguments, /W4 / analyze, you get a warning:




Warning C6386: Buffer overrun: accessing 'argument 1',
the writable size is '32' bytes, but '40' bytes might
be written: Lines: 15, 16


Let’s look at the updated syntax. SAL uses macros to describe function arguments and in some cases, return-value, pre- and post-conditions. In this case, the _Out_bytecap_(n) macro means




  • The buffer, pBuf, is an “out” buffer; in other words, the function will write to pBuf but not read from it.




  • pBuf’s byte capacity (bytecap) is cchBuf.




That’s all there is to it. As you can see, we have provided valuable information to the compiler and the static analysis engine about the relationship between pBuf and cchBuf: pBuf points to data that is cchBuf bytes long.


Let’s look at a slightly different example. Instead of allocating the destination buffer on the stack, let’s call the C run-time malloc() function:




char *dst = reinterpret_cast<char*>(malloc(40));
FillString(dst,40,'*');
free(dst);



If you compile this with /analyze, you get this warning:





Warning C6387: 'argument 1' might be '0': this does not adhere to the
specification for the function 'FillString': Lines: 17, 18



Look carefully at the code and the warning: The warning states that there’s an issue with the first argument to FillString() and that the argument might be 0. 0 is the same as NULL. The first argument comes from the return value from malloc(). So what’s going on? If you look at the declaration for malloc() in malloc.h, you’ll see this:





_Check_return_ _Ret_opt_bytecap_(_Size) void * __cdecl malloc(_In_ size_t
_Size);



Notice that not only is the sole argument to malloc() annotated with SAL, but so is the return value; here’s what it all means:




  • _Check_return_ means you must check the return value of malloc(). Some functions must always be checked for error.




  • _Ret_opt_bytecap_(_Size) means the return value is optional—because malloc() might fail, in which case it will return NULL—and the byte capacity of the returned buffer is _Size, which is the first argument to malloc().




So the return value from malloc() is optional because malloc() might fail by returning returning NULL. The first argument to FillString() is not optional and you can’t pass in NULL because it makes no sense to fill a NULL string with a series of characters; hence the error. If a function could take a NULL argument, then the function would look like this:




void Function
_Out_opt_bytecap_(cchBuf) char* pBuf,
int cchBuf,
char ch);


Note the inclusion of “opt” in the SAL annotation. Interestingly, if you compile this code, you get the following new warning:





warning C6011: Dereferencing NULL pointer 'pBuf': Lines: 10, 11



Again, it’s SAL that’s helping to drive this warning, because the “NULLness” is described in the SAL annotation and the code is attempting to dereference a pointer that can be NULL.


So, finally, we end up with this code that compiles with no warnings:




void FillString(
_Out_opt_bytecap_(cchBuf) char* pBuf,
int cchBuf,
char ch) {
if (!pBuf) return;
for (int i = 0; i < cchBuf; i++)
pBuf[i] = ch;
}




Table 18-1 provides some common SAL annotations, along with examples and comments; you can get a full list by looking at the sal.h header.

























Table 18-1: Common SAL Annotations

Annotation



Example



Comment



_In_



void Foo(_In_ int *p)



Parameter is input only and not NULL.



_In_opt_



void Foo(_In_opt_ int *p)



Parameter is input only and can be NULL.



_In_count_(n)



void Foo( _In_count_(cch) const char* buf, size_t cch )



Parameter is a non-NULL and valid buffer, the extent of which is described by another argument.



_Out_z_cap_(n)



void CopyStr( _In_z_ const char* szFrom, _Out_z_cap_(cchTo) char* szTo, size_t cchTo );



Parameter is a NULL-terminated buffer (a string) and is filled to a capacity described by another argument.



There are many more SAL annotations. You should start using SAL in your code by annotating all function buffer arguments.




Security Improvements to the Compiled Code


C++ is a powerful language. There is little you cannot do in C++, assuming you are willing to write enough code! The problem is, with that power comes danger. Relative to languages such as C#, C++ is a loosely typed language, and relative to C, C++ is actually a strongly typed language—it’s all relative! Loose typing combined with C++’s direct access to memory can lead to catastrophic security vulnerabilities such as buffer overruns and integer arithmetic issues.


Remember, C was designed as a replacement for assembly language, and C++ is an object-oriented progression of the C language. Therefore, C++ can be used anywhere you would normally use assembly language.


So we have a little problem: there is a lot of C++ code out there, and more is written every day, yet, in the hands of the average developer, C++ can be dangerous. To address this issue, or at least attempt to address the issue, Microsoft has added numerous valuable and effective defenses to the compiled code created by the C++ compiler and linker automatically. These defenses help reduce the chance that a buffer overrun bug will lead to a predictable and exploitable security vulnerability. The big change in Visual C++ 2008 over Visual C++ 2005 is that these defenses are enabled by default. It is the authors’ recommendation that you stick with the defaults and don’t turn them off.


In the preceding paragraph, the phrase “predictable and exploitable security vulnerability” is very precise and chosen specifically. The word “exploitable” means an attacker can cause malicious code to execute—code dictated by the attack. It is beyond the scope of this one chapter to explain how security vulnerabilities are exploited, so you are urged to read some of the many excellent books on the topic. The word “predictable” is interesting in this context. Attackers love predictable attacks, attacks that always succeed. As a defender, it is important that you ensure that the outcome of attacks will be as unpredictable as possible to attackers, because that will dissuade attackers.


There are a number of defenses that help make life miserable for attackers by making the outcome of their attacks unpredictable. The first two defenses are native to the compiled code and added by the compiler; the other defenses are settings added to the compiled code to inform Windows that the application wants to opt in for the operating system defenses.


Before we explain the defenses, a small amount of background knowledge about some security vulnerabilities is important.





The Evils of Stack-based Buffer Overruns


Explaining how buffer overruns work is beyond the scope of this book. The only thing you really need to know (unless you are a security person) is that attackers need to control the flow of execution in the code, from the normal flow to a flow dictated by the attacker. And this is where the danger lies when using C and C++; C and C++ place sensitive constructs in harm’s way, constructs that determine the flow of execution (for example, function return addresses, function pointers, and exception handler addresses). Many of these constructs are located in stack memory right alongside other function arguments, such as buffers or arrays of data. If this data is overrun, then the attacker can corrupt the sensitive data and overwrite it with data that changes the flow of execution.


The real lesson here is that you should be aware of where your data lives, and make sure it is safe and protected, especially if your application is a highly exposed application, such as one that’s reachable from the Internet.


The diagram in Figure 18-5 shows what a function’s stack memory might look like.


As you can see, a function’s local variables are lower in memory than the function’s return address, so code like the following could lead to a buffer overrun if the data pointed to by *src and count are both longer than the length of dst:




void SomeFunction(char *src, size_t count) {
char dst[80];
memcpy(dst, src, count);
// etc…
}

















Higher Memory



Stack Variables (including function pointers)



Function Return Address



Function Arguments






Figure 18-5: Stack memory layout

Notice in Figure 18-5 that the function’s return address is right after the function’s local variables. If src is correctly crafted by an attacker, and count is longer than 80 bytes, then the attacker can smash the return address and cause the function to return to some other location, perhaps the buffer, src, itself, and start executing malicious code.


The purpose of the defenses added by the compiler and linker in Visual C++ 2008, and enforced by Windows Vista and Windows Server 2008 and later, is to make an attack against such vulnerable code harder, but not impossible. To be more accurate, the goal is to convert a code execution bug into a denial of service bug. Denial of service bugs are still serious bugs, especially in critical servers, but they are the lesser of two evils when compared to code execution vulnerabilities.


Now let’s move onto the defenses.





Stack-based Buffer Overrun Detection (/GS)


Visual C++ includes an option, enabled by default, to make successful and predictable function return address clobbering harder. This capability was introduced in the C/C++ compiler in Visual Studio .NET 2002 and has been updated in later versions of the compiler. /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, thereby reducing the chance that shell code that attempts to exploit a buffer overrun will execute correctly.






Note 

Compiler switches are case sensitive, so don’t confuse /GS with /Gs—they are not the same.



/GS is enabled by adding the /GS switch to the compiler command line. In Visual C++ 2008 and later, this option is enabled by default even if /GS is not included on the command line; so, the following command line enables /GS:




cl.exe foo.cpp


You can disable the option by using /GS–, but don’t do that! There is very little downside to using this compiler option.


/GS works by adding startup code to generate a random number and then modifying the stack a little for each function the compiler thinks needs defending, by adding the random number just prior to the function’s return address. So, a function’s stack memory might look like what’s shown in Figure 18-6.

















Higher Memory



Stack Variables (including function pointers)



Random Number



Function Return Address



Function Arguments






Figure 18-6: Stack memory layout after /GS

When the function returns, it compares the value of the random number generated when the application started up to the random number on the stack. If they are not the same, then something corrupted the stack, so the function kills the process. You may think that killing the process is a little draconian; it is, but it’s exactly the right thing to do. For some reason, your application’s stack is besmirched, so you really ought not to continue execution in this state. There’s a good side to halting the application: if the application is under development and your tests trip the /GS code, you get an application failure that is easy to debug and fix.


Stack corruption is detected only as the function exits and is about to return to its caller, so if an attacker can craft an exploit that executes before the function returns, then the /GS defense is most probably rendered useless. What this means to you is that /GS is a good defense, and will help make life harder for an attacker, but it offers no security guarantees. With that said, you should use /GS anyway unless you can write pristine C++ codean almost impossible task.





Exception Handling Defenses


Do you remember the CodeRed worm? If you used Internet Information Server 4.0 or Internet Information Services 5.0, then the answer is probably “yes.” CodeRed was a worm that took advantage of a buffer overrun bug in the Index Server code installed with IIS. The bug was fixed by Microsoft in bulletin MS01-033.


Interestingly, the CodeRed exploit took advantage of a stack-based buffer overrun, but if /GS had been around back in 2001 (it was not) and the code had been compiled with /GS (it was not), /GS would not have helped at all, because CodeRed corrupted an exception handler address on the stack instead of attacking the function’s return address. An exception handler is a unit of code that is 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.


CodeRed led Microsoft to add a new defense to the image that helps protect exception handlers. The defense, /SAFESEH, is a linker flag, not a compiler flag, and is enabled by default in Visual C++ 2008 and later. When you use this linker option, the linker stores the list of valid exception handlers in the image’s PE header at link time. When an exception is raised at run time, the operating system (Windows XP SP2, Windows Server 2003, Windows Vista, and Windows Server 2008 and later) won’t dispatch to an address in that image other than the valid exception handler addresses in the PE header added at link time. This alone would have prevented the CodeRed worm.





Data Execution Prevention/No eXecute/eXecute Disable


If you look closely at the vast majority of buffer overruns, they have one thing in common: they execute data. There are some valid reasons to execute data—for example, if your application has a just-in-time (JIT) compiler—but in most cases, executing data is a very bad idea and is highly discouraged.


To help make life harder for attackers, a new option was added that marks memory pages as writeable but not executable; AMD calls the technology No eXecute (NX), Microsoft calls it Data Execution Prevention (DEP), and Intel calls it eXecute Disable (XD). Most modern CPUs support this capability regardless of vendor. DEP support in Windows was first introduced in Windows XP SP2 and is a critically important defense in Windows Vista, especially when used with ASLR, explained in the next section.


By default, Windows Server 2008 enables DEP for all processes, but Windows Vista supports DEP for system processes and for applications that opt in to support DEP.


To enable DEP defenses for your application, you must link with the /NXCOMPAT linker option. Thankfully, like so many other defenses in Visual C++, this is enabled by default too.


You can see DEP at work with the following code:





// Code from Metasploit.com
unsigned char scode[] =
"\xfc\xe8\x44\x00\x00\x00\x8b\x45\x3c\x8b\x7c\x05\x78\x01\xef\x8b"
"\x4f\x18\x8b\x5f\x20\x01\xeb\x49\x8b\x34\x8b\x01\xee\x31\xc0\x99"
"\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x3b\x54\x24\x04"
"\x75\xe5\x8b\x5f\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5f\x1c\x01\xeb"
"\x8b\x1c\x8b\x01\xeb\x89\x5c\x24\x04\xc3\x31\xc0\x64\x8b\x40\x30"
"\x85\xc0\x78\x0c\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x68\x08\xeb\x09"
"\x8b\x80\xb0\x00\x00\x00\x8b\x68\x3c\x5f\x31\xf6\x60\x56\x89\xf8"
"\x83\xc0\x7b\x50\x68\x7e\xd8\xe2\x73\x68\x98\xfe\x8a\x0e\x57\xff"
"\xe7\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";

typedef void (*F)(void);

int _tmain(int argc, _TCHAR* argv[]) {
F f = (F)(void*)scode;
(*f)(); // run the data
return 0;
}



If you compile and link this code but do not link with /NXCOMPAT, you will notice that a copy of the Windows Calculator pops up. Think about this for a moment; this code just ran some data and ran a simple and innocuous piece of code, but that could have been malware.


That’s how easy it is to run data.


Now relink the code, but this time make sure /NXCOMPAT is enabled. Then run the code again; note that the calculator does not pop up and your application crashes with a 0xC0000005 Access Violation error. That’s DEP in action.


You can turn /NXCOMPAT on or off by following these steps:




  1. Right-click the project name in Solution Explorer and click Properties.




  2. Expand Configuration Properties.




  3. Expand Linker.




  4. Expand Advanced.




  5. Select Image Is Compatible with DEP (/NXCOMPAT) to enable DEP for this process, or select Image Is Not Compatible with DEP (/NXCOMPAT:NO) to disable DEP.




If you are testing your application on Windows Server 2008, your code will always crash, because DEP is enabled all the time. You can still test DEP by opting your process out of DEP support. To do this, go to the Control Panel and then follow these steps:




  1. Select System and Maintenance.




  2. Select System.




  3. Select Advanced System Settings.




  4. Click the Advanced tab.




  5. Click Settings under the Performance category.




  6. Click the Data Execution Prevention tab.




  7. Click Add.




  8. Add the name of your application.




One caveat with DEP is that if your application needs to execute data, you should still opt in to DEP and mark any data that will be executed like this:





PVOID pBuff = VirtualAlloc(NULL,4096,MEM_COMMIT,PAGE_READWRITE );
if (pBuff) {
// Copy executable ASM code to buffer
CopyMemory(pBuff,...)
// Mark buffer as executable and protect from writes
DWORD dwOldProtect = 0;
if (!VirtualProtect(pBuff,sizeof scode,PAGE_EXECUTE_READ,&dwOldProtect))
// oops, error
else
// Call into pBuff
VirtualFree(pBuff,0,MEM_RELEASE);
}



DEP by itself is a reasonable defense, but it really starts to shine as a truly valuable defense when used in conjunction with another defense offered by Windows Vista and Windows Server 2008 and later: Address Space Layout Randomization.





Address Space Layout Randomization


The goal of ASLR is very simple: make it harder for attacks to succeed by removing much of the memory layout predictability from the operating system.


ASLR moves images into random locations when a system boots to make it harder for attack code to operate successfully. For a component to support ASLR, all components that it loads must also support ASLR. For example, if Foo.EXE consumes Boo.DLL and Coo.DLL, all three must support ASLR. By default, Windows Vista will randomize system DLLs and EXEs, but DLLs and EXEs created by software vendors must opt in to support ASLR by linking with the /DYNAMICBASE linker option.


ASLR not only juggles processes around in memory, but also juggles the stack around. When a thread starts in a process compiled with /DYNAMICBASE, Windows Vista (and later) moves the thread’s stack at a random offset (0 to 31 pages) to help reduce the chance that a stack-based buffer overrun will succeed.


You can verify ASLR by using code like this, which simply displays the address of a local stack variable:




int main(int argc, char* argv[]) {
argc;
argv;

char p = 0;

printf("%08X",&p);
return 0;
}



On the author’s Windows Server 2008 computer, compiling the code without / DYNAMICBASE (or /DYNAMICBASE:NO) and running the resulting application five times always yields 0012FF2B, but when linked with /DYNAMICBASE, the code yields the following addresses:




  • 0027F8CF




  • 0031FBBF




  • 0029F9DF




  • 0017FCF7




  • 0031F957




As you can see, the stack is moved around each time the application starts. This, along with moving the image in memory, removes much of the predictability desired by attackers. If you run this same code on Windows XP, which does not have support for randomization, you’ll notice the address always comes out the same. In fact, the chances are very good indeed that the address will be the same on one Windows XP machine and the next, and the next, and so on. As you probably now realize, ASLR is an extremely useful defense to help make exploits fail to run correctly.


There is one important caveat: for ASLR to be effective, you must combine ASLR with DEP. Also note that if you enable any of these defenses, your application will still run correctly on versions of Windows that do not support these defenses.


























No comments:

Post a Comment