Как Visual Studio 2013 обнаруживает переполнение буфера

проекты Visual Studio 2013 C++ имеют /GS переключатель для включения проверки безопасности буфера во время выполнения. Мы сталкиваемся со многими другими ошибками STATUS_STACK_BUFFER_OVERRUN с момента обновления до VS 2013 и подозреваем, что это связано с улучшенной проверкой переполнения буфера в новом компиляторе. Я пытался проверить это и лучше понять, как обнаруживается переполнение буфера. Меня сбивает с толку тот факт, что переполнение буфера сообщается даже тогда, когда память обновляется оператор изменяет только содержимое другой локальной переменной в стеке в той же области! Таким образом, он должен проверять не только то, что изменение не повреждает память, не "принадлежащую" локальной переменной, но и то, что изменение не влияет ни на одну локальную переменную, кроме той, на которую ссылается отдельная инструкция update. Как это работает? Изменился ли он с против 2010?

Edit: Вот пример, иллюстрирующий случай, что Mysticial объяснение не покрывает:

void TestFunc1();

int _tmain(int argc, _TCHAR* argv[])
{
   TestFunc1();
   return 0;
}

void TestFunc1()
{
   char buffer1[4] = ("123");
   char buffer2[4] = ("456");
   int diff = buffer1 - buffer2;
   printf("%dn", diff);
   getchar();
   buffer2[4] = '';
}

выход 4 указывает, что память, которая будет перезаписана, находится в пределах buffer1 (сразу после buffer2), но затем программа завершается с переполнением буфера. Технически это следует рассматривать как переполнение буфера, но я не знаю, как он обнаруживается, поскольку он все еще находится в хранилище локальных переменных и не повреждает ничего за пределами локальных переменных.

этот скриншот с макетом памяти доказать это. После шага одной строки программа прервалась с ошибкой переполнения буфера. Debugger screenshot with memory layout

Я просто попробовал тот же код в VS 2010, и хотя режим отладки поймал переполнение буфера (со смещением буфера 12), в режиме выпуска он сделал не поймать его (со смещением буфера 8). Поэтому я думаю, что VS 2013 ужесточил поведение /GS переключатель.

Edit 2: Мне удалось проскользнуть мимо даже VS 2013 диапазон проверки с настоящий кодекс. Он все еще не обнаружил, что попытка обновить одну локальную переменную фактически обновила другую:

void TestFunc()
{
   char buffer1[4] = "123";
   char buffer2[4] = "456";
   int diff;
   if (buffer1 < buffer2)
   {
      puts("Sequence 1,2");
      diff = buffer2 - buffer1;
   }
   else
   {
      puts("Sequence 2,1");
      diff = buffer1 - buffer2;
   }

   printf("Offset: %dn", diff);
   switch (getchar())
   {
   case '1':
      puts("Updating buffer 1");
      buffer1[diff] = '!';
      break;
   case '2':
      puts("Updating buffer 2");
      buffer2[diff] = '!';
      break;
   }
   getchar(); // Eat enter keypress
   printf("%s,%sn", buffer1, buffer2);
}

2 ответов


вы видите улучшение механизма /GS, впервые добавленного в VS2012. Первоначально /GS мог обнаруживать переполнения буфера, но все еще есть петля, где атакующий код может топтать стек, но обходить cookie. Примерно так:

void foo(int index, char value) {
   char buf[256];
   buf[index] = value;
}

Если злоумышленник может манипулировать стоимостью индекс тогда cookie не помогает. Этот код теперь переписан на:

void foo(int index, char value) {
   char buf[256];
   buf[index] = value;
   if (index >= 256) __report_rangefailure();
}

просто проверка индекса. Который при срабатывании мгновенно прекращается приложение с __fastfail (), если отладчик не подключен. Backgrounder здесь.


С страница MSDN on /GS в Visual Studio 2013:

Проверки Безопасности

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

для более подробной информации, та же страница относится к Проверка Безопасности Компилятора В Глубине:

что делает / GS

переключатель /GS обеспечивает "Рему скорости," или cookie, между буфера и обратный адрес. Если переполнение записывает по обратному адресу, ему придется перезаписать файл cookie, помещенный между ним и буфером, что приведет к новому макету стека:

  • параметры функции
  • функция обратного адреса
  • указатель фрейма
  • Cookie
  • фрейм обработчика исключений
  • локально объявленные переменные и буфера
  • вызываемый сохранить регистры

файл cookie будет рассмотрен более подробно позже. Выполнение функции изменяется с помощью этих проверок безопасности. Во-первых, при вызове функции первые инструкции для выполнения находятся в прологе функции. Как минимум, пролог выделяет пространство для локальных переменных в стеке, таких как следующая инструкция:

sub esp, 20h

эта инструкция выделяет 32 байта для использования локальными переменными в функции. Когда функция скомпилированные с /GS, функции prolog отложат дополнительные четыре байта и добавят еще три инструкции следующим образом:

sub   esp,24h
mov   eax,dword ptr [___security_cookie (408040h)]
xor   eax,dword ptr [esp+24h]
mov   dword ptr [esp+20h],eax

пролог содержит инструкцию, которая извлекает копию файла cookie, за которой следует инструкция, которая выполняет логический xor файла cookie и обратного адреса, а затем, наконец, инструкция, которая хранит файл cookie в стеке непосредственно под обратным адресом. С этого момента функция будет выполняться так, как она это делает обычно. Когда функция возвращается, последнее, что нужно выполнить, - это эпилог функции, противоположный прологу. Без проверки безопасности он вернет пространство стека и вернет, например, следующие инструкции:

add   esp,20h
ret

при компиляции с /GS проверки безопасности также помещаются в эпилог:

mov   ecx,dword ptr [esp+20h]
xor   ecx,dword ptr [esp+24h]
add   esp,24h
jmp   __security_check_cookie (4010B2h)

извлекается копия cookie стека, а затем следует с инструкцией XOR с помощью обратный адрес. Регистр ECX должен содержать значение, соответствующее исходному файлу cookie, хранящемуся в переменной __security_cookie. Затем пространство стека восстанавливается,а затем вместо выполнения инструкции RET выполняется инструкция JMP для процедуры cookie__security _ check_.

процедура __security_check_cookie проста: если файл cookie не изменился, он выполняет инструкцию RET и завершает вызов функции. Если cookie не соответствует, процедура вызывает report_failure. Затем функция report_failure вызывает обработчик__security _ error _ (_SECERR_BUFFER_OVERRUN, NULL). Обе функции определены в seccook.C файл исходных файлов времени выполнения C (CRT).