Visual C++ Defenses and 64-bit
Michael Howard just published a good article here on how Visual C++ features can help protect your app. I go into a fair bit more detail on these in our most recent book, "Writing Secure Code for Windows Vista" (WSCV) if you're curious. Something Michael left out of the article was how this changes when you go to 64-bit – to be completely accurate, I'd need to go sit down with my compiler and debugger and go look at the actual code that's emitted – which I don't have time for this evening – but I'll take a shot anyway, and if I get something wrong, I'm sure someone will correct me.
- Stack Overrun detection – by and large the same stuff as on x86, but with one interesting twist. There's a lot more registers available in x64, which is part of why some code runs faster on x64, and one of the things that changes is that x64 uses fastcall as the default calling convention. What this does on x86 is that it puts the first argument into a register rather than pushing it on the stack. If it's in a register, it can't be overrun as easily. On x64, it puts the first four arguments in registers. So this largely removes a fairly significant attack vector, and means that most functions won't need duplicate arguments in 2 places on the stack. It's also arguably a little safer, since a buffer _underrun_ might still get the x86 code, but won't typically attack the argument on x64.
- SafeSEH – IIRC, the exception handlers get compiled in, and aren't loitering on the stack to see if they'll get mugged.
- NXCompat – everyone gets this, like it or not. Much safer that way.
- DynamicBase (ASLR) – I don't think there's any changes here.
The CRT is still the CRT, so no difference there, and new is the same thing as before. Something else that we do that I learned from John McDonald is that when you call delete[], the destructor needs to get called for every object in the array. Some other compilers will run through the whole array, calling the destructor for each object in turn, referencing what that object claims is its destructor. Thus if someone can whack a vtable for just one of them, off to the arbitrary code races we go. Visual C++ will just call the destructor function for the first one, and it's less likely that an attacker can get to that one vtable – this ends up being safer.
Comments
- Anonymous
March 20, 2008
One additional way that x64 provides additional security -- the exception handlers are described in the binary metadata and are not pushed onto the stack, so attacks that smash the exception handler aren't viable. [dcl] Yup, I mentioned that. Also -- a clarification to the original post -- while it is true that the first arguments are passed in registers on x64, arguments have pre-assigned stack slots as 'spill space' that the compiler will use if it wants to use those registers for something else. So there is a predictable location on the stack for most args which can be used by an attacker. [dcl] Right - and how the compiler deals with what goes in a register is going to vary with the function, inlining, and a lot of other things. There's still going to be stuff to attack, but there will in general be less of it. Every little bit helps. I don't think anyone has really dug into the nuances of creating x64 exploits yet. Due to the many differences in how everything works, I'm not going to be surprised to see a fair number of cases where something is exploitable on x86 and not on x64. That said, I don't expect Metasploit to go out of business, either.