45809 (665153), страница 3
Текст из файла (страница 3)
Поскольку функция UnhandledExceptionFilter может быть вызвана как из обработчика Runtime, так и системой, то изменение ее адреса в таблице импортируемых функций может не решить всей проблемы, поэтому для большей надежности нужно изменить код функции UnhandledExceptionFilter так, что бы сразу после ее вызова управление попадало к нам. К статье прилагается проект ehfilter, в котором реализован класс CatUnhandledExceptionFilter, устанавливающий обработчик необработанных исключений. Объявление класса CatUnhandledExceptionFilter приведено ниже:
class CatUnhandledExceptionFilter { private: friend LONG myUnhandledExceptionFilter( _EXCEPTION_POINTERS* ExceptionInfo ); UINT_PTR m_oldSystemUnhandledFilter; LONG UnhandledExceptionFilter(_EXCEPTION_POINTERS* ExceptionInfo); public: CatUnhandledExceptionFilter(); ~CatUnhandledExceptionFilter(); ool HookUpUnhandledFilter(); }; |
где
m_oldSystemUnhandledFilter – адрес оригинальной функции UnhandledExceptionFilter.
myUnhandledExceptionFilter – дружественная классу функция-переходник, ее назначение и код будут рассмотрены ниже.
UnhandledExceptionFilter – наш фильтр необработанных исключений
HookUpUnhandledFilter – функция установки нашего фильтра исключений.
Проект ehfilter является обычной DLL, которая должна быть загружена в адресное пространство приложения. Во время загрузки библиотеки в файле main.cpp создается глобальная переменная gFeedBackFilter типа CatUnhandledExceptionFilter. Во время создания этой переменной в конструкторе определяется адрес функции UnhandledExceptionFilter и запоминается в переменной m_oldSystemUnhandledFilter. Когда в библиотеку приходит сообщение DLL_PROCESS_ATTACH, вызывается функция HookUpUnhandledFilter, которая устанавливает наш фильтр необработанных исключений.
Код функции HookUpUnhandledFilter приведен ниже:
bool CatUnhandledExceptionFilter::HookUpUnhandledFilter() { if ( m_oldSystemUnhandledFilter == 0 ) return false; DWORD addr = m_oldSystemUnhandledFilter; DWORD old = 0; if ( TRUE == VirtualProtect((LPVOID)addr, 5, PAGE_READWRITE, &old) ) { unsigned char *p = (unsigned char*)addr; *p = 0xE9; UINT_PTR ehFilter = (UINT_PTR)myUnhandledExceptionFilter; addr += 5; ehFilter = ehFilter - addr; p++; DWORD *pp = (DWORD*)p; *pp = ehFilter; m_oldSystemUnhandledFilter += 5; VirtualProtect((LPVOID)addr, 5, old, &old); return true; } return false; } |
ПРИМЕЧАНИЕ Этот код будет работать только в семействе ОС Windows NT. Дело в том, что 2 верхних гигабайта, где размещены системные библиотеки, в Windows 9х недоступны на запись из пользовательского (user) режима. – прим.ред. |
Сначала функция проверяет, был ли найден системный обработчик UnhandledExceptionFilter. Если он не найден, функция возвращает false и завершает свою работу. Затем, поскольку необходимо писать в системную область памяти, изменяются атрибуты доступа к ней, что делает ее доступной для чтений/записи, и записывается инструкция безусловного перехода на функцию-переходник myUnhandledExceptionFilter. Функция-переходник имеет две цели – это вызвать фильтр и вернуть управление системной функции.
Приведенные в примере реализации функций HookUpUnhandledFilter и myUnhandledExceptionFilter являются сильно упрощенными и, конечно, неприменимы в реальной жизни. Мало того, попытка их применения может привести к печальным последствиям. Однако они достаточны для иллюстрации механизма подмены вызова системных функций. В реальности же необходимо дизассемблировать код функции UnhandledExceptionFilter, запоминать его и затем использовать при возврате управления. Но это в значительной мере усложнило бы код и могло скрыть основной его смысл, поэтому я решил оставить эту реализацию для демонстрации самого факта возможности подобных действий.
Сбор и сохранение информации о состоянии процесса
Как было упомянуто выше, функция UnhandledExceptionFilter принимает один аргумент, являющийся указателем на _EXCEPTION_POINTERS. В этой структуре сохранена информация о состоянии приложения после возникновения исключения, состояние регистров, информация об исключении. Но этого может быть мало для восстановления реальной картины произошедшего. Поэтому в своем обработчике я попытаюсь собрать ту информацию, которая поможет понять причину сбоя. Объем этой информации может зависеть от типа приложения и нельзя привести весь список необходимых параметров, способных помочь при анализе причины падения приложения. Программист должен сам решить, какой объем и тип информации ему требуется. Здесь я хочу лишь сказать, что информации, которую собирает о приложении библиотека dbghelp.dll, поставляемая с Windows, достаточно для большинства приложений. dbghelp.dll сохраняет следующее:
- информация о потоках;
- информация о загруженных модулях;
- информация об исключении;
- информация о системе;
- информация о памяти.
После сбора информации ее можно сохранить в файле на диске для последующего анализа и разбора.
Заключение
Операционная система Windows предоставляет каждому разработчику уникальную возможность обрабатывать исключения, возникающие в его программах, с помощью механизма Структурированной Обработки Исключений (SEH). Но приложения не полностью используют возможности, предоставляемые этим системным сервисом, и оставляют часть ошибок необработанными. В статье я привел пример того, как получить управление в случае фатальной ошибки приложения, и дал приложению получить управление вместо того, чтобы быть просто выгруженным. Этот метод может дать приложению последний шанс:
сохранить данные;
собрать информацию о состоянии на момент ошибки;
хотя бы извиниться....
Список литературы
Для подготовки данной работы были использованы материалы с сайта http://www.rsdn.ru/