Пишем упаковщик по шагам. Шаг девятый. Delay-loaded DLLs и Image Config.
Предыдущий шаг здесь.
Появилась новая версия библиотеки для работы с PE-файлами (0.1.8). Перекачайте и пересоберите ее.
Сегодня мы будем заниматься теми мелочами, на которые я в свое время забил при написании старого упаковщика. Наш распаковщик уже умеет всё, но есть пара мелких нюансов, которые неплохо бы допилить. Первое - это отложенный импорт (Delay-loaded). Этот механизм позволяет загружать необходимые PE-файлу библиотеки тогда, когда они реально становятся нужны, тем самым экономя время на загрузку образа в память. Механизм этот реализуется исключительно компиляторами/линкерами и никакого отношения к загрузчику не имеет, однако в PE-заголовке есть директория IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, указывающая на данные отложенного импорта. Не знаю, используется ли это линкером и собранной программой, но загрузчику определенно пофиг. Но лучше оставим эту директорию, не будем ее обнулять. Уберем строку
image.remove_directory(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT); |
Эта статья рассказывает о какой-то антинаучной хуйне. Если вы — физик, химик, биолог или просто слишком хорошо помните школьную программу соответствующего курса, вам лучше ее не читать. В противном случае вы рискуете умереть от смеха. Мы предупредили. |
I see what you did there. Информация в данной статье приведена по состоянию на неизвестно когда. Возможно, она уже безнадёжно устарела и заинтересует только слоупоков. |
С отложенным импортом всё. Следующая вещь, требующая внимания - это конфигурация загрузки образа. Есть такая директория в заголовке PE-файлов, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG. Эта директория может содержать структуру IMAGE_LOAD_CONFIG_DIRECTORY32 для x86 PE-файлов (IMAGE_LOAD_CONFIG_DIRECTORY64 для PE+), которая предоставляет загрузчику информацию о том, как образ должен быть загружен. Еще там же содержится список адресов команд, имеющих префикс LOCK, который на однопроцессорных системах заменяется на NOP, а также список всех SEH-обработчиков (он используется для предотвращения SEH-хакинга и представляет собой список всех легальных и допустимых обработчиков исключений в PE-файле). Компиляторы MSVC++ последних версий иногда генерируют эту директорию, помещая в нее список SEH-обработчиков программы и указатель на свой security cookie (переменная для контроля переполнений и порчи буферов/стэка). Судя по исходникам ядра Win 2000, это все считывается загрузчиком, поэтому убивать напрочь директорию IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG не совсем правильно, хотя нарушений в работе PE-файлов после ее зануления я не наблюдал. Сохраним эту директорию, переместив ее во вторую секцию упакованного файла ("kaimi.ru"). Первым делом уберем из кода упаковщика строку
image.remove_directory(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG); |
В остальном нам всегда поможет моя библиотека для работы с PE. Список SE-хендлеров мы просто перенесем в нашу вторую добавленную секцию. А вот список адресов LOCK-префиксов нам придется вручную обработать в распаковщике (переносить его мы не будем, так как загрузчик на этапе распаковки не должен их фиксить - файл еще не распакован). В распаковщике добавим после строк
exports = image.get_exported_functions(exports_info);} |
строки
//Если файл имеет Image Load Config, получим информацию о ней std::auto_ptr<pe_base::image_config_info> load_config;if(image.has_config()){ std::cout<<"Reading Image Load Config..."<< std::endl; load_config.reset(new pe_base::image_config_info(image.get_image_config()));} |
Эти строки считывают конфигурацию загрузки образа, если она имеется. Код аналогичен считыванию TLS. Далее, строку
if(tls.get()|| image.has_exports()|| image.has_reloc()) |
меняем на
if(tls.get()|| image.has_exports()|| image.has_reloc()|| load_config.get()) |
так как директорию конфигурации загрузки мы будем размещать там же, в секции "kaimi.ru". Далее, аналогично меняем строку
if(!image.has_reloc()&&!image.has_exports()) |
на
if(!image.has_reloc()&&!image.has_exports()&&!load_config.get()) |
и
image.rebuild_relocations(reloc_tables, unpacker_section, unpacker_section.get_raw_data().size(), true, !image.has_exports()); |
на
image.rebuild_relocations(reloc_tables, unpacker_section, unpacker_section.get_raw_data().size(), true, !image.has_exports()&&!load_config.get()); |
и, наконец,
image.rebuild_exports(exports_info, exports, unpacker_section, unpacker_section.get_raw_data().size()); |
на
image.rebuild_exports(exports_info, exports, unpacker_section, unpacker_section.get_raw_data().size(), true, !load_config.get()); |
Далее, в структуру packed_file_info (файл structs.h) добавим пару новых полей:
DWORD original_load_config_directory_rva;//Относительный адрес оригинальной директории конфигурации загрузки DWORD lock_opcode;//Фиктивный опкод команды ассемблера LOCK |
Эти поля нам потребуются в распаковщике, а пока что в упаковщике мы их заполним, дописав после строк
//Запоминаем относительный адрес и размер//оригинальной директории релокаций упаковываемого файла basic_info.original_relocation_directory_rva= image.get_directory_rva(IMAGE_DIRECTORY_ENTRY_BASERELOC); basic_info.original_relocation_directory_size= image.get_directory_size(IMAGE_DIRECTORY_ENTRY_BASERELOC); |
следующие строки:
//Запоминаем относительный адрес//оригинальной директории конфигурации загрузки упаковываемого файла basic_info.original_load_config_directory_rva= image.get_directory_rva(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG); |
и после
//Получаем и сохраняем изначальное количество секций basic_info.number_of_sections= sections.size(); |
такие строки:
//Опкод ассемблерной инструкции LOCK basic_info.lock_opcode=0xf0; |
Поясню, зачем это нужно. Загрузчик увидит, что наша таблица LOCK-префиксов состоит из единственного элемента, который указывает на поле lock_opcode структуры basic_info (мы ее так соберем, разумеется). На однопроцессорной системе опкод команды LOCK (0xf0), который мы записали в это поле, будет заменен на опкод инструкции NOP (0x90), и в распаковщике мы сможем проверить, нужно ли обрабатывать оригинальную таблицу LOCK-префиксов. Вообще, я не уверен, что эта функциональность присутствует в загрузчиках новых систем начиная от XP (похоже, что всем системам наплевать на эти таблицы), однако, пусть будет, мало ли всплывет. На самом деле, я и файлов-то с LOCK-таблицами не видел ни разу, и может мне просто нефиг делать. Хотя видел, в исходниках Win 2000, но об этом ниже. :D
Ладно, с правками покончено, переходим к пересборке директории конфигурации. Сразу после куска кода, ответственного за пересборку экспортов, дописываем следующий код:
if(load_config.get()){ std::cout<<"Repacking load configuration..."<< std::endl; pe_base::section& unpacker_section = image.get_image_sections().at(1); //Очистим таблицу адресов LOCK-префиксов load_config->clear_lock_prefix_list();//Добавим единственный адрес нашего левого LOCK-префикса load_config->add_lock_prefix_rva(pe_base::rva_from_section_offset(image.get_image_sections().at(0), offsetof(packed_file_info, lock_opcode))); //Пересобираем директорию конфигурации загрузки и располагаем ее в секции "kaimi.ru"//Пересобираем автоматически таблицу SE Handler'ов и LOCK-префиксов image.rebuild_image_config(*load_config, unpacker_section, unpacker_section.get_raw_data().size(), true, true);} |
Мы пересобираем директорию конфигурации загрузки, располагая ее в самом конце второй добавленной в упакованный файл секции. В опциях распаковщика мы указываем, что таблицу SE-обработчиков и LOCK-префиксов нужно пересобрать. Оригинальную таблицу LOCK-префиксов мы обработаем уже в распаковщике. С упаковщиком на этом все. Переходим к проекту распаковщика (unpacker). Такое впечатление, что снова поехали смещения, указанные в файле parameters.h, и не факт, что в предыдущем шаге они правильные (MSVC++ собирает проект так, как ему соблаговолится, оптимизируя по размеру, поэтому минимальные изменения могут привести к тому, что ассемблерные команды будут использованы другие). Поэтому я решил их раз и навсегда зафиксировать, сделав так:
//Пролог вручную __asm { jmp next; ret 0xC; next: push ebp; mov ebp, esp; sub esp, 4096; mov eax, 0x11111111; mov ecx, 0x22222222; mov edx, 0x33333333;} //Адрес загрузки образаunsignedint original_image_base;//Относительный адрес первой секции,//в которую упаковщик кладет информацию для//распаковщика и сами упакованные данныеunsignedint rva_of_first_section;//Адрес загрузки образа (оригинальный, к нему не применяются релокации)unsignedint original_image_base_no_fixup; //Эти инструкции нужны только для того, чтобы//заменить в билдере распаковщика адреса на реальные __asm { mov original_image_base, eax; mov rva_of_first_section, ecx; mov original_image_base_no_fixup, edx;} |
Теперь у нас смещения ассемблерных команд [mov eax, 0x11111111] и т.д. будут всегда одинаковыми, так как опкод команд [mov eax/ecx/edx, число] всегда одинаков. Поправим под новый код значения смещений в файле parameters.h:
staticconstunsignedint original_image_base_offset =0x0F;staticconstunsignedint rva_of_first_section_offset =0x14;staticconstunsignedint original_image_base_no_fixup_offset =0x19; |
Далее перед кодом, обрабатывающим TLS, напишем следующий код:
//Если файл имеет директорию конфигурации загрузкиif(info_copy.original_load_config_directory_rva){//Получим указатель на оригинальную директорию//конфигурации загрузкиconst IMAGE_LOAD_CONFIG_DIRECTORY32* cfg =reinterpret_cast<const IMAGE_LOAD_CONFIG_DIRECTORY32*>(info_copy.original_load_config_directory_rva+ original_image_base); //Если директория имеет таблицу LOCK-префиксов//и загрузчик переписал наш подложный LOCK-опкод//на опкод NOP (0x90) (т.е. система однопроцессорная)if(cfg->LockPrefixTable && info_copy.lock_opcode==0x90/* NOP opcode */){//Получаем указатель на первый элемент таблицы//абсолютных адресов LOCK-префиксовconst DWORD* table_ptr =reinterpret_cast<const DWORD*>(cfg->LockPrefixTable);//Перечисляем ихwhile(true){//Указатель на LOCK-префикс BYTE* lock_prefix_va =reinterpret_cast<BYTE*>(*table_ptr); if(!lock_prefix_va)break; //Меняем его на NOP*lock_prefix_va =0x90;}}} |
Вот мы и закончили заниматься уже, по всей видимости, мало кому нужным функционалом, потому что современным одноядерным процессорам пофиг на префикс LOCK, и загрузчику пофиг на таблицу LOCK-префисков. :)
Забавно кстати, но EXE-файлы из Win 2000 нормально пакуются и работают под ней.
P.S. В Win2000 загрузчику тоже, кажется, насрать на LOCK-префиксы. Единственное, что он делает при загрузке - проверяет, чтобы по адресам LOCK-префиксов не были записаны опкоды инструкции NOP (0x90) для многопроцессорных систем. В то время Windows имела два ядра - однопроцессорное и многопроцессорное, которые подсовывались системе еще на этапе установки. С тех пор, по всей видимости, никто описанный функционал директории Load Configuration так и не реализовал, а поля с описаниями остались. Кстати, в Win2000 и сама структура другая совершенно, в ней отсутствуют некоторые поля. Моя библиотека для работы с PE-файлами ее считать не сможет. Но функционал в упаковщике я решил оставить. Теперь упаковщик самый правильный и соответствует открытой документации от Microsoft, хотя ей не соответствует их загрузчик. :) В конце-концов, пересборка самой директории конфигурации загрузки с сохранением адресов SEH-обработчиков - точно не лишнее.
Полный солюшен для этого шага: Own PE Packer Step 9
Также рекомендую почитать
Обсудить на форуме
Источник: http://feedproxy.google.com/~r/kaimi/dev/~3/1vX0RfI6eb0/


