Безопасность Windows Vista для независимых поставщиков программного обеспечения

 

Майкл Говард (Michael Howard) и Мэтт Томлинсон (Matt Thomlinson)
Корпорация Майкрософт

Апрель 2007 года

Введение

В Windows Vista предлагается множество усовершенствований в защите клиентов от вредоносных программ. Приложениям, работающим на этой платформе, следует в полной мере использовать их, поскольку они, в сущности, бесплатны и могут превратить ошибку в коде из серьезной уязвимости в ошибку, приводящую к краху системы.

Что касается Internet Explorer, то некоторые из способов защиты Windows Vista могут сработать только в том случае, когда все компоненты, употребляемые обозревателем, поддерживают их. Поскольку обозреватели являются одними из наиболее часто атакуемых (и взламываемых) компонентов программного обеспечения, важно, чтобы популярные компоненты, употребляемые обозревателем, использовали данные способы защиты в полной мере. Целью этого короткого документа является предоставление сведений об этих способах защиты и объяснение, как поставщикам программного обеспечения следует использовать их, чтобы защитить наших общих клиентов.

Этот документ является сильно сокращенной версией статьи «Написание безопасного кода для Windows Vista» ("Writing Secure Code for Windows Vista") от Говарда и Лебланка (LeBlanc) и приложим только к неуправляемому (не использующему .NET) коду на C и C++.

Многочисленные защиты BO в Windows Vista

Windows Vista включает множество стратегий защиты клиентских систем от взлома. Некоторые из этих способов защиты входят в ядро операционной системы, другие же предлагаются компилятором Microsoft Visual C++. Способы защиты включают в себя следующие.

  • Обнаружение переполнения буфера стека /GS.
  • Защита обработки исключений /SafeSEH.
  • No eXecute (NX) / Data Execution Prevention (предотвращение выполнения данных) (DEP) / eXecute Disable (XD).
  • Придание случайного характера компоновке адресного пространства (ASLR).
  • Случайное расположение кучи.
  • Случайное расположение стека.
  • Обнаружение нарушений в куче.

В оставшейся части документа мы кратко расскажем о каждой из этих защит и предложим руководство по развертыванию и тестированию.

Обнаружение переполнения буфера стека /GS.

Эта возможность появилась в новом компиляторе для C/C++ в Visual Studio .NET 2002, а затем была обновлена в Visual Studio .NET 2003 и Visual Studio 2005. /GS – это ключ компилятора, дающий компилятору указание добавить код запуска, а также код пролога и эпилога функции для создания и проверки случайного числа, которое помещается в стек функции. В случае повреждения этого значения вызывается функция обработки для прерывания работы приложения и уменьшения шансов правильного исполнения кода оболочки, который пытается использовать переполнение буфера во вредоносных целях.

/GS включается путем добавления ключа /GS к командной строке компилятора. В Visual C++ эта возможность включена по умолчанию, даже если /GS нет в командной строке.

Примечание.   Оценить воздействие /GS на производительность сложно, поскольку оно сильно зависит от стиля кодирования. Для кода с большим количеством аргументов и буферов строки на основе стеков оно будет заметным, для кода, где их нет, воздействия не будет. Примерная оценка может дать около 3%, но в поздних версиях компилятора это частично компенсируется другими оптимизациями. Следует также заметить, что основная часть потенциально уязвимого кода выполняет операции сетевого и дискового ввода-вывода, которые на много порядков медленнее, чем небольшие проверки /GS.

Обратите внимание на то, что Visual C++ 2005 также перемещает данные на стеке, чтобы затруднить их повреждение. В число примеров таких действий входят следующие.

  • Перемещение буферов в более высокие адреса памяти, чем все остальное. Это действие может помочь при защите указателей функций, размещенных на стеке.
  • Перемещение указателей и аргументов буфера в более низкие адреса памяти по ходу выполнения для смягчения последствий различных атак, основанных на переполнении буфера.

Требования к независимым поставщикам программного обеспечения

  • Независимым поставщикам следует компилировать свой код с помощью новейшего доступного компилятора, Visual C++ 2005.
  • Компиляцию следует производить с флагом /GS.
  • Компоновку следует проводить с использованием библиотек, использующих /GS.

Защита обработки исключений /SafeSEH.

Обработчик исключений – это модуль кода, выполняемый при возникновении исключительных ситуаций, таких как деление на ноль. Адрес обработчика держится в кадре стека функции, а следовательно, подвержен повреждениям или перехвату. Компоновщик, включенный в Visual Studio 2003 и более поздние версии, содержит возможность сохранить во время компиляции список допустимых обработчиков исключений в заголовке предвыполнения образа. Когда во время выполнения происходит исключение, операционная система (Windows XP SP2, Windows Server 2003, Windows Vista и Windows «Longhorn» Server, а также последующие версии) не будет производить передачу другому адресу в этом образе, помимо адресов обработчиков исключений в заголовке предвыполнения.

Примечание.   /SafeSEH не влияет на производительность ветвей кода, не столкнувшихся с исключением.

