Можно ли защитить область памяти от WinAPI?

Прочитав эту интересную статью, в которой описывается метод отладки повреждения кучи , я начал задаваться вопросом, как я могу настроить его для своих нужд. Основная идея состоит в том, чтобы предоставить пользовательский malloc() для выделения целых страниц памяти, а затем включить некоторые биты защиты памяти для этих страниц, чтобы программа аварийно завершала работу при записи в них, а неправильная инструкция записи могла быть поймана в действии. . Пример кода — C под Linux (для включения защиты используется mprotect()), и мне любопытно, как применить это к родному C++ и Windows. VirtualAlloc() и/или VirtualProtect() выглядят многообещающе, но я не уверен, как будет выглядеть сценарий использования.

Fred *p = new Fred[100];
ProtectBuffer(p);
p[10] = Fred(); // like this to crash please

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

РЕДАКТИРОВАТЬ: Кроме того, это вообще хорошая идея под Windows или просто развлекательное интеллектуальное упражнение?


person neuviemeporte    schedule 30.01.2013    source источник


Ответы (2)


Да, вы можете использовать VirtualAlloc и VirtualProtect для настройки разделов памяти, защищенных от операций чтения/записи.

Вам придется заново реализовать operator new и operator delete (и их [] родственников), чтобы распределение памяти контролировалось вашим кодом.

И имейте в виду, что это будет только для каждой страницы, и вы будете использовать (по крайней мере) три страницы виртуальной памяти для каждого выделения - не большая проблема в 64-битной системе, но может вызвать проблемы, если у вас много распределений в 32-битной системе.

Примерно то, что вам нужно сделать (на самом деле вы должны найти размер страницы для сборки Windows - я слишком ленив, поэтому я буду использовать 4096 и 4095 для представления размера страницы и размера страницы-1 - вам также нужно будет сделать больше проверка ошибок, чем этот код!!!):

void *operator new(size_t size)
{
    Round size up to size in pages + 2 pages extra.
    size_t bigsize = (size + 2*4096 + 4095) & ~4095; 

    // Make a reservation of "size" bytes. 
    void *addr = VirtualAlloc(NULL, bigsize, PAGE_NOACCESS, MEM_RESERVE);

    addr = reinterpret_cast<void *>(reinterpret_cast<char *>(addr) + 4096);

    void *new_addr = VirtualAlloc(addr, size, PAGE_READWRITE, MEM_COMMIT); 

    return new_addr;
}

void operator delete(void *ptr)
{
    char *tmp = reinterpret_cast<char *>(ptr) - 4096;

    VirtualFree(reinterpret_cast<void*>(tmp)); 
}

Что-то в этом роде, как я уже сказал - я не пытался компилировать этот код, так как у меня есть только виртуальная машина Windows, и я не могу загрузить компилятор и посмотреть, действительно ли он компилируется. [Я знаю, что этот принцип работает, так как мы делали нечто подобное там, где я работал несколько лет назад].

person Mats Petersson    schedule 30.01.2013
comment
Мне особенно нравится часть reinterpret_cats ;) - person nneonneo; 30.01.2013
comment
Ну, это C++, и вы не можете добавлять вещи к пустым указателям в соответствии со стандартом... ;) Эм, теперь я понимаю, что вы имеете в виду - уже поздно, я должен был лечь спать задолго до того, как начал это писать, В самом деле.... - person Mats Petersson; 30.01.2013
comment
я не понимаю; почему два VirtualAlloc? - person neuviemeporte; 30.01.2013
comment
Первый резервирует область (скажем) 12 КБ без доступа, используя MEM_RESERVE, а второй заполняет ее РЕАЛЬНОЙ памятью (MEM_COMMIT), чтобы вы действительно могли писать в нее. Это предполагает, что вы действительно хотите использовать память в середине. Основная идея заключается в том, что вы получаете по одной странице с каждой стороны фактической памяти. - person Mats Petersson; 30.01.2013
comment
Итак, мне все еще нужно использовать VirtualProtect для адреса, возвращенного из этой новой реализации? - person neuviemeporte; 30.01.2013
comment
Я так не думаю, но, как я уже сказал, я не пробовал этот код, поэтому он может не работать... Я бы начал с реализации двух вышеупомянутых функций как myalloc и myfree и посмотрел, сможете ли вы выделить/ свободная память, и что доступ за пределами страниц, к которым вы должны получить доступ, приводит к соответствующему сбою. - person Mats Petersson; 30.01.2013
comment
Итак, я предполагаю, что VirtualProtect используется для включения защиты страниц, которые были выделены без PAGE_NOACCESS? Кроме того, что вы, ребята, имели в виду по поводу reinterpret_cast? - person neuviemeporte; 31.01.2013

Вот для чего нужны страницы Gaurd (см. MSDN), они вызывают специальное исключение при первом доступе к странице, что позволяет вам делать больше, чем сбой при первом доступе к недопустимым страницам (и отлавливать плохие чтения/записи в отличие от указателей NULL). так далее).

person Necrolis    schedule 30.01.2013
comment
Из любопытства, вы когда-нибудь пробовали это и можете подтвердить, что это работает надежно? Кажется, я помню, что сама Windows использует защитные страницы для фиксации страниц для стека сверх минимального размера фиксации. Итак, запуск и обработка ошибок защитной страницы могут помешать нормальной работе? - person Damon; 30.01.2013
comment
@Damon: я сам не использовал защитные страницы просто потому, что они мне не нужны, но я видел несколько систем, которые используют это для обнаружения несанкционированного доступа для внешних программ. Кроме того, обработчик исключений вашей программы будет получать только исключения страницы защиты для памяти, которую вы охраняете (если по какой-то глупости Windows передает вам свои исключения защиты, вы можете легко отфильтровать это, проверив адрес) . - person Necrolis; 31.01.2013