Дайджест новых статей по интернет-маркетингу на ваш email
Новые статьи и публикации
- 2025-03-14 » SPF-запись
- 2025-03-07 » SEO на маркетплейсах: как оптимизировать карточку товара для поисковой выдачи
- 2025-02-18 » Топ-10 бесплатных нейросетей для генерации изображений: лучшие ии генераторы 2024 года
- 2025-02-11 » Критическая уязвимость в 1С-Битрикс
- 2025-02-11 » Google Search Console: руководство для начинающих вебмастеров
- 2025-02-11 » Методы измерения результативности рекламных кампаний: плюсы и минусы
- 2025-02-11 » Тренды SEO в 2025 году
- 2025-02-10 » Свой Google в локалке. Ищем иголку в стоге сена
- 2025-01-29 » SEO — это комплексная работа. Шесть главных факторов ранжирования сайтов
- 2025-01-29 » Гайд для главной страницы e-commerce сайта: как оформить, чтобы повысить конверсию
- 2025-01-20 » Krea AI выпустила бесплатную функцию преобразования изображений в 3D-объекты — их можно вращать и вписывать в фотографии
- 2025-01-19 » Отзывы на Яндекс Картах: как пройти модерацию
- 2025-01-15 » Топ-6 лучших российских нейросетей, в которых можно генерировать тексты и изображения бесплатно и без VPN
- 2025-01-14 » 15 бесплатных способов узнать, чем интересуется ваша аудитория
- 2025-01-11 » Бездепозитные бонусы в казино за регистрацию с выводом: особенности и возможности получения
- 2025-01-09 » Новая модель LAM способна выполнять задачи в Word
- 2024-12-26 » Универсальный промпт для нейросети: как выжать максимум из ChatGPT, YandexGPT, Gemini, Claude в 2025
- 2024-11-26 » Капитан грузового судна, или Как начать использовать Docker в своих проектах
- 2024-11-26 » Обеспечение безопасности ваших веб-приложений с помощью PHP OOP и PDO
- 2024-11-22 » Ошибки в Яндекс Вебмастере: как найти и исправить
- 2024-11-22 » Ошибки в Яндекс Вебмастере: как найти и исправить
- 2024-11-15 » Перенос сайта на WordPress с одного домена на другой
- 2024-11-08 » OSPanel 6: быстрый старт
- 2024-11-08 » Как установить PhpMyAdmin в Open Server Panel
- 2024-09-30 » Как быстро запустить Laravel на Windows
- 2024-09-25 » Next.js
- 2024-09-05 » OpenAI рассказал, как запретить ChatGPT использовать содержимое сайта для обучения
- 2024-08-28 » Чек-лист: как увеличить конверсию интернет-магазина на примере спортпита
- 2024-08-01 » WebSocket
- 2024-07-26 » Интеграция с Яндекс Еда
"Интернет не меняет бизнес-модели, он способен лишь дать новые мощные инструменты уже существующим." |
Мы создаем сайты, которые работают! Профессионально обслуживаем и продвигаем их , а также по всей России и ближнему зарубежью с 2006 года!
Как мы работаем
Заявка
Позвоните или оставьте заявку на сайте.
Консультация
Обсуждаем что именно Вам нужно и помогаем определить как это лучше сделать!
Договор
Заключаем договор на оказание услуг, в котором прописаны условия и обязанности обеих сторон.
Выполнение работ
Непосредственно оказание требующихся услуг и работ по вашему заданию.
Поддержка
Сдача выполненых работ, последующие корректировки и поддержка при необходимости.
Или напишите нам в WhatsApp
Или напишите нам в WhatsApp