Раздел: Документация
0 ... 85 86 87 88 89 90 91 ... 102 находим пункт Set Next Statement. Нажимаем F5 для возобновления работы про. граммы, и... она действительно возобновляет свою работу! А теперь откомпилируем наш проект в чистовом варианте (то есть без отладочном информации) и попробуем реанимировать приложение в голом машинном коде. Пользуясь тем обстоятельством, что Windows — это действительно многозадачная среда, в которой крушение одного процесса не мешает работе всех остальных, запустим свой любимый дизассемблер (например, IDA PRO) и проанализируем таблицу импорта отлаживаемой программы (вообше-то это может сделать и бесплатно распространяемый dumpbin, но его отчет не так нагляден). Целью нашего поиска будут функции Trans!ateMessage/DispatchMessage и перекрестные ссылки, ведущие к циклу выборки сообщений (листинг А.13). Листинг А.13. Поиск функций TranslateMessage/DispatchMessage в таблице импорта .idata:004040E0 : BOOL stdcel1 IranslateMessageCconst MSG *lpMsg) extrn Transatew.essage:dword : DATA XRr.F: WinKein@16+71*r : WinMainP16*80*r LONG stdcall D:spatchMessageA(const MSG *lpMsg) extrn DispatchMessageA:dwcrd DATA XREF: WinMain@16+94*r .idata:004C40EC ,idata:004040EC .idata:004040E4 .idata:004040E4 .i data-.00404018 С функцией DispatchMessage связана всего лишь одна перекрестная ссылка, со всей очевидностью ведущая к искомому циклу обработки сообщений, дизас-семблерный код которого выглядит так (листинг А.14). Листинг А.14. Дизассемблерный листинг функции обработки сообщений .text:004С1050 .text:00401050 .text:00401050 .text:004С1056 .text:00401058 .text:004C105A .text:0040105A .text:004C105A .text .-0040105A .text:0040105A .text:0040105A .text:0040105A .text:0040105E .text:00401060 .text:00401061 .text:00401063 .text:00401063 .text:00401063 .text:00401065 .text .-00401067 .text:00401067 .text:00401067 mov ed\ dS:GcLMcssagcA первый вызов GetMessageA (это еще не цикл, это только его преддверье) p-jsh 0. wsgFilterMax push 0: wMsgFilterMin lea осх. [esp+2Ch+Msg] ECX указывает на область памяти, через которую GetMessageA станет возвращать сообщение, текущее значение ESP может быть любым, главное, чтобы оно указывало на действительную область памяти (см. карту памяти, если значение ESP оказалось искажено настолько, -то вывело его в "космос") push0 pjshесх mcvesi. eax callcd : GetMessageA вызываем GetMessageA : hWnd : lpMsg test eax. eax jz short loc 4010AD проверка на наличие необработанных сообщений в очереди *- Программная часть 273 .text:ОО4О1077 .text:0040-077 .text:00401G77 text:0040IC77 ext:00401C7B .text:00401078 .text:0040107B .text:00401C7F .text:00401C8D ,text:00401081 .text:00401082 .text:00401C82 ,text:00401C82 .text:00401084 .text:00401086 .text:00401086 .text:00401C86 .text:00401C88 .text:0040108C .text:0040108D .text:00401CSD .text:0040138D .text:004C108F .text:00401093 .text:00401G94 .text:00401094 .text:0040109A •text:0040109Л text:0040109A •text:0040109C •text:0040109E -text:004010A2 text:004010A4 text:00401 OAS text:004010A5 text:004010A5 text:004010A7 •text:00401CA9 text:004010A9 •text:004010A9 •text:00401CAB text;004C10AC text;004010AD text:00401CAD text:004ClOAD •te>!t:004010B! text:004010B? text:00401033 •text:004010B6 text:0040ICB6 loc 401077: : начало цикга обработки сообщений : CODE XREF: WinMdTnP16*A9vj mov eax. Lesp+2Cb+Msg.hwnd] lea edx. [esp+2Ch+Msg] EDX указывает на область памяти, используемую для передачи сообщений pusn edx push esi push eax call ebx вызываем функцию TranslateAcceleratorA lpMsg hAccTable hwnd Trans1ateAcceleratorA test eax. eax jnz snort loc 40109A проверка на наличие в очереди необработанных сообщений lea есх. [esp+2Ch+Msg] push есх: lpMsg call езр: TranslateMessage вызываем функцию TranslateMessage.если есть что транслировать lpMsg CODE XREF: WinMain@16+86Aj wMsgFilterMax wMsgFilterMin hWnd lpMsg GetMessageA lea edx. [esp+2Ch+MsgJ push edx call ds:OispatchMessageA : диспетчеризуем сообщение 1ос 40109А: push0 pushС !eaeax. [esp+34h+Msg] push0 pusheax calledi ; читаем очередное сообщение из очереди test eax. eax jnz short loc 401077 : вращаем цикл обработки сообщений pop ebp pop ebx ioc 4010AD:: CODE XREF: WinMain@16+67*j moveax. Lesp+24h+Msg.wParam] poped i popes-: addesp. ICh retnlOh WinMain@16endp Мы видим, что цикл обработки сообщении начинается с адреса 4Q1050h, и именно на этот адрес следует передать управление, чтобы возобновить работу упавшей программы. Пробуем сделать это, и... программа работает! Разумеется, настоящее приложение оживить намного сложнее, поскольку цикл обработки сообщений в нем рассредоточен по большому количеству функций, отождествить которые при беглом дезассемблировании невозможно. Тем не менее приложения, построенные на основе общедоступных библиотек (например, MFC, OVL), обладают вполне предсказуемой архитектурой, и .реанимировать их вполне возможно. Рассмотрим, как устроен цикл обработки сообщений в MFC. Большую часть своего времени исполнения MFC-приложения проводят внутри функции CWinThread: -.Run(void), которая периодически опрашивает очередь на предмет поступления свежих сообщений и рассылает их соответствующим обработчикам. Если один из обработчиков споткнулся и довел систему до критической ошибки, выполнение программы может быть продолжено в функции Run. В этом-то и заключается ее главная прелесть! Функция не имеет явных аргументов, но принимает скрытый аргумент this, указывающей на экземпляр класса CWinThread или производный от него класс, без которого функция просто не сможет работать. К счастью, таблицы виртуальных методов класса CWinThread содержат достаточное количество «родимых пятен», чтобы указатель this можно было воссоздать вручную. Загрузим функцию Run в дизассемблер и отметим все обращения к таблице виртуальных методов, адресуемой через регистр ЕСХ (листинг А.15). Листинг А.15. Дизассемблерный листинг функции Run (фрагмент) .text-.6C29919D n2k Trasnlate nai .text:6C29929D CODE XREF: MFC42 5715+lFTj Ч-С42 5715+67vj ...
0 ... 85 86 87 88 89 90 91 ... 102
|