Welcome to MSDN Blogs Sign in | Join | Help

/DYNAMICBASE和/NXCOMPAT

大家好,我叫范翔。我是微软C++上海团队的一名软件开发工程师。

今天我想向大家介绍一下两个和安全性相关的链接器选项:/DYNAMICBASE/NXCOMPAT

 

这两个选项是在VS2005中引入的,目的是提高本机代码的安全性。

 

您可以在IDE中设置它们:

test

这两个选项在IDE中有三个可选值:OnOffDefault

 

如果你是用VS2008的向导生成的C++工程,那么它们会被设置为“On”

VS2008从不支持这两个选项的老版本工程进行升级时 ,会将这两个选项设置为“Off”。

如果您选择“Default”,链接器的默认值是“Off”。

 

经过多年的推广,我们决定在VS2010中将“Default”对应的值改为“On”,以增强C++应用程序的安全性。

 

下面是对这两个选项的详细介绍:

 

1.      DYNAMICBASE

 

/DYNAMICBASE使用 Windows Vista 的地址空间布局随机化 ASLRAddress Space Layout Randomization 功能,指定是否生成可在加载时随机重新设定基址的可执行文件映像。

该选项隐含了“/FIXED:NO”,以便在可执行文件中生成重定位节。 具体请参见/FIXED中的说明。

 

默认情况下,如果某个组件需要 Windows Vista /SUBSYSTEM 6.0和更高版本),则会自动打开 /DYNAMICBASE

/DYNAMICBASE:NO可以用于禁用随机重新设定基址。

 

这篇文件对ASLR进行了介绍: http://technet.microsoft.com/zh-cn/magazine/cc162458.aspx

ASLR仅被Windows Vista和之后的操作系统支持,它会被之前的操作系统忽略。

 

ASLR对于应用程序来说是透明的。当开启ASLR的时候,唯一的区别是,操作系统将会无条件的对可执行文件重新设定基址。如果未开启ASLR,只有当发生地址冲突的时候,操作系统才会重新设定基址。

 

2.      NXCOMPAT

 

/NXCOMPAT用于指定可执行文件与 Windows 数据执行保护功能DEPData Execution Prevention)兼容

请注意,该选项只适用于32位的可执行文件。非32位的桌面版Windows(比如x64IA64)总是会为64位的应用程序开启DEP

 

下面是对DEP的详尽介绍:

http://support.microsoft.com/kb/875352

 

默认情况下,如果某个组件需要 Windows Vista /SUBSYSTEM 6.0和更高版本),则会自动打开 /NXCOMPAT

/NXCOMPAT:NO可以用于指定应用程序和DEP不兼容。然而,管理员仍然可以强制为所有的应用程序开启DEP。因此,您总是应当测试您的程序和DEP的兼容性。

 

Windows Vista SP1Windows XP SP3Windows Server 2008增加了一个新的API SetProcessDEPPolicy。它允许开发人员在运行时而不是链接的时候设置程序的DEP策略。具体的信息请参考下面的链接:

http://blogs.msdn.com/michael_howard/archive/2008/01/29/new-nx-apis-added-to-windows-vista-sp1-windows-xp-sp3-and-windows-server-2008.aspx

 

