Русский
Русский
English
Статистика
Реклама

Seh

Использование SEH в 32 разрядных приложениях Windows с компилятором Mingw-W64

23.12.2020 22:19:01 | Автор: admin

Что такое SEH


Из всех механизмов, предоставляемых операционными системами семейства Windows, возможно наиболее широко используемым, но не полностью документированным, является механизм структурной обработки исключений (он же Structured Exception Handling, или просто SEH). Структурная обработка исключений это сервис, предоставляемый операционной системой, механизм обработки программных и аппаратных исключений в операционной системе Microsoft Windows, позволяющий программистам управлять обработкой исключений. Исключение это событие при выполнении программы, которое приводит к её ненормальному или неправильному поведению.

Вся документация по SEH, которую вы, вероятно, найдете, описывает одну лишь компиляторно-зависимую оболочку, созданную функциями библиотеки времени выполнения (Run-Time-Library, RTL) вокруг реализации SEH операционной системы. В ключевых словах _try, _finally, или _except, нет ничего магического. Группы разработчиков из Microsoft, занимающиеся разработкой операционных систем и компиляторов, определили эти ключевые слова, и то, что они делают. Другие поставщики компиляторов просто поддержали эту семантику. Видимые программисту инструменты SEH уровня компилятора призваны скрыть базовый механизм SEH уровня операционной системы, что позволяет не обнародовать детали функционирования последнего. Основные детали базового механизма SEH уровня ОС будут рассмотрены в этой статье. В статье отражены личные взгляды и предпочтения автора.

Основной смысл SEH состоит в следующем: когда выполняющийся поток совершает ошибку, ОС дает возможность узнать об этом. Точнее, когда поток совершает ошибку, ОС приостанавливает поток, сохраняет все регистры процессора в специальной структуре и из служебного потока, связанного с тем, который вызвал ошибку, вызывает определенную пользователем callback-функцию. Функциональность этой callback-функции в значительной степени не регламентирована, т.е. в процессе своей работы она может делать все, что угодно. Например, она может устранить причину ошибки, если это возможно. Независимо оттого, что эта callback-функция делает, она должна возвратить значение, которое скажет системе, что делать дальше. Эта callback-функция называется exception handler(обработчик исключения). Для того, чтобы быть вызванной:
1. Функция обработчик исключения должна соответствовать прототипу
2. Адрес функции обработчика исключения должен быть указан в специальной структуре данных EXCEPTION_REGISTRATION
3. Структура данных EXCEPTION_REGISTRATION должна располагаться в стеке потока, который вызвал исключение с выравниванием адреса структуры и ее компонентов на адрес, кратный 4 (биты 0 и 1 адреса равны нулю)
4. Тело функции обработчика исключения не должно располагаться в стеке потока, который вызвал исключение

Значение, которое возвращает обработчик исключения, указывает ОС обработано ли исключение или нет.

Технические детали SEH


Прототип функции обработчика исключения:
EXCEPTION_DISPOSITION __cdecl _except_handler(     struct _EXCEPTION_RECORD *ExceptionRecord,     void * EstablisherFrame,     struct _CONTEXT *ContextRecord,     void * DispatcherContext     );

EXCEPTION_DISPOSITION это макроопределение, имеющее тип int, _EXCEPTION_RECORD содержит информацию о произошедшем исключении, EstablisherFrame адрес структуры EXCEPTION_REGISTRATION в стеке потока, вызвавшего исключение, _CONTEXT адрес структуры содержащей все регистры процессора в момент возникновения исключения, DispatcherContext служебая информация

Структура данных EXCEPTION_REGISTRATION
_EXCEPTION_REGISTRATION struc
prev dd ?
handler dd ?
_EXCEPTION_REGISTRATION ends


