Раздел: Документация
0 ... 83 84 85 86 87 88 89 ... 102 266 Приложение А. Практические советы по восстановлению системы в боевых успов ПРИНУДИТЕЛЬНЫЙ ВЫХОД ИЗ ФУНКЦИИ Запускаем тестовую программу, набиваем в одном или нескольких окнах кой-нпбудь текст, затем в меню Help выбираем пункт About TestCEdit и в иоявив" шемся диалоговом окне щелкаем на кнопке make error. Опля! Программа вы брасывает критическую ошибку, и, если мы нажмем на ОК, все несохранещ,, данные необратимо погибнут, что никак не входит в наши планы. Однако нрн наличии предварительно установленного отладчика мы еще можем кое-что предпринять. Пусть для определенности это будет Microsoft Visual Studio Debugger. Нажимаем Отмена, и отладчик немедленно дисассемблирует функцию, возбудившую исключение (листинг А.5). Листинг А.5. Отладчик Microsoft Visual Studio Debugger дизассемблировал функцию, возбудившую исключение
Проанализировав причину возникновения исключения (функции передан указатель на невыделенную память), мы приходим к выводу, что заставить функцию продолжить свою работу невозможно, поскольку структура передаваемых данных нам неизвестна. Приходится прибегать к принудительному возврату в материнскую функцию, не забыв при этом установить флаг ошибки, сигнализируя программе, что текущая операция не была выполнена. К сожалению, никаких общепринятых флагов ошибок не существует, и различные функции используют различные соглашения. Чтобы выяснить, как обстоят дела в данном конкретном случае, мы должны дизассемблировать материнскую функш110 и определить, какой именно код ошибки она ожидает. Переместив курсор в окно дампа, набьем в строке адреса название регистра ука зателя вершины стека — ESP и нажмем на Enter. Содержимое стека тут же пред станет перед нашими глазами (листинг А.6). Листинг А.6. Поиск адреса возврата из текущей функции (выделен полужирным) 0012F488 0012FA64 0012FA64 004012FF 0012-494 00С00000 00000064 00403458 0012F4AO FFFFFFFF 0012F4C4 6С291СЕА 0012F4AC 00000019 00000000 6C32FAF0 0012F4B8 0012F4C0 0012FA64 01100059 Протри Dl2F4C4 006403С2 002F5788 OOOCOOOG p012F4DO 006403C1 77E16383 004C1E20 Первые два двойных слова соответствуют машинным командам POP EDI/POP ESI не представляют для нас совершенно никакого интереса. А вот следующее двойное слово содержит адрес выхода в материнскую процедуру (в листинге д 6 оно выделено полужирным шрифтом). Как раз его-то нам и надо! Нажимаем Ctrl+D и затем 0x4012FF, отладчик послушно отображает следующий дизассемблерный текст (листинг А.7). Листинг А.7. Дизассемблерный листинг материнской функции C04C12FA 004012FF С0401302 00401304 00401305 00401309 004О13ОЕ 004O13OF 00401315 00401318 0040131С 0040131F 00401320 00401321 00401323 00401328 00401329 0040132С 0040132С 0040132D 004O132D 0040132F 00401331 00401336 00401338 °M0133D 0040i33E 0040134! Сотрите: если регистр ЕАХ равен FFh, то материнская функция передает управ-Ие на ветку 40132Dh и спустя несколько машинных команд завершает свою Hi °Т пеРедавая бразды правления функции более высокого уровня. Нанро-Но °СЛИ ЕАХ = frfr1то СП<> 311аченпе передается функции 4033B4h. Следователь-• Мы можем предположить, что FFh - это флаг ошибки и есть. Возвращаемся п°Допытную функцию, нажав Ctrl+G и EIP, переходим в окно Registers и меня-.значение ЕАХ на FFh. р0- )ь необходимо найти подходящую точку возврата из функции. Просто пе-11711 к машинной команде RET нельзя, поскольку перед выходом из функции
268 Приложение А. Практические советы по восстановлению системы в боевых vcnn следует в обязательном порядке сбалансировать стек, иначе нас выбросщ-известно куда п программа обрушится окончательно. В общем случае число PUSH-команд должно в точности соответствовать ]Со личеству POP (также учитывайте, что PUSH DWORD X эквивалентен SUB ESP 4 a POP DWORD X — ADD ESP. 4). Проанализировав диэассемблерный листинг функции,мы приходим к выводу, что для достижения гармонии добра и зламыдо:15к ны «стащить» с вершины стека два двойных слова, соответствующие машин ным командам 40135C:PUSH ESI h401361:PUSH EDI. Это достигается передачей управления по адресу 40136Dh, где живут два «добродушных» РОРа, приводящие стек в равновесное состояние. Подводим сюда курсор и уверенным щелчком правой клавиши мыши вызываем контекстное меню, среди пунктов которого выбираем Set Next Statement. Как вариант, можно перейти в окно регистров и изменить значение EIP с 401362h на 40136Dh. Нажатием F5 мы заставляем процессор продолжить выполнение программы и. 0.чудо! Она действительно продолжает свою работу (незлобное ругательство на ошибку последней операции — не в счет!). Несохраненные данные спасены! РАСКРУТКА СТЕКА Далеко не во всех случаях принудительный выход из функции оказывается возможным. Ряд критических сбоев затрагивает не одну, а сразу несколько вложенных функций, и тогда для реанимации программы мы должны совершить глубокий откат назад, продолжив выполнение программы с того места, где бы ее работоспособности ничто не угрожало. Точная глубина отката подбирается экспериментально и обычно составляет три-нять ступеней. Имейте в виду, что если вложенные функции модифицируют глобальные данные (например, данные кучи), то попытка отката может привести к полному краху отлаживаемой программы, поэтому требуемую глубину отката желательно угадать с первого раза, придерживаясь правила: лучше перебрать, чем недобрать. С другой стороны, чрезмерно глубокий откат ведет к потере всех несохранеиных данных... Процедура отката состоит из трех шагов: 1.Построения дерева вызовов. 2.Определения координат стекового фрейма для каждого из них. 3.Восстановления регистрового контекста материнской функции. Хороший отладчик все это сделает за пас, и вам останется лишь записать в ре гистры EIP и ESP соответствующие значения. К сожалению, отладчик N 1С soft Visual Studio Debugger к хорошим не относится. Он довольно посредстве трассирует стек, пропуская FPO-функции (Frame Point Omission — Р-*111,, с оптимизированным фреймом), и не сообщает координат стекового (РряТ1, благодаря» чему самую трудоемкую часть работы нам приходится выи0- « самостоятельно Впрочем, даже такой стек вызовов все же лучше, чем совсем ничего. Ре вая его вручную, мы будем отталкиваться от того, что координаты ФРеиМЗйМос ственным образом определяются по адресу возврата. Допустим, содер окна Call Stack выглядит так (листинг А.8). 0 ... 83 84 85 86 87 88 89 ... 102
|