Сообщение Re[9]: особенности структурной обработки исключений в Win64 от 04.12.2016 11:00
Изменено 04.12.2016 11:08 ononim
O>>Почему вас смущает что InternetGetConnectedState сохраняет внутри себя MMX регистры перед тем как вызывать какие то свои внутренние потроха (которые юзают SSE)?
кт>Потому что я знаю, что делают команды SSE2, для которых задействованы регистры XMM. И на кой ляд в подпрограмме возвращающей статус соединения в интернете, нужны команды (и регистры) обрабатывающие по два double за раз, не понимаю.
Да хоть и для банального memcpy. Откройте IDA и поройтесь по графу вызовов этой функции сами. Он между прочим очень развесистый.
O>>Действительно не понимаем. Я имел ввиду что есть два рода исключений. Первое — исключения уровня логики приложения. Второе — исключения уровня защиты ОС от неверного функционирования программы. Ваше underflow exception — относится к первым. Такие не могут привести к вызову обработчика исключений на невыровненном стеке, поскольку места из которых они могут вылететь известны компилятору и он генерит код таким образом, чтобы исключения логики генерились на корректном стеке.
кт>Компилятор никаких исключений не генерирует. По определению исключение может произойти в любом месте. Автор статьи справедливо показывает, что попытка по текущему содержимому стека определить подпрограмму — это непродуманная ерунда. Например, идет подготовка параметров к вызову API. Выделили место, достаем очередной параметр — ошибка, неинициализированный указатель — исключение доступа. Опа, а в стеке еще просто мусор (туда еще ничего не поместили). Мусор начинает "исследоваться" ОС.
Исключения логики могут возникнуть при throw и при арифметике. Исключение доступа — это исключение, после которого не живут, а выживают. Без гарантий. Кроме того, как я понял проблема возникла с кодом, который был сгенерен доморощенным PL/1 компилятором. Фиксить ее нужно было начинать с того, чтоб после каждой инструкции сгенеренного кода RSP оставался выровненным на 8 байт.
O>>Потому что есть соглашения об адекватном валидном коде, которые несколько выше чем "аппаратно". И согласно которым стек должен всегда оставаться выровненным на 8 (а при вызове публичной функции, в ее прологе — на 16). Если он таковым вдруг не стал — значит код не валидный. Время любителей писать выверты на асме прошло. Теперь балом правит кодогенератор.
кт>Выше "аппаратного" ничего нет. Требования кратности 16 дает не соглашение, а всего лишь используемые команды типа
"Выше" аппаратного имеется ввиду по соглашениям. Впрочем кулхакерам, привыкшим писать на асме пофиг на соглашения, они такого понятия не понимают..
кт>movdqa xmm0,xmmword ptr [rsp+20h] — обращение к XMM и памяти. Они дают аппаратное исключение из-за невыровненного доступа.
кт>Смешной штрих: чтобы внутри вызова стек был кратен 16, снаружи вызова он должен быть НЕ кратен 16.
кт>Но автор статьи не из-за этого пострадал. Он согласен со всеми требованиями вызова функций. Но ему-то хотелось в момент исключения пошагового режима обращаться со стеком как он привык в Win32. А исключение пошагового режима превращается в фатальное из-за того, что какой-то чудак на букву М не может поверить, что в этот момент (просто выполнение очередной команды) стек используется не так как при вызове API.
SetUnhandledExceptionFilter* не предназначена для реализации дебаггера, всем кто в теме давно известно что она ненадежна. Для дебаггера есть DebugAPI у коготорых нет таких проблем, т.к. они работают через LPC из соседнего процесса. Автор статьи пошел через реку вброд, хотя рямом был мост и жалуется**.
* Использование SetUnhandledExceptionFilter для дебаггера тем более странно, что оно не ловит уже пойманные исключения. Полагаю AddVectoredExceptionHandler для самодебаггера было бы более адекватным решением, хотя все еще не адекватным. Установка обработчика через RtlAddFunctionTable на всевозможные адреса чревата сломом других обработчиков исключений, написанных не автором: в системном коде и в стороннем коде, который оказался в его процессе по каким либо причинам (например windows hooks dll, COM серверы).
** Я сам ходил через реку вброд и реализовывал аналогичный механизм в одном исследовательском проекте для трейсинга софта, а в другом, неисследовательском — для реализации подкачки на уровне юзермода. Но в отличии от автора статьи я примерно понимал с чем имею дело и вместо того чтоб жаловаться на гнусный микрософт, не дающий кулхацкерам жизним, просто похукал ntdll!KiUserExceptionDispatcher.
кт>Потому что я знаю, что делают команды SSE2, для которых задействованы регистры XMM. И на кой ляд в подпрограмме возвращающей статус соединения в интернете, нужны команды (и регистры) обрабатывающие по два double за раз, не понимаю.
Да хоть и для банального memcpy. Откройте IDA и поройтесь по графу вызовов этой функции сами. Он между прочим очень развесистый.
O>>Действительно не понимаем. Я имел ввиду что есть два рода исключений. Первое — исключения уровня логики приложения. Второе — исключения уровня защиты ОС от неверного функционирования программы. Ваше underflow exception — относится к первым. Такие не могут привести к вызову обработчика исключений на невыровненном стеке, поскольку места из которых они могут вылететь известны компилятору и он генерит код таким образом, чтобы исключения логики генерились на корректном стеке.
кт>Компилятор никаких исключений не генерирует. По определению исключение может произойти в любом месте. Автор статьи справедливо показывает, что попытка по текущему содержимому стека определить подпрограмму — это непродуманная ерунда. Например, идет подготовка параметров к вызову API. Выделили место, достаем очередной параметр — ошибка, неинициализированный указатель — исключение доступа. Опа, а в стеке еще просто мусор (туда еще ничего не поместили). Мусор начинает "исследоваться" ОС.
Исключения логики могут возникнуть при throw и при арифметике. Исключение доступа — это исключение, после которого не живут, а выживают. Без гарантий. Кроме того, как я понял проблема возникла с кодом, который был сгенерен доморощенным PL/1 компилятором. Фиксить ее нужно было начинать с того, чтоб после каждой инструкции сгенеренного кода RSP оставался выровненным на 8 байт.
O>>Потому что есть соглашения об адекватном валидном коде, которые несколько выше чем "аппаратно". И согласно которым стек должен всегда оставаться выровненным на 8 (а при вызове публичной функции, в ее прологе — на 16). Если он таковым вдруг не стал — значит код не валидный. Время любителей писать выверты на асме прошло. Теперь балом правит кодогенератор.
кт>Выше "аппаратного" ничего нет. Требования кратности 16 дает не соглашение, а всего лишь используемые команды типа
"Выше" аппаратного имеется ввиду по соглашениям. Впрочем кулхакерам, привыкшим писать на асме пофиг на соглашения, они такого понятия не понимают..
кт>movdqa xmm0,xmmword ptr [rsp+20h] — обращение к XMM и памяти. Они дают аппаратное исключение из-за невыровненного доступа.
кт>Смешной штрих: чтобы внутри вызова стек был кратен 16, снаружи вызова он должен быть НЕ кратен 16.
кт>Но автор статьи не из-за этого пострадал. Он согласен со всеми требованиями вызова функций. Но ему-то хотелось в момент исключения пошагового режима обращаться со стеком как он привык в Win32. А исключение пошагового режима превращается в фатальное из-за того, что какой-то чудак на букву М не может поверить, что в этот момент (просто выполнение очередной команды) стек используется не так как при вызове API.
SetUnhandledExceptionFilter* не предназначена для реализации дебаггера, всем кто в теме давно известно что она ненадежна. Для дебаггера есть DebugAPI у коготорых нет таких проблем, т.к. они работают через LPC из соседнего процесса. Автор статьи пошел через реку вброд, хотя рямом был мост и жалуется**.
* Использование SetUnhandledExceptionFilter для дебаггера тем более странно, что оно не ловит уже пойманные исключения. Полагаю AddVectoredExceptionHandler для самодебаггера было бы более адекватным решением, хотя все еще не адекватным. Установка обработчика через RtlAddFunctionTable на всевозможные адреса чревата сломом других обработчиков исключений, написанных не автором: в системном коде и в стороннем коде, который оказался в его процессе по каким либо причинам (например windows hooks dll, COM серверы).
** Я сам ходил через реку вброд и реализовывал аналогичный механизм в одном исследовательском проекте для трейсинга софта, а в другом, неисследовательском — для реализации подкачки на уровне юзермода. Но в отличии от автора статьи я примерно понимал с чем имею дело и вместо того чтоб жаловаться на гнусный микрософт, не дающий кулхацкерам жизним, просто похукал ntdll!KiUserExceptionDispatcher.
O>>Почему вас смущает что InternetGetConnectedState сохраняет внутри себя MMX регистры перед тем как вызывать какие то свои внутренние потроха (которые юзают SSE)?
кт>Потому что я знаю, что делают команды SSE2, для которых задействованы регистры XMM. И на кой ляд в подпрограмме возвращающей статус соединения в интернете, нужны команды (и регистры) обрабатывающие по два double за раз, не понимаю.
Да хоть и для банального memcpy. Откройте IDA и поройтесь по графу вызовов этой функции сами. Он между прочим очень развесистый.
O>>Действительно не понимаем. Я имел ввиду что есть два рода исключений. Первое — исключения уровня логики приложения. Второе — исключения уровня защиты ОС от неверного функционирования программы. Ваше underflow exception — относится к первым. Такие не могут привести к вызову обработчика исключений на невыровненном стеке, поскольку места из которых они могут вылететь известны компилятору и он генерит код таким образом, чтобы исключения логики генерились на корректном стеке.
кт>Компилятор никаких исключений не генерирует. По определению исключение может произойти в любом месте. Автор статьи справедливо показывает, что попытка по текущему содержимому стека определить подпрограмму — это непродуманная ерунда. Например, идет подготовка параметров к вызову API. Выделили место, достаем очередной параметр — ошибка, неинициализированный указатель — исключение доступа. Опа, а в стеке еще просто мусор (туда еще ничего не поместили). Мусор начинает "исследоваться" ОС.
Исключения логики могут возникнуть при throw и при арифметике. Исключение доступа — это исключение, после которого не живут, а выживают. Без гарантий. Кроме того, как я понял проблема возникла с кодом, который был сгенерен доморощенным PL/1 компилятором. Фиксить ее нужно было начинать с того, чтоб после каждой инструкции сгенеренного кода RSP оставался выровненным на 8 байт.
O>>Потому что есть соглашения об адекватном валидном коде, которые несколько выше чем "аппаратно". И согласно которым стек должен всегда оставаться выровненным на 8 (а при вызове публичной функции, в ее прологе — на 16). Если он таковым вдруг не стал — значит код не валидный. Время любителей писать выверты на асме прошло. Теперь балом правит кодогенератор.
кт>Выше "аппаратного" ничего нет. Требования кратности 16 дает не соглашение, а всего лишь используемые команды типа
"Выше" аппаратного имеется ввиду по соглашениям. Впрочем кулхакерам, привыкшим писать на асме пофиг на соглашения, они такого понятия не понимают..
кт>movdqa xmm0,xmmword ptr [rsp+20h] — обращение к XMM и памяти. Они дают аппаратное исключение из-за невыровненного доступа.
кт>Смешной штрих: чтобы внутри вызова стек был кратен 16, снаружи вызова он должен быть НЕ кратен 16.
кт>Но автор статьи не из-за этого пострадал. Он согласен со всеми требованиями вызова функций. Но ему-то хотелось в момент исключения пошагового режима обращаться со стеком как он привык в Win32. А исключение пошагового режима превращается в фатальное из-за того, что какой-то чудак на букву М не может поверить, что в этот момент (просто выполнение очередной команды) стек используется не так как при вызове API.
SetUnhandledExceptionFilter* не предназначена для реализации дебаггера, всем кто в теме давно известно что она ненадежна. Для дебаггера есть DebugAPI у коготорых нет таких проблем, т.к. они работают через LPC из соседнего процесса. Автор статьи пошел через реку вброд, хотя рямом был мост и жалуется**.
* Использование SetUnhandledExceptionFilter для дебаггера тем более странно, что оно не ловит уже пойманные исключения. Полагаю AddVectoredExceptionHandler для самодебаггера было бы более адекватным решением, хотя все еще не адекватным. Установка обработчика через RtlAddFunctionTable на всевозможные адреса чревата сломом других обработчиков исключений, написанных не автором: в системном коде и в стороннем коде, который оказался в его процессе по каким либо причинам (например windows hooks dll, COM серверы). Кроме того однажды RtlAddFunctionTable может отказаться регистрировать такую функцию, т.к. ее диапазон перекрывается с уже зарегистрированными и будет совершенно права. Аккуратнее надо быть.
** Я сам ходил через реку вброд и реализовывал аналогичный механизм в одном исследовательском проекте для трейсинга софта, а в другом, неисследовательском — для реализации подкачки на уровне юзермода. Но в отличии от автора статьи я примерно понимал с чем имею дело и вместо того чтоб жаловаться на гнусный микрософт, не дающий кулхацкерам жизним, просто похукал ntdll!KiUserExceptionDispatcher.
кт>Потому что я знаю, что делают команды SSE2, для которых задействованы регистры XMM. И на кой ляд в подпрограмме возвращающей статус соединения в интернете, нужны команды (и регистры) обрабатывающие по два double за раз, не понимаю.
Да хоть и для банального memcpy. Откройте IDA и поройтесь по графу вызовов этой функции сами. Он между прочим очень развесистый.
O>>Действительно не понимаем. Я имел ввиду что есть два рода исключений. Первое — исключения уровня логики приложения. Второе — исключения уровня защиты ОС от неверного функционирования программы. Ваше underflow exception — относится к первым. Такие не могут привести к вызову обработчика исключений на невыровненном стеке, поскольку места из которых они могут вылететь известны компилятору и он генерит код таким образом, чтобы исключения логики генерились на корректном стеке.
кт>Компилятор никаких исключений не генерирует. По определению исключение может произойти в любом месте. Автор статьи справедливо показывает, что попытка по текущему содержимому стека определить подпрограмму — это непродуманная ерунда. Например, идет подготовка параметров к вызову API. Выделили место, достаем очередной параметр — ошибка, неинициализированный указатель — исключение доступа. Опа, а в стеке еще просто мусор (туда еще ничего не поместили). Мусор начинает "исследоваться" ОС.
Исключения логики могут возникнуть при throw и при арифметике. Исключение доступа — это исключение, после которого не живут, а выживают. Без гарантий. Кроме того, как я понял проблема возникла с кодом, который был сгенерен доморощенным PL/1 компилятором. Фиксить ее нужно было начинать с того, чтоб после каждой инструкции сгенеренного кода RSP оставался выровненным на 8 байт.
O>>Потому что есть соглашения об адекватном валидном коде, которые несколько выше чем "аппаратно". И согласно которым стек должен всегда оставаться выровненным на 8 (а при вызове публичной функции, в ее прологе — на 16). Если он таковым вдруг не стал — значит код не валидный. Время любителей писать выверты на асме прошло. Теперь балом правит кодогенератор.
кт>Выше "аппаратного" ничего нет. Требования кратности 16 дает не соглашение, а всего лишь используемые команды типа
"Выше" аппаратного имеется ввиду по соглашениям. Впрочем кулхакерам, привыкшим писать на асме пофиг на соглашения, они такого понятия не понимают..
кт>movdqa xmm0,xmmword ptr [rsp+20h] — обращение к XMM и памяти. Они дают аппаратное исключение из-за невыровненного доступа.
кт>Смешной штрих: чтобы внутри вызова стек был кратен 16, снаружи вызова он должен быть НЕ кратен 16.
кт>Но автор статьи не из-за этого пострадал. Он согласен со всеми требованиями вызова функций. Но ему-то хотелось в момент исключения пошагового режима обращаться со стеком как он привык в Win32. А исключение пошагового режима превращается в фатальное из-за того, что какой-то чудак на букву М не может поверить, что в этот момент (просто выполнение очередной команды) стек используется не так как при вызове API.
SetUnhandledExceptionFilter* не предназначена для реализации дебаггера, всем кто в теме давно известно что она ненадежна. Для дебаггера есть DebugAPI у коготорых нет таких проблем, т.к. они работают через LPC из соседнего процесса. Автор статьи пошел через реку вброд, хотя рямом был мост и жалуется**.
* Использование SetUnhandledExceptionFilter для дебаггера тем более странно, что оно не ловит уже пойманные исключения. Полагаю AddVectoredExceptionHandler для самодебаггера было бы более адекватным решением, хотя все еще не адекватным. Установка обработчика через RtlAddFunctionTable на всевозможные адреса чревата сломом других обработчиков исключений, написанных не автором: в системном коде и в стороннем коде, который оказался в его процессе по каким либо причинам (например windows hooks dll, COM серверы). Кроме того однажды RtlAddFunctionTable может отказаться регистрировать такую функцию, т.к. ее диапазон перекрывается с уже зарегистрированными и будет совершенно права. Аккуратнее надо быть.
** Я сам ходил через реку вброд и реализовывал аналогичный механизм в одном исследовательском проекте для трейсинга софта, а в другом, неисследовательском — для реализации подкачки на уровне юзермода. Но в отличии от автора статьи я примерно понимал с чем имею дело и вместо того чтоб жаловаться на гнусный микрософт, не дающий кулхацкерам жизним, просто похукал ntdll!KiUserExceptionDispatcher.