image
Как видно из определения, структура состоит из 2х элементов по 32 бита, prev адрес структуры с предыдущим обработчиком, handler адрес функции обработчика исключения. Наличие элемента prev позволяет выстраивать цепочку из обработчиков, адрес последней структуры с актуальным обработчиком доступен через регистр fs:0, отсюда и начинается просмотр и вызов последовательности обработчиков, до тех пор, пока один из них не вернет значение ExceptionContinueExecution (0). Если ни один обработчик из цепочки обработчиков не может обработать исключение, то его обрабатывает первый элемент цепочки обработчик исключений по умолчанию (стандартный обработчик), у которого нет варианта отказаться от обработки исключения.
image

SEH это ошибка.


Я совершенно уверен в том, что сама изначальная идея SEH ошибочна. Операционная система будет сообщать программе о возникновении незапланированных исключительных ситуаций, таких как деление на 0, неверный HANDLE, попытка исполнения неверной инструкции процессора попытка доступа к памяти при отсутствии соответствующих разрешений и так далее. Совершенно нереально исправить такие ошибки во время исполнения программы, ошибка уже случилась, программа уже пошла по неверному пути, который привел ее к этой ошибке. Нельзя уже сделать ничего разумного, но SEH дает возможности:
1. Скрыть ошибку, заменив стандартный обработчик ошибок своим, и обработать исключение
2. Позволить программе прожить немного дольше, сделав вид, что ничего не случилось
И первое, и второе очень плохо.
К сожалению, стандартные обработчики ошибок современных ОС семейства Windows не показывают пользователю ничего, что могло бы информировать его о том, что случилось и что надо делать, после паузы и показа неинформативного окна приложение аварийно закрывается. Моя идея состоит в том, чтобы при возникновении исключения программист должен иметь возможность информировать пользователя об ошибке с помощью специально сформированных сообщений, после чего выполнить минимально возможную приборку за собой и постараться корректно закрыть программу. Еще раз напоминаю, что в большинстве случаев нормальное продолжение выполнения программы после возникновения исключения невозможно.

Компилятор GCC


GCC является главным компилятором для сборки ряда операционных систем; среди них различные варианты Linux и BSD, а также ReactOS, Mac OS X, OpenSolaris, NeXTSTEP, BeOS и Haiku.
GCC часто выбирается для разработки программного обеспечения, которое должно работать на большом числе различных аппаратных платформ.
GCC является лидером по количеству процессоров и операционных систем, которые он поддерживает. Имеет развитые средства для программирования на встроенном ассемблере (ассемблерные вставки). Генерирует компактный и быстрый исполняемый код. Имеет возможности оптимизации, в том числе с учетом ассемблерных вставок. К относительным недостаткам GCC можно отнести непривычный синтаксис AT&T используемый во встроенном ассемблере.
GCC/MinGW является полностью надежным качественным компилятором, который, на мой взгляд, превосходит любой доступный на сегодняшний день компилятор языка Си по качеству сгенерированного кода. Это несколько менее выражено с самыми последними версиями MSVC, но все еще заметно. Особенно для всего, что связано с inline assembly, GCC на мой взгляд, превосходит MSVC.
Соответствие стандартам, как мне кажется, также намного лучше в GCC.

MinGW-W64 компилятор


MinGW набор инструментов разработки программного обеспечения для создания приложений под Windows. Включает в себя компилятор, родной программный порт GNU Compiler Collection (GCC) под Windows вместе с набором свободно распространяемых библиотек импорта и заголовочных файлов для Windows API. В MinGW включены расширения для библиотеки времени выполнения Microsoft Visual C++ для поддержки функциональности C99.
В связи с тем, что в рамках изначального проекта MinGW не обещалось, что в его кодовую базу будут вноситься обновления, связанные с добавлением некоторых новых ключевых элементов Win32 API, а также наиболее необходимой поддержки 64-битной архитектуры, был создан проект MinGW-w64. Он является новой чистой реализацией портирования GNU Compiler Collection (GCC) под Microsoft Windows, осуществленной изначально компанией OneVision и переданной в 2008 году в общественное пользование (Public Domain). Сначала он был предложен на рассмотрение для интеграции с оригинальным проектом MinGW, но был отклонен в связи с подозрением на использование несвободного или проприетарного кода. По многим серьезным причинам этического характера, связанным с отношением со стороны авторов MinGW, ведущие разработчики кода MinGW-w64 решили больше не пытаться кооперироваться с проектом MinGW.
MinGW-w64 обеспечивает более полную реализацию Win32 API, включая:
  • лучшую поддержку стандарта C99
  • лучшую поддержку pthreads (включая возможность задействовать функциональность стандарта C++11 библиотеки libstdc++ компилятора GCC)
  • GCC multilib
  • точки входа в программу с поддержкой Unicode (wmain/wWinMain)
  • DDK (из проекта ReactOS)
  • DirectX (из проекта WINE)
  • поддержку больших файлов
  • поддержку 64-битной архитектуры Windows