Требования к независимым поставщикам программного обеспечения

  • Независимым поставщикам следует компоновать свои образы с помощью /SafeSEH.
  • Компоновку следует производить посредством библиотек, также скомпонованных с помощью /SafeSEH.

Data Execution Prevention («Предотвращение исполнения данных») (DEP) / No eXecute (NX) / eXecute Disable (XD)

AMD именует эту технологию NX, Майкрософт – DEP, а Intel – XD. Она требует поддержки ЦП, помогающей предотвращать исполнение кода в сегментах данных. Большинство современных ЦП Intel сейчас поддерживают эту возможность, и все выпускаемые ЦП AMD поддерживают NX. Поддержка DEP впервые появилась Windows XP с пакетом обновления 2 (SP2) и является одним из ключевых приемов защиты в Windows Vista, особенно в сочетании с приданием случайного характера компоновке адресного пространства (АSLR), о которой будет рассказано ниже.

Недостатком DEP является то, что если в приложении имеется самомодифицирующийся код или оно выполняет компиляцию непосредственно перед выполнением, DEP вызовет сбой приложения. Чтобы облегчить эту проблему, поставщикам приложений следует все же использовать DEP (см. приведенный ниже ключ компоновщика) и отметить любые данные, которые будут использованы в динамической компиляции, следующим образом:

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);
}

Примечание.   DEP/NX/XD не влияет на производительность.

Требования к независимым поставщикам программного обеспечения

  • Независимым поставщикам следует тестировать свои приложения на ЦП, поддерживающем DEP, отмечая и исправляя любые сбои, вызванные NX.
  • Следует также компоновать код с помощью /NXCOMPAT после успешного завершения тестирования DEP.

Придание случайного характера компоновке адресного пространства (ASLR).

При ASLR образы перемещаются в случайные местоположения при загрузке системы, что затрудняет успешные действия кода оболочки. Чтобы компонент поддерживал ASLR, все загружаемые им компоненты также должны поддерживать ASLR. Например, если A.EXE употребляет B.DLL и C.DLL, все три должны поддерживать ASLR. По умолчанию Windows Vista придаст случайный характер размещению системных файлов DLL и EXE, но файлы DLL и EXE, созданные независимыми поставщиками программного обеспечения, должны для этого поддерживать ASLR.

Примечание.   В целом ASLR не влияет на производительность. На 32-разрядных системах оно даже может вызвать ее незначительное улучшение. Впрочем, в сильно перегруженных системах с большим количеством случайных образов может произойти ухудшение, однако его сложно измерить, поскольку оно зависит от числа и размера образов.

Требования к независимым поставщикам программного обеспечения

  • Независимым поставщикам следует тестировать свои приложения на Windows Vista, отмечая и исправляя любые сбои, вызванные ASLR.
  • Им следует обеспечить поддержку ASLR во всех образах, проводя компоновку с помощью обновленного компоновщика Microsoft (версии 8.00.50727.161 или более поздней).
  • Компоновку следует проводить с ключом компоновщика /DYNAMICBASE.

Случайное расположение кучи.

Когда приложение создает кучу в Windows Vista, диспетчер кучи создаст ее в случайном местоположении, понижая вероятность удачного переполнения буфера на основе кучи. Случайное расположение кучи включено по умолчанию во всех приложениях Windows Vista.

Примечание.   Влияние на производительность несущественно.

  • Независимым поставщикам программного обеспечения следует тестировать свои приложения на Windows Vista, отмечая и исправляя любые сбои, вызванные случайным расположением кучи.

Случайное расположение стека

При запуске потока в процессе, скомпилированном с ключом /DYNAMICBASE, Windows Vista перемещает стек потока в случайное местоположение, понижая вероятность удачного переполнения буфера на основе стека.

Примечание.   Влияние на производительность несущественно.

  • Независимым поставщикам программного обеспечения следует компоновать свой код с ключом /DYNAMICBASE, тестировать приложения на Windows Vista и отмечать и исправлять сбои, вызванные случайным расположением стека.

Обнаружение нарушений в куче

Обнаружение нарушений в куче – это возможность вызвать сбой приложения в случае, если диспетчер кучи обнаруживает, что приложение повредило ее, или если куча становится несогласованной. Этот параметр обнаруживает не только переполнение буфера на основе кучи, но и определенные неразрешенные действия; например освобождение указателя на неверную кучу также приведет к сбою приложения.

Совместимость приложений с обнаружением нарушений в куче требует небольшого изменения в коде, применимого только в Windows Vista. Нижеследующий код просто не будет выполнен (не вызывая при этом дополнительных проблем) на платформах Windows, не поддерживающих эту функцию (в первую очередь Windows 2000), и включит возможность обнаружения нарушений в куче для вызывающего приложения при работе на 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;
}

Как проявляется обнаружение нарушений в куче

При прекращении работы приложения из-за нарушения в куче пользователь может увидеть примерно следующее в отладчике, работающем под 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

Примечание.   Влияние на производительность отсутствует.

Требования к независимым поставщикам программного обеспечения

  • Независимым поставщикам следует включать этот код во все файлы EXE. Если компонент является файлом DLL или элементом управления ActiveX, включать в него этот код или устанавливать флаг обнаружения нарушений в куче нет необходимости.

