Закрытый раздел размером 64 Кб (только Windows 2000)
Этот раздел заблокирован, и любая попытка обращения к нему приводит к нарушению доступа Microsoft резервирует этот раздел специально, чтобы упростить внутреннюю реализацию операционной системы. Вспомните, когда Вы передаете Windows-функции адрес блока памяти и его размер, то она (функция), прежде чем приступить к работе, проверяет, действителен ли данный блок. Допустим, Вы написали код:
В случае функций типа WriteProcessMemory область памяти, в которую предполагается запись, проверяется кодом, работающим в режиме ядра, — только он имеет право обращаться к памяти, выделяемой под код и данные режима ядра (в 32-разрядных системах — по адресам выше 0x80000000) Если по этому адресу есть память, вызов WriteProcessMemory, показанный выше, благополучно запишет данные в ту область памяти, которая, по идее, доступна только коду, работающему в режиме ядра. Чтобы предотвратить это и в то же время ускорить проверку таких областей памяти, Microsoft предпочла заблокировать данный раздел, и поэтому любая попытка чтения или записи в нем всегда вызывает нарушение доступа
Никак не могу понят, что имелось ввиду? Как этот кусок в 64к способствует тому, что я не могу писать в память ядра? Если имеется ввиду, что этот кусок является гранью между моей памятью и памятью ядра, и служит guard'ом для моих увеличивающихся указателей, то как борятся с тем, что я просто буду писать в память, находящуюся за этим куском?
Здравствуйте, Keith, Вы писали:
K> Никак не могу понят, что имелось ввиду? Как этот кусок в 64к способствует тому, что я не могу писать в память ядра? Если имеется ввиду, что этот кусок является гранью между моей памятью и памятью ядра, и служит guard'ом для моих увеличивающихся указателей, то как борятся с тем, что я просто буду писать в память, находящуюся за этим куском?
Просто это защита от дурака, а не от злоумышленника. Она предназначена для защиты от случайной записи: когда вы, записывая не туда куда надо, дойдёте до этого блока, система вас остановит, не дожидаясь, пока вы начнёте затирать память ядра. Только-то и всего.
MS>Просто это защита от дурака, а не от злоумышленника. Она предназначена для защиты от случайной записи: когда вы, записывая не туда куда надо, дойдёте до этого блока, система вас остановит, не дожидаясь, пока вы начнёте затирать память ядра. Только-то и всего.
Что-то дурак очень односторонний. Кроме как превышать границу своих данных больше и не умеет ничего делать.
А если случайно сгенерится неправильный указатель в область лежащую за пределами этого куска, то что будет? Какая принципиальная разница между этим куском в 64k(кстати, нафига такой большой?) и памятью, лежащей за ним?
K>>А если случайно сгенерится неправильный указатель V>Как он может случайно сгененироваться?
Очень легко. Есть такие указатели, которые называют дикими. Или, например, указателю присваевается значение, которое не является указателем.
V>Неправильная обращение обычно происходит при выходе за границы массива, и вот тут блок-страж как раз кстати.
А если необычно?
V>>Как он может случайно сгененироваться?
SH>Например, обратился за пределы массива и изменил при этом следующую переменную (всё происходит в стеке). А в ней был указатель..
Похоже, на самом деле Рихтер лукавит и говорит совсем не о том, для чего на самом деле эти предохранительные блоки. Очевидно, любая попытка записать в адреса выше второго гигабайта из user-mode невозможна и приложение, которое осуществит такую попытку, вызовет исключения доступа. Другое дело — адреса ниже второго гигабайта. Тут в случае наличия страницы запись вполне возможна. Однако хотелось бы уже на этапе отладки приложения отследить "стремные" действия, связанные с наиболее частыми ошибками программирования (например, с нулевыми или отрицательными указателями). Для этого как раз и предназначены эти блоки. Согласитесь — гораздо приятнее увидеть ацесс виолятион на этапе отладки, нежели получить трудновоспроизводимый баг у пользователя.
WriteProcessMemory — совершенно другая песня, и, очевидно, guard pages в данном случае только малая часть того, что проверяет система.
Hello Andrew S, you wrote:
> на самом деле эти предохранительные блоки. Очевидно, любая попытка > записать в адреса выше второго гигабайта из user-mode невозможна
Возмножна, если АП разделено в отношении 3/1, а не 2/2.
Для этого есть соответсвующий ключ в boot.ini. Также это есть на каком-то
Windows Server
>> на самом деле эти предохранительные блоки. Очевидно, любая попытка >> записать в адреса выше второго гигабайта из user-mode невозможна
SA>Возмножна, если АП разделено в отношении 3/1, а не 2/2. SA>Для этого есть соответсвующий ключ в boot.ini. Также это есть на каком-то SA>Windows Server
Вот только не надо передергивать
Это все понятно, что соотношение адресных пространств user\kernel может быть разным. Для этого, помимо ключей в boot.ini, есть еще и опция в заголовке PE ( /LARGEADDRESSAWARE). Мы ведь говорим про совсем другие вещи, верно?
Здравствуйте, Keith, Вы писали:
K> Никак не могу понят, что имелось ввиду? Как этот кусок в 64к способствует тому, что я не могу писать в память ядра? Если имеется ввиду, что этот кусок является гранью между моей памятью и памятью ядра, и служит guard'ом для моих увеличивающихся указателей, то как борятся с тем, что я просто буду писать в память, находящуюся за этим куском?
этот регион предотвращает передачу потоками буферов через границу между um\km (user\kernel mode)/ Стартовый адрес в MmUserProbeAddress (0х7FFF000 на х86 с 2 Гб user space и 0xBFFF000 с 3Гб — SergH, привет )
Цифра растет ИМХО из того факта, что гранулярность выделения памяти (see GetSystemInfo) 64Кб — так было задумано для того чтобы быть готовым к поддержке будущих CPU с размером страницы 64Кб. Кстати выравнивание таких регионов также происходит в соотв с гранулярностью выделения памяти, а вот размер адресного пространства кратен уже странице.
Подчеркну, что здесь речь о резервировании региона адресного пространства и это не относится к режиму ядра — там все нормально, гранулярность 1 страница.
... << Rsdn@Home 1.1.4 beta 1 >>
Valery A. Boronin, RSDN Team, linkedin.com\in\boronin
R&D Mgmt & Security. AppSec & SDL. Data Protection and Systems Programming. FDE, DLP, Incident Management. Windows Filesystems and Drivers.
Здравствуйте, Andrew S, Вы писали:
AS>Похоже, на самом деле Рихтер лукавит и говорит совсем не о том, для чего на самом деле эти предохранительные блоки. Очевидно, любая попытка записать в адреса выше второго гигабайта из user-mode невозможна и приложение, которое осуществит такую попытку, вызовет исключения доступа. Другое дело — адреса ниже второго гигабайта. Тут в случае наличия страницы запись вполне возможна. Однако хотелось бы уже на этапе отладки приложения отследить "стремные" действия, связанные с наиболее частыми ошибками программирования (например, с нулевыми или отрицательными указателями). Для этого как раз и предназначены эти блоки. Согласитесь — гораздо приятнее увидеть ацесс виолятион на этапе отладки, нежели получить трудновоспроизводимый баг у пользователя.
И всё-таки про верхний блок я не въехал Без этого блока память была чётко разделена на две части — куда можно обращаться и куда нельзя. И это стабильно, как во время отладки, так и у заказчика. С этим блоком, в общем-то таже фигня — есть две области, в обдну лазать можно, в другую нельзя. И тоже как при отладке, так и у заказчика. Причём конфигурация областей не поменялась — снизу можно, сверху нельзя (введение нижнего блока меняет конфигурацию, но оно мне и понятно), поменялся только адрес границы. И где здесь упрощение отладки?
SH>И всё-таки про верхний блок я не въехал Без этого блока память была чётко разделена на две части — куда можно обращаться и куда нельзя. И это стабильно, как во время отладки, так и у заказчика. С этим блоком, в общем-то таже фигня — есть две области, в обдну лазать можно, в другую нельзя. И тоже как при отладке, так и у заказчика. Причём конфигурация областей не поменялась — снизу можно, сверху нельзя (введение нижнего блока меняет конфигурацию, но оно мне и понятно), поменялся только адрес границы. И где здесь упрощение отладки?
Верхний блок отлавливает обращение к диапазону 7FFE0000 — 7FFFFFFF в стандартной конфигурации. Вероятно, такие адреса тоже часто бывают ошибочными, как и нулевые (пример я с ходу придумать не могу, но выглядят они подозрительно ). Ну, или в майкрософт так решили По крайней мере, во всей доступной литературе они описываются как блоки, позволяющие отлавливать часто встречающиеся ошибки программирования. Насчет того, что Рихтер говорит про упрощение проверки, например, в WriteProcessMemory — это, по моему мнению, глупость — нам и так известен адрес, куда мы пишем и размер блока, поэтому проверить простым условием это гораздо проще. А все остальное попадает под определение отлова ошибок программинга.
Здравствуйте, Valerio, Вы писали:
V>этот регион предотвращает передачу потоками буферов через границу между um\km (user\kernel mode) V>Стартовый адрес в MmUserProbeAddress (0х7FFF000 на х86 с 2 Гб user space и 0xBFFF000 с 3Гб — SergH, привет )
А теперь ешё раз для тех кто в танке? Буферы — это те, что в запросах ввода-вывода? Они же вроде выделяются пользовательским приложением. Типа я вызываю ReadFile а перед этим выделяюдля него буфер.. В общем объясни пожалуйста ещё раз.
И почемк мне привет, ктстати?
V>Цифра растет ИМХО из того факта, что гранулярность выделения памяти (see GetSystemInfo) 64Кб — так было задумано для того чтобы быть готовым к поддержке будущих CPU с размером страницы 64Кб. Кстати выравнивание таких регионов также происходит в соотв с гранулярностью выделения памяти, а вот размер адресного пространства кратен уже странице.
V>>этот регион предотвращает передачу потоками буферов через границу между um\km (user\kernel mode) V>>Стартовый адрес в MmUserProbeAddress (0х7FFF000 на х86 с 2 Гб user space и 0xBFFF000 с 3Гб — SergH, привет )
SH>А теперь ешё раз для тех кто в танке? Буферы — это те, что в запросах ввода-вывода? Они же вроде выделяются пользовательским приложением. Типа я вызываю ReadFile а перед этим выделяюдля него буфер.. В общем объясни пожалуйста ещё раз.
никто не мешает ошибочно\преднамеренно передать плохой адрес\размер буфера
Если запись (WriteProcessMemory тот же, на который у Рихтера ссылаются) пойдет из um в ядро непрерывно, она прервется при доступе к этой страничке.
MmUserProbeAddress используется еще вот для чего часто:
драйверу (через METHOD_NEITHER например) напрямую отдаются user mode buffers которые будут отображаться в ядро. Соотв такие вещи там перед использованием надо оборачивать в try/except блоке проверками на ProbeForRead/ProbeForWrite (что и делают соотв Zw* ф-ии для usermode буферов)
Соотв проверки на валидность элементарные:
cmp esi, _MmUserProbeAddress ; check if user address
jae notuseraddress ; if ae, then not user address
Короче опираясь на эту переменную всегда можно проверить валидность адреса для um\km и сопоставить с режимом доступа.
SH>И почемк мне привет, ктстати?
Сергей, прошу прощения, привет предназначался Славе Антонову получается ( это из-за Re[6]: Не пойму.
Valery A. Boronin, RSDN Team, linkedin.com\in\boronin
R&D Mgmt & Security. AppSec & SDL. Data Protection and Systems Programming. FDE, DLP, Incident Management. Windows Filesystems and Drivers.
Здравствуйте, Valerio, Вы писали:
V>никто не мешает ошибочно\преднамеренно передать плохой адрес\размер буфера
Угу, значит буферы эти.
V>MmUserProbeAddress используется еще вот для чего часто:
...
Понятно. Непонятно, почему MmUserProbeAddress нельзя было сделать 0x80000000 (ну или 0x7FFFFFFF). На первый взгляд, получится тоже самое. В чём разница?
V>Сергей, прошу прощения, привет предназначался Славе Антонову получается ( это из-за Re[6]: Не пойму.
V>>никто не мешает ошибочно\преднамеренно передать плохой адрес\размер буфера
SH>Угу, значит буферы эти.
V>>MmUserProbeAddress используется еще вот для чего часто:
SH>...
SH>Понятно. Непонятно, почему MmUserProbeAddress нельзя было сделать 0x80000000 (ну или 0x7FFFFFFF). На первый взгляд, получится тоже самое. В чём разница?
Вероятно, все фишка в том, как ProbeForXXX тестирует данные адреса. Насколько я помню, она пробует читать\писать по одному дворду из каждой страницы указанного диапазона. Т.о. MmUserProbeAddress отсекает явные ошибки (грубая проверка) + отсекая при этом попытки залезть в адреса кернела, которые могут в отличие от гвард пространства, быть вполне доступными, а затем при помощи ProbeForXXX кернел окончательно убеждается в валидности предоставленного буфера.
Hello Andrew S, you wrote:
>>> на самом деле эти предохранительные блоки. Очевидно, любая попытка >>> записать в адреса выше второго гигабайта из user-mode невозможна > > SA>Возмножна, если АП разделено в отношении 3/1, а не 2/2. > SA>Для этого есть соответсвующий ключ в boot.ini. Также это есть на > каком-то > SA>Windows Server > > Вот только не надо передергивать
Я не передергиваю, а опровергаю ваше (и те только) утверждение "любая
попытка записать в адреса выше второго гигабайта из user-mode невозможна"
Здравствуйте, Andrew S, Вы писали:
AS>Вероятно, все фишка в том, как ProbeForXXX тестирует данные адреса. Насколько я помню, она пробует читать\писать по одному дворду из каждой страницы указанного диапазона. Т.о. MmUserProbeAddress отсекает явные ошибки (грубая проверка) + отсекая при этом попытки залезть в адреса кернела, которые могут в отличие от гвард пространства, быть вполне доступными, а затем при помощи ProbeForXXX кернел окончательно убеждается в валидности предоставленного буфера.
Не знаешь — так честно и скажи За ответ опять не катит. Потому как абсолютно непонятно, из-за чего вдруг "грубые ошибки" начинаются не там, где начинается область ядра, а на 64 кб раньше.
Здравствуйте, SergH, Вы писали:
SH>Здравствуйте, Andrew S, Вы писали:
AS>>Вероятно, все фишка в том, как ProbeForXXX тестирует данные адреса. Насколько я помню, она пробует читать\писать по одному дворду из каждой страницы указанного диапазона. Т.о. MmUserProbeAddress отсекает явные ошибки (грубая проверка) + отсекая при этом попытки залезть в адреса кернела, которые могут в отличие от гвард пространства, быть вполне доступными, а затем при помощи ProbeForXXX кернел окончательно убеждается в валидности предоставленного буфера.
SH>Не знаешь — так честно и скажи За ответ опять не катит.
??? Валерий же уже ответил, я всего лишь сказал другими словами то, о чем он писал.
SH>Потому как абсолютно непонятно, из-за чего вдруг "грубые ошибки" начинаются не там, где начинается область ядра, а на 64 кб раньше.
Не грубые ошибки, а грубая проверка. Разница очевидна?
Гвард страница используется в качестве своеобразного барьера между адресным пространством um\km. Как я понимаю, Ваше недоумение вызвано тем, почему MmUserProbeAddress == km_address_begin — 64кб, а не km_address_begin? Это вполне понятно — потому что адреса, которые выше km_address_begin — 64кб уже по определению не будут валидными для um. Все остальное, что ниже — обязано проверяться при помощи ProbeForXXX, и в случае, если часть буфера попадет на указанные 64 кб, то сгенерится исключения недоступной страницы, т.о. получается, что проверка действительно упрощается. 64кб же, как сказал Валерий:
V>Цифра растет ИМХО из того факта, что гранулярность выделения памяти (see GetSystemInfo) 64Кб — так было задумано для того чтобы быть готовым к поддержке будущих CPU с размером страницы 64Кб. Кстати выравнивание таких регионов также происходит в соотв с гранулярностью выделения памяти, а вот размер адресного пространства кратен уже странице.
SH>>Потому как абсолютно непонятно, из-за чего вдруг "грубые ошибки" начинаются не там, где начинается область ядра, а на 64 кб раньше. AS>Не грубые ошибки, а грубая проверка. Разница очевидна?
Я особенно еще не разобрался, но так понимаю, что существует два типа проверки на валидность адреса:
1) ошибка вызванная при попадании адреса в блок 64k;
2) ошибка вызванная при попадании адреса в облать ядра.
На сколько я понимаю, первая проверка обходится "дешевле", чем вторая, но я сильно сомневаюсь, что на столько дешевле, чтобы вообще заморачиватся на этот блок в 64k. Короче, есть ли убедительные доказательства тому, что этот блок дает какой-то ощутимый выигрышь?
Кстати, а действия Windows'а при этих двух ошибках разные?
K> Я особенно еще не разобрался, но так понимаю, что существует два типа проверки на валидность адреса: K>1) ошибка вызванная при попадании адреса в блок 64k; K>2) ошибка вызванная при попадании адреса в облать ядра. K> На сколько я понимаю, первая проверка обходится "дешевле", чем вторая, но я сильно сомневаюсь, что на столько дешевле, чтобы вообще заморачиватся на этот блок в 64k. Короче, есть ли убедительные доказательства тому, что этот блок дает какой-то ощутимый выигрышь? K> Кстати, а действия Windows'а при этих двух ошибках разные?
Нет, не так. В данном случае проверка на валидность адреса состоит из 2-х этапов (это все в кернеле)
1. Проверка на то, чтобы стартовый адрес блока был ниже MmUserProbeAddress. Этим мы отсекаем явно невалидные адреса.
2. Проверка доступности страниц предоставленного буфера — при помощи ProbeForXXX.
Очевидно, что случай (1) оставляет за собой вариант, когда начало блока находится в допустимых пределах, а остальное — выше MmUserProbeAddress. Поскольку проверка производится из кернела, то в случае, если бы перед началом km adress space не было бы предохранительного блока из хотя бы одной страницы, то ProbeForXXX могла бы и не вызвать исключения недоступности страницы при проверке (2), поскольку страницы, расположенные выше km adress space для нее вполне доступны (напомню, это все происходит в km) — про это и писал Рихтер. Одну из причин, почему размер предохранительного блока выбран больше, чем одна страница IA32 — Валерий уже сказал. Конечно, можно было бы поступить и по-другому — проверять попадание блока в нужные границы, но затем все равно вызывалось бы ProbeForXXX для проверки доступности предоставленной памяти. А так — мы экономим одно сравнение (правда, теряем 64 кб адресного пространства процесса, но это не критично, как мне кажется — физически эти страницы же не существуют).
Т.о. получается, что действительно, одна из задач наличия верхнего предохранительного блока — упрощение жизни разработчикам драйверов и взаимодействия um-km. (Если что не так — Валерий поправит )