Собственно, компилятор используемый в статье доступен по
ссылке

Поддержка SEH в Mingw-w64


Совершенно неудовлетворительна поддержка SEH, проще сказать ее нет, несмотря на наличие макросов __try1() и __except1. Мало того, что ни синтаксически (поскольку синтаксис __try __except() не поддерживается), ни семантически (макросы __try1 __except1 только позволяют установить/удалить обработчик исключений) невозможно программирование SEH, как с компиляторами Microsoft или Borland/Embarcadero, даже само использование макросов __try1 __except1 может приводить к проблемам

Проблемы


Использование стандартных макросов __try1 и __except1 может приводить к проблемам, так как в этих макросах явно изменяется состояние регистра ESP, но компилятор, который должен вести учет использования стека (что означает, что компилятор всегда должен знать, каково значение регистра ESP), об этом не уведомляется. Может быть, стандартные макросы будут работать, а может быть и нет.

Решение


Во-первых, однозначно придется отказаться от использования стандартных макросов __try1 и __except1, вместо них будут ассемблерные вставки, не затрагивающие регистр ESP и локальная переменная типа EXCEPTION_REGISTRATION в стеке. Приблизительно вот так:

static int exception=0;EXCEPTION_REGISTRATION seh_ex_reg;seh_ex_reg.handler = (PEXCEPTION_ROUTINE) exception_handler;/*ассемблерная вставка, которая в поле prev записывает адрес предыдущей структуры из fs:0 и устанавливает новый адрес этой структуры в fs:0 */asm ("\t movl %%fs:0, %%eax; movl %%eax, %0 \n" : "=r" (seh_ex_reg.prev) : : "%eax");asm volatile("\t movl %0,%%eax; movl %%eax, %%fs:0 \n"::"r" (&seh_ex_reg) : "%eax");// блок try{// codeif(exception == 0){//ассемблерная вставка: убираем обработчик исключений asm (// restore previous handler, ESP is not modified "\t movl %0, %%eax \n""\t movl %%eax, %%fs:0 \n" : :"r" (seh_ex_reg.prev) : "%eax");goto end;}}// блок except{trusted_code://сюда мы попадаем при исключении//ассемблерная вставка: убираем обработчик исключений//выполняем очистку}end:exit();

Во-вторых, необходимо отделить доверяемый код (т.е., отлаженный, не генерирующий исключений) от недоверяемого (где возможны исключения). Это достаточно стандартная практика, где за блоком try следует блок except С помощью нескольких глобальных переменных, ассемблерных вставок и имеющего дурную славу опрератора goto удалось решить такую задачу. Решение не претендует на универсальность, над этим еще надо думать, пока это выглядит как сваленный в кучу набор трюков. Недостатки: много ассемблерного кода, используются статические переменные, не производится размотка стека, состояние локальных переменных в стеке try блока теряется безвозвратно. Корректное завершение программы возможно, используя только переменные и функции из except блока.
От использования статических переменных, впрочем, легко отказаться, для чего придется расширить структуру EXCEPTION_REGISTRATION, дополнив ее недостающими элементами, перенести туда все бывшие ранее статическими переменные. Это сделает код примера не намного сложнее, но зато код будет по-настоящему структурным, способным обрабатывать исключения на нескольких уровнях вложенности. Доступ к дополнительным элементам расширенной структуры EXCEPTION_REGISTRATION_EX из функции обработчика исключения осуществляется через параметр EstablisherFrame который нужно преобразовать к соответствующему типу. Заодно сохраним значения регистров ESP и EBP, чтобы восстановить стек фрейм к тому состоянию, которое было в начале блока try. Теперь наш пример обретает законченную форму. К сожалению, при возникновении исключения, содержимое локальных переменных блока try безвозвратно теряется.
Ссылка на исходный текст
Подробнее..