下面列举了常见的和DEP不兼容的例子(更详细的信息请参见http://msdn.microsoft.com/en-us/library/aa366553.aspx)

a.       在堆上或者栈上动态生成代码

如果您的程序必须运行内存中的代码,存放代码的内存必须拥有正确的属性

 

分配的内存必须被标记为PAGE_EXECUTEPAGE_EXECUTE_READPAGE_EXECUTE_READWRITE或者PAGE_EXECUTE_WRITECOPY。使用newmallocHeapAlloc函数分配的内存是不能运行代码的。应用程序需要调用VirtualAlloc函数分配可以运行代码的内存。

 

那外一种方法是在使用HeapCreate创建堆的时候传入HEAP_CREATE_ENABLE_EXECUTE。这样之后用HeapAlloc分配的内存就可以运行代码了。

 

b.      在数据段中执行代码

您应当将代码放置在代码段

 

如果没有开启DEP,安全漏洞的破坏性会更大。所以您应当总是使您的程序和DEP兼容并开启DEP

 

下面的例子展示了和DEP不兼容的代码。它同时展示了两种和DEP兼容的在堆上运行代码的方法。

在数据段或者栈上运行代码总是非常危险的,会被恶意用户利用产生安全漏洞。您应当将代码放在代码段或者堆上。

 

#include "windows.h"

#include <cstdio>

 

typedef void (*funType)();

unsigned char gCode[] = {0xC3}; // x86上的ret指令的机器码

const size_t gCodeSize = sizeof(gCode);

 

// 下面的代码和DEP不兼容

 

void RunCodeOnHeap()

{

    unsigned char *code = new unsigned char[gCodeSize];

    memcpy(code, gCode, gCodeSize);

 

    funType fun = reinterpret_cast<funType>(code);

    fun();

 

    delete []code;

}

 

// 下面的代码和DEP兼容

 

void RunCodeOnHeapCompatible1()

{

    unsigned char *code = (unsigned char *)::VirtualAlloc(NULL, gCodeSize, MEM_COMMIT, PAGE_READWRITE);

    memcpy(code, gCode, gCodeSize);

 

    DWORD flOldProtect;

    ::VirtualProtect(code, gCodeSize, PAGE_EXECUTE_READ, &flOldProtect);

 

    funType fun = reinterpret_cast<funType>(code);

    fun();

 

    ::VirtualFree(code, 0, MEM_RELEASE);

}

void RunCodeOnHeapCompatible2()

{

    HANDLE hheap = ::HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);

 

    unsigned char *code = (unsigned char *)::HeapAlloc(hheap, 0, gCodeSize);

    memcpy(code, gCode, gCodeSize);

 

    funType fun = reinterpret_cast<funType>(code);

    fun();

 

    ::HeapFree(hheap, 0, code);

    ::HeapDestroy(hheap);

}

 

INT DEPExceptionFilter(LPEXCEPTION_POINTERS lpInfo)

{

    // 详细信息请参见 http://technet.microsoft.com/en-us/library/bb457155.aspx

 

    if (lpInfo->ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION &&

        lpInfo->ExceptionRecord->ExceptionInformation[0] == 8) {

        return EXCEPTION_EXECUTE_HANDLER;

    }

 

    return EXCEPTION_CONTINUE_SEARCH;

}

 

int main()

{

    __try

    {

        RunCodeOnHeap();

        printf("RunCodeOnHeap: OK\n");

    }

    __except (DEPExceptionFilter(GetExceptionInformation()))

    {

        printf("RunCodeOnHeap: Fail due to DEP\n");

    }

    __try

    {

        RunCodeOnHeapCompatible1();

        printf("RunCodeOnHeapCompatible1: OK\n");

    }

    __except (DEPExceptionFilter(GetExceptionInformation()))

    {

        printf("RunCodeOnHeapCompatible1: Fail due to DEP\n");

    }

    __try

    {

        RunCodeOnHeapCompatible2();

        printf("RunCodeOnHeapCompatible2: OK\n");

    }

    __except (DEPExceptionFilter(GetExceptionInformation()))

    {

        printf("RunCodeOnHeapCompatible2: Fail due to DEP\n");

    }

}

 

输出:

 

cl test.cpp /link /nxcompat:no

 

RunCodeOnHeap: OK

RunCodeOnHeapCompatible1: OK

RunCodeOnHeapCompatible2: OK

 

cl test.cpp /link /nxcompat

 

RunCodeOnHeap: Fail due to DEP

RunCodeOnHeapCompatible1: OK

RunCodeOnHeapCompatible2: OK

 

综上,在VS2010以前, “cl test.cpp”等价于“cl test.cpp /link /nxcompat:no /dynamicbase:no”。在VS2010中,它将等价于“cl test.cpp /link /nxcompat /dynamicbase”

如果您对于这两个选项的默认值的改动有任何意见或者建议,欢迎反馈。谢谢!

 

Posted by ShanghaiCPPTeam | 1 Comments
Filed under:

Visual C++ Shanghai Team Blog 上海团队博客 开通了

欢迎大家来访问 Visual C++ Shanghai Team Blog 上海团队博客!上海Visual C++团队的成员会写一些我们做项目的体验以及产品技术方面的评论。同时也会翻译部分 Visual C++ Team Blog 上的博客文章。旨在为读中文的开发人员提供中文内容。

Pung Xu (徐鹏阳)

 

 
Page view tracker