Значение и приоритет различных способов защиты

Нижеследующая таблица обрисовывает относительное значение этих способов защиты и приоритетность их поддержки независимыми поставщикам программного обеспечения.

Способ защиты Степень приоритетности
Придание случайного характера компоновке адресного пространства Важнейшая
Использование DEP Важнейшая
Обнаружение переполнения буфера на основе стека при помощи /GS Высокая
Защита обработчика исключений при помощи /SafeSEH. Высокая
Тестирование случайного расположения стека Средняя
Тестирование случайного расположения кучи Средняя
Обнаружение нарушений в куче Средняя

Как проводить тестирование

После внесения любых изменений в код и общий проект приложения важно проверить правильность настройку операционной системы и наличие соответствующих изменений в коде приложения.

Использование компилятора для C++

Убедитесь, что используется версия компилятора 13.10 или более поздняя. Использование версии 14.00 или более поздней настоятельно рекомендуется, поскольку эта версия включена в Visual Studio 2005 и предлагает лучшую из реализаций /GS.

Использование /GS

Откройте файл(ы) make, связанные с продуктом. Если используются более ранние версии Visual C++, чем Visual C++ 2005, убедитесь, что весь код скомпилирован с ключами /GS или –GS. Если используется Visual C++ 2005, убедитесь в отсутствии ссылок на /GS- или –GS-. Параметры компилятора зависят от регистра символов, и –GS не эквивалентен –Gs.

Использование /SafeSEH

Откройте файлы make, связанные с продуктом, и убедитесь, что компоновщик использует /SafeSEH. Параметры компоновщика не зависят от регистра символов.

Совместимость с DEP

Есть много способов проверить, поддерживает ли DEP существующая комбинация BIOS и ЦП и включен ли он. Проще всего открыть Панель управления / Система / Дополнительно / Быстродействие / Параметры быстродействия / Предотвращение выполнения данных. Если DEP включен, то можно будет увидеть слова:

«процессор этого компьютера допускает аппаратную поддержку DEP».

Если надо определить из командной строки, включен ли DEP, выполните следующие действия для определения, включен ли он на компьютере, используемом для тестирования продукта.

Команда Возможные результаты Требуемый результат
wmic OS Get DataExecutionPrevention_
Доступно
TRUE – DEP доступен

FALSE – DEP не доступен

TRUE
wmic OS Get DataExecutionPrevention_
SupportPolicy
0 – DEP отключен для всех процессов

1 – DEP включен для всех процессов

2 – включено использование DEP для компонентов системы Windows

3 – включено использование DEP для всех компонентов, кроме особо оговоренных

1
wmic OS Get DataExecutionPrevention_
32BitApplications
TRUE – DEP включен для приложений

FALSE – DEP отключен для приложений

TRUE
wmic OS Get DataExecutionPrevention_
Драйверы
TRUE – DEP включен для драйверов

FALSE – DEP отключен для для драйверов

TRUE

На некоторых компьютерах необходимо включать DEP через параметр BIOS. Также обратите внимание на то, что в некоторых случаях NX может все же оказаться незадействованным для созданного приложения или компонента, и это часто вызывается привязкой приложения к библиотекам, для которых NX не включен, таким как старые версии библиотек абстрактных типов (ATL), включенных в Visual Studio. Если библиотека ATL необходима, используйте версию, поставляемую в составе Visual Studio 2003 SP1 (ATL v7.1) или Visual Studio 2005 (ATL v8.0). Версию библиотеки ATL можно проверить с помощью примерно такой строки кода отладчика:

  // Version in Visual Studio .NET 2003 
  assert(AtlGetVersion(NULL) >= 0x0710);

Совместимость с ASLR

Убедитесь, что в файлах make используется флаг компоновщика /DYNAMICBASE (не зависящий от регистра). Далее убедитесь, что создаваемое приложение динамически размещено в памяти.

Проверка файла EXE

Если приложение –– это файл EXE, то можно выполнить следующие задачи.

  • Запустите отладчик и проведите отладку нужного процесса, при этом отметьте адрес загрузки.
  • Выполните перезагрузку.
  • Повторите действие (1).

Если для процесса включено придание случайного характера, можно будет заметить, что адреса загрузки изменились. Ниже приведен пример выходных данных из файла cdb.exe:

Путь поиска исполняемого файла: 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

Путь исполняемого поиска: 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

Совместимость с обнаружением нарушений в куче

Лучший способ проверить совместимость – найти верный вызов HeapSetInformation в коде.

Тестирование совместимости со случайным расположением стека и кучи

Простого запуска приложения на Windows Vista достаточно для проверки совместимости, поскольку эти защиты включены по умолчанию.

Заключение

Обозреватели сейчас являются наиболее часто атакуемой частью программного обеспечения, поэтому важно полное использование ими способов защиты, предлагаемых операционной системой. Это распространяется и на компоненты, используемые обозревателем. Следование указаниям этого документа позволит независимым поставщикам программного обеспечения создавать компоненты, обладающие большей защищенностью при размещении в Internet Explorer.

Ссылки