О параметре компилятора SAFESEH

16.06.2021 18:22:08 | Автор: admin

Введение

В этой истории я расскажу вам об увлекательном приключении, которое привело меня к решению одной загадки, которую я сам себе загадал. Разгадка являет собой небольшую подробность в механизме загрузчика 32-х разрядных приложений в системе Windows 7 и выше, а процесс разгадки - длинное путешествие воина, который следует по пути сердца.

Если вы попали на эту страницу в поисках ответа на вопрос, то смотрите спойлер ниже, потому что основное содержание может быть интересно тем, кто только пытается разобраться в механизме SEH.

Поле boundImport, которое содержится в структуре dataDirectories, указывает на структуру IMAGE_LOAD_CONFIG_DIRECTORY32. Эта структура содержит поле SEHandlerTable. Если оно равно нулю, то параметр /SAFESEH выключен. Если оно является виртуальным указателем на таблицу безопасных обработчиков, то будут работать только те обработчики, которые указаны в таблице. Таблица представляет из себя список смещений, относительно виртуального адреса секции .text . Количество обработчиков задаётся в поле SEHandlerCount.

С чего всё началось

Задумавшись о способе защиты своей программы от копирования, я вспомнил о статье Криса Касперского, в которой я узнал о существовании антиотладочных приёмов. Тщетно пытаясь реализовать разобранный Крисом пример в своей программе, я натыкался на стену непонимания со стороны Windows. Проблема заключалась в том, что Windows отказывалась пользоваться моим обработчиком исключений, посчитав его небезопасным.

Более менее подробно о SEH мне удалось почерпнуть из этой статьи. Но, тем не менее, пока я сам не рассмотрел этот механизм c лупой и инструментами, у меня оставались очень смутные представления о его устройстве. Я разматывал цепочки обработчиков, ставил на них точки останова, наблюдал порядок их выполнения, рассматривал возвращаемые значения и т.д.

Ближе к делу

Рассмотрим маленький пример (я пользуюсь компилятором MicrosoftVisual C++ )

int main(){ __asm { mov eax, DWORD PTR SS : [0] }}

Ничего особенного, пытаемся положить в регистр eax содержимое по адресу 0.

Компилируем, запускаем под отладчиком OllyDbg.

Получаем грустное сообщение access violation when reading 0x00000000.

Берем на заметку, что размер инструкции, которая пытается прочитать по адресу 0 - равен 6 байтам. Обратим также внимание на регистр Eip, который указывает на проблемную инструкцию (совпадает с её адресом слева). Сама же инструкция выглядит как последовательность из 6 байт: 36 A1 00 00 00 00.

Теперь я хочу обработать это исключение, не используя ключевых слов __try, __except т.к. мне показалось это интересной и заманчивой идеей.

Напишем же свой собственный обработчик для обработки этого исключения!

Есть информация, что при возникновении исключения в функцию-обработчик система отправляет следующие аргументы:

    struct _EXCEPTION_RECORD* exceptionRecord,    void* establisherFrame,    struct _CONTEXT* contextRecord,    void* dispatcherContext

где exceptionRecord структура, содержащая код ошибки, адрес исключения, а contextRecord структура содержащая контекст регистров (включая регистр Eip). Соглашение о вызове функции-обработчика должно быть __cdecl, а возвратить она должна одно из следующих значений:

typedef enum _EXCEPTION_DISPOSITION{ ExceptionContinueExecution, ExceptionContinueSearch, ExceptionNestedException, ExceptionCollidedUnwind} EXCEPTION_DISPOSITION;

Я решил обработать это исключение таким нехитрым образом:

EXCEPTION_DISPOSITION __cdecl ExceptionHanler(    struct _EXCEPTION_RECORD* exceptionRecord,    void* establisherFrame,    struct _CONTEXT* contextRecord,    void* dispatcherContext){    contextRecord->Eip += 6;    MessageBoxA(0, "Exteption was handled", "Success!", 0);    return ExceptionContinueExecution;}

Сместив регистр Eip на 6 байт, мы обойдём проблемную инструкцию! И говорим: ExceptionContinueExecution продолжить выполнение. Соответственно, выполнение продолжится с инструкции по адресу Eip + 6 и исключений больше возникнуть не должно.

Просто поместить код обработчика в программе - этого мало. Необходимо дать понять системе, что нужно вызывать именно его. И ещё я хочу, чтобы он вызвался самым первым, среди прочих. Для этого нам необходимо где-то создать структуру, состоящую из двух полей: Next и Handler. Поле Next будет содержать указатель на такую же структуру, но с предыдущим обработчиком (с тем, который мы потесним). Поле Handler будет содержать указатель на нашу функцию-обработчик. Указатель на текущую такую структуру находится в TIB (thread information block), иначе говоря, по адресу fs:[0]. Создадим её в стеке.

Преобразуем нашу функцию main:

int main(){__asm{    push ExceptionHanler //положим в стек адрес функции-обработчикаpush fs:[0]          //положим в стек указатель на структуру текущего обработчика                     //теперь в стеке лежит структура, содержащая 2 поля: указатель на     //следующий обработчик и адрес нашего обработчикаmov fs:[0] , esp   //положим указатель на свою структуру, вместо текущего                                                                                                                                                   //обработчика по адресу fs:[0]mov eax, DWORD PTR SS:[0] //пытаемся прочитать по адресу 0    add esp, 8          //чистим стек, удалив 8 байт (размер структуры)}}

По незнанию, я компилирую это с настройками по умолчанию - с флагом /SAFESEH и игнорирую непонятные рекомендации компилятора о том, что мне неплохо было бы отключить этот флаг, раз я записываю что-то по адресу fs:[0]. Да что там говорить, я далеко не сразу обратил на это внимание.

Результат все та же ошибка чтения по адресу 0 и приложение падает как ни в чем не бывало!

Хорошо, что все позади. Теперь я знаю об этом флаге. Пробую компилировать с настройкой /SAFESEH:NO.

Компилируем, выполняем наш код, видим наше развесёлое сообщение:

Жмем ок программа успешно завершается. Фух.

Переломный момент

Но нет, подождите-ка, но что ИМЕННО сделал в тот раз компилятор, чтобы проигнорировать мой обработчик исключения? Что это за фокусы? И как мне быть, если вдруг ну чисто теоретически, мне вдруг однажды захочется сделать так, чтобы моё приложение имело флаг /SAFESEH, но при этом, чтобы оно заработало, я должен буду встроить в него код mov fs:[0], esp, с указателем на свою функцию-обработчик, вызвать в произвольном месте исключение и обработать его так, как моей душе угодно? Не считаю, что флаг /SAFESEH должен быть помехой для моих фантазий.

Пытаясь получить информацию о работе этого флага, я увидел что-то размытое про таблицы безопасных обработчиков. Если они есть, то как мне на них посмотреть? Как добавить в них свой обработчик? Каким образом нужно изменить исполняемый файл, чтобы заставить систему при его исполнении выбирать обработчик именно из этой таблицы или наоборот, отключить выбор обработчика? Столько вопросов и так мало ответов

Не падая духом, в надежде разгадать загадку

Полистав срабатывающие обработчики, я увидел те, которые пушит компилятор на стадии выполнения crt0 (код до функции main), а также обработчик из библиотеки ntdll.dll. Все эти обработчики исправно срабатывают в порядке своей очереди, если я не помещаю свой обработчик по адресу fs:[0]! Но как система узнает, какая функция безопасный обработчик, а какая нет?

В своем путешествии я провёл серию экспериментов. Например, компилируя с флагом /SAFESEH, ставил точку останова перед проблемной инструкцией, брал первый обработчик, на который указывала первая структура по адресу fs:[0] и менял начало функции прямо в отладчике на JMP ExceptionHandler (мой самописный обработчик). Срабатывает! Срабатывает именно мой обработчик! И к чему тогда эта неведомая таблица безопасных, когда я могу легко подставить вместо любого безопасного прыжок на свой опасный? Впрочем, не важно. Это не даёт ответа на мои изначальные вопросы.

Теперь я пробую записать лог от entry point до функции main двух EXE файлов, скомпилированных с флагом /SAFESEH и /SAFESEH:NO, наивно предполагая, что компилятор на этой стадии вызывает некие WinApi функции, с помощью которых можно добавить в систему адреса безопасных обработчиков. Все тщетно. Два лога практически не отличаются, разве что серией дополнительных невнятных инструкций, не имеющих ничего общего с адресами безопасных обработчиков. Но подозрения все равно остались.

Чтобы окончательно исключить crt0 из списка подозреваемых мне необходимо выяснить еще кое-что. Я придумал следующий эксперимент, результат которого даст мне точный ответ на вопрос! Меняю инструкцию точки входа в программу на следующие команды push main, ret. Таким образом, первой командой в стек попадает адрес функции main, а вторая команда ret возвращает регистр указатель на выполнение функции main и функция main выполняется сразу, без crt0! (можно было обойтись и простым JMP, но его немного сложнее реализовать на лету в отладчике)

Я снова был удивлён, ведь мой обработчик вновь не вызвался! Несмотря на неудачу, это дало очень важный ответ: тайна кроется внутри EXE, скомпилированного хитрым образом, а не в коде, который выполняет компилятор.

Может быть, они спрятали этот флаг в PE хедере? В каком-нибудь поле LoaderFlags? И тут неудача У обоих файлов абсолютно идентичные структуры, за исключением разницы в размере секции rdata. Но даже если в секции rdata и лежит несколько дополнительных байт, разве это может быть способом взаимодействия с загрузчиком, кардинально изменяющим работу программы? Я был уверен, что нет

Ведь в секциях можно размещать что угодно, главное - соблюсти ключевые правила, думаю я. Там ведь полно мусора! Всякие строки, цифры, дни недели, это ведь не монолитная структура какая-то, а блоки данных, на которые указывают указатели. Эти блоки располагаются в произвольном месте, но в диапазоне секции. При сравнении двух файлов, никаких указателей на область rdata я не увидел, кроме известной importTable, debug и неизвестного указателя на boundImport. Кроме того, такой же указатель был и в файле без флага /SAFESEH! Его размер в таблице dataDirectories был очень маленьким и он был заполнен нулями. Это не вызывало моих подозрений, потому что я был уверен, что если бы это каким-то образом включало в себя таблицу обработчиков, то в файле без флага /SAFESEH это поле просто напросто отсутствовало бы, что было бы знаком для загрузчика у этого EXE таблицы нет.

Победа близка

Отчаявшись, я беру адрес первого безопасного обработчика, который подсовывает в мой EXE компилятор. А точнее - первые два младших байта его адреса и прошелся поиском по всему файлу. То, что я решил взять именно 2 байта, включая те значения, которые не являются виртуальными адресами, является везением. И вот, я выписал все сырые адреса, по которым располагаются эти значения, преобразовал адреса в виртуальные (их было около 3-х) и приступил к задуманному.

Имея несколько адресов в памяти, предположительно намекающих на законный обработчик от компилятора, я ставлю точку останова перед командой mov eax, DWORD PTR SS : [0] и жму play. Срабатывает. Я расставляю точки останова на чтение памяти по записанным адресам и снова жму play.

Удивительно! О брекпоинт споткнулась некая безымянная функция в библиотеке ntdll.dll.

Смотрим. Функция пытается считать по адресу 0x1341d20 из диапазона rdata число 0x1be0, а затем сравнить его с числом, находящимся в EBX 0x1000. Так, а ведь по адресу imageBase + 0x1000 расположился мой подставной обработчик! Он - то сейчас и находится в числе первых обработчиков. А по адресам 0x1be0, 0x2080 и 0x2351 располагаются безопасные обработчики от компилятора. Вот она эта таблица! Она всё это время была в моём файле!

В отладчике я меняю содержимое адреса 0x1341d20 с 0x1be0 на 0x1000 и мой обработчик стал вдруг безопасным и послушно обработал исключение!

Реализация проверки безопасных обработчиков в открытом виде! Она не находится внутри ядра Windows, как я ожидал. Так что можно даже подкорректировать код в ntdll.dll, чтобы в нужное время он разрешал или запрещал выбранные нами обработчики!

Вернёмся к нашему файлу еще раз внимательно посмотрим, что находится в окрестностях.

Ага, таблица на месте. Кроме того, случайно мне бросился в глаза адрес-указатель на начало таблицы, а также странное совпадение число 3, равное количеству обработчиков.

Попробую-ка я заполнить нулями этот адрес-указатель.

Успех! Программа, которую я скомпилировал с флагом /SAFESEH теперь выполняется так, как будто бы был установлен флаг флага /SAFESEH:NO!

А сравним с файлом, который был скомпилирован без этого флага?

Чёрт ни адреса-указателя на таблицу в этом месте, ни таблицы обработчиков И как я сразу не додумался сравнить два файла скомпилированных с разными флагами в Total Commander?!

Тем не менее, что указывает на это место в PE хедере? Самым близким является boundImport. И вот только теперь я открываю сайт Microsoft с описанием PE формата.

В общем я предлагаю вам самим ознакомиться с тем, что парни из Microsoft пишут там про boundImport. Что-то о таблице каталога отложенной загрузки на этапе связывания пост-обработки? Что?

В этом большом и непонятном документе, в котором описаны разные различия этого формата для разных платформ и т.д. Но меня уже не остановить, я начинаю упорно вводить в поиске слово except прыгаю по результатам поиска.. Вот, .sxdata - содержит индекс символа каждого из обработчиков. Какая . sxdata? У меня такого нет. Есть только виртуальный указатель в поле boundImport, относительно которого неподалёку размещается адрес-указатель и таблица с адресами функций-обработчиков.

Ищу дальше... Вот, ещё говорят, что в разделе pdata содержится массив обработчиков и что таблица может быть в нескольких форматах. Даже если и так, как загрузчик попадает на начало этой таблицы? В том месте, куда указывает boundImport, нет ничего похожего на тот формат, который представлен.

Смотрю дальше... The Load Configuration Structure. Судя по описанию очень похоже! Они говорят, что структура IMAGE_LOAD_CONFIG_DIRECTORY где-то присутствует и описывают ее предназначение, связанное с SEH.

Думаю, а попробую-ка. Взял, да и написал, что по адресу boundImport находится структура IMAGE_LOAD_CONFIG_DIRECTORY32 и рассмотрел её поля. Все сошлось как пазл! Это опять же было большим везением, что я решился на это.

Вот и всё, весь секрет заключался в том, что boundImport в PE хедере (разложенном по таблице из Википедии) указывает на структуру IMAGE_LOAD_CONFIG_DIRECTORY32. Эта структура содержит поля, которые кардинальным образом меняют работу программы! Теперь мы с уверенностью можем сами создать свою таблицу обработчиков и добавить в неё только то, что посчитаем нужным!

Может быть, где-то в недрах документации есть информация о том, что boundImport уже называется не так на моей платформе? Или информация о том, что его назначение изменилось? Может быть, boundImport оказался исключен или сдвинут?

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru