Print This Post Пишем упаковщик по шагам. Шаг десятый. Общая архитектура.

Воскресенье, 30. Сентябрь 2012
Раздел: Windows, Для новичков, автор:

Предыдущий шаг здесь

В этом шаге я ничего с кодом делать не буду, а просто приведу в доступном виде архитектуру упаковщика, а точнее, упакованного им файла. Я делаю это для того, чтобы вы смогли разобраться, как устроен упакованный файл, не изучая для этого все шаги подробно. Возможно, с этого стоило начать, но теперь уже поздно.

Итак, представим, что у нас есть DLL-файл, имеющий следующие директории:
- импорты
- экспорты
- ресурсы (в том числе информацию о версии)
- релокации
- конфигурацию загрузки
- TLS с коллбэками

Словом, всего по максимуму. Как это все будет расположено в упакованном файле?

Читать дальше »

Print This Post Менеджер плагинов для клиента LastFM

Суббота, 29. Сентябрь 2012
Раздел: C/C++, Windows, автор:

В перерыве между состоянием ретроспективной рефлексии и созерцанием километровых постов dx'a немного пришел в себя и решил сделать какой-нибудь пост в блог.

В результате написал небольшую вспомогательную библиотеку, которая позволяет загружать-выгружать "плагины" в клиент LastFM. Если быть точным, то не совсем плагины, а своеобразные надстройки, которые я несколько раз описывал, например, тут и тут.
GUI минималистичен и выглядит следующим образом:

Функционал элементарен. Окно менеджера можно вызвать с помощью нажатия Alt+M. В контекст клиента LastFM библиотеку следует либо грузить инжектором, либо править импорты, либо ещё как-нибудь.

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

Исходный код и скомпилированная версия менеджера: скачать.

Print This Post Пишем упаковщик по шагам. Шаг девятый. Delay-loaded DLLs и Image Config.

Пятница, 28. Сентябрь 2012
Раздел: C/C++, Windows, Для новичков, автор:

Предыдущий шаг здесь.

Появилась новая версия библиотеки для работы с PE-файлами (0.1.8). Перекачайте и пересоберите ее.

Сегодня мы будем заниматься теми мелочами, на которые я в свое время забил при написании старого упаковщика. Наш распаковщик уже умеет всё, но есть пара мелких нюансов, которые неплохо бы допилить. Первое - это отложенный импорт (Delay-loaded). Этот механизм позволяет загружать необходимые PE-файлу библиотеки тогда, когда они реально становятся нужны, тем самым экономя время на загрузку образа в память. Механизм этот реализуется исключительно компиляторами/линкерами и никакого отношения к загрузчику не имеет, однако в PE-заголовке есть директория IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, указывающая на данные отложенного импорта. Не знаю, используется ли это линкером и собранной программой, но загрузчику определенно пофиг. Но лучше оставим эту директорию, не будем ее обнулять. Уберем строку

    image.remove_directory(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);

Читать дальше »

Print This Post Пишем упаковщик по шагам. Шаг восьмой. DLL и экспорты.

Среда, 26. Сентябрь 2012
Раздел: Assembler, C/C++, Windows, Для новичков, автор:

Предыдущий шаг здесь.

Наш упаковщик уже умеет все, кроме одной вещи - упаковки бинарников, имеющих экспорты. Это, в частности, абсолютное большинство DLL-файлов и OCX-компоненты. Некоторые exe-файлы также имеют экспорты. Наш упаковщик должен пересобрать таблицу экспортов и расположить ее в доступном месте, чтобы загрузчик мог ею воспользоваться.

Пока что можно немного расслабиться - в упаковщике кода добавится совсем немного (в распаковщике, в общем-то, тоже, но он будет на ассемблере).

Читать дальше »

Print This Post Пишем упаковщик по шагам. Шаг седьмой. Релокации.

Вторник, 25. Сентябрь 2012
Раздел: C/C++, Windows, Для новичков, автор:

Предыдущий шаг здесь. Там, кстати, имелась ошибка в коде, я ее поправил. Она проявлялась, когда у файла было больше одного TLS-коллбэка.

Появилась новая версия библиотеки для работы с PE-файлами (0.1.7). Перекачайте и пересоберите ее.

Перейдем к следующей немаловажной части многих PE-файлов - релокациям. Они используются, когда невозможно загрузить образ по указанному в заголовке базовому адресу. Преимущественно такое поведение характерно для DLL-файлов (они в принципе без релокаций не могут нормально работать). Представьте, что exe-файл грузится по адресу 0x400000. Этот exe-файл грузит DLL, которая также грузится по этому адресу. Адреса совпадают, и загрузчик будет искать релокации у DLL-файла, потому что он грузится вторым после exe. И если релокаций не будет, то загрузка не пройдет.

Сами релокации - это просто набор таблиц с указателеми на DWORD'ы, которе загрузчик должен пересчитать, если образ загружается по адресу, отличному от базового. Типов релокаций много, но реально в x86 (PE) используются только два: IMAGE_REL_BASED_HIGHLOW = 3 и IMAGE_REL_BASED_ABSOLUTE = 0, причем второй ничего не делает, а нужен только для выравнивания таблиц релокаций.

Сразу скажу, что загрузчик exe-файлы грузит практически всегда по базовому адресу, не применяя релокации. DLL наш упаковщик паковать пока не умеет, поэтому для теста упаковки релокаций мы должны создать exe-файл с некорректным базовым адресом, и тогда загрузчик будет вынужден этот файл в памяти переместить. Я тут не буду приводить исходный код проекта для теста, вы найдете его в солюшене в конце статьи. Базовый адрес загрузки (Linker - Advanced - Base Address) я выбрал 0x7F000000.

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

Чтобы дать загрузчику знать о том, что у нашего файла есть релокации, делать ничего и не надо - у нас еще от оригинального файла остались нужные флаги, выставленные в заголовках PE-файла. Однако, нам нужно знать, по какому адресу файл загрузился.

Читать дальше »

Print This Post Пишем упаковщик по шагам. Шаг шестой. TLS.

Пятница, 21. Сентябрь 2012
Раздел: Assembler, C/C++, Windows, Для новичков, автор:

Предыдущий шаг здесь.

Появилась новая версия библиотеки для работы с PE-файлами (0.1.5). Перекачайте и пересоберите ее.

Пришло время заняться обработкой такой важной вещи, как Thread Local Storage (TLS) - локальной памяти потока. Что она из себя представляет? Это небольшая структура, которая говорит загрузчику PE-файлов о том, где находятся данные, которые должны быть выделены в памяти для каждого потока. Загрузчиком также производится вызов функции TlsAlloc, и значение, возвращенное ей, записывается по адресу, также указанному в этой структуре (называется это индексом). Кроме того, эта же структура может содержать адрес массива, хранящего набор коллбэков (адресов функций), которые будут вызваны загрузчиком при загрузке файла в память или при создании нового потока в процессе.

С TLS, признаться честно, все будет несколько хардкорнее, чем с остальным, так что приготовьтесь и напрягите мозг. Мой прошлый упаковщик TLS-коллбэки не поддерживал, трусливо выдавая сообщение о том, что они есть, но не обрабатываются. В принципе, поведение разумное, так как TLS-коллбеки имеют в основном всякие странные файлы, использующие эту вещь как антиотладочный прием. Ни один штатный линкер, вроде линкера от Майкрософт или Борланд, не поддерживают создание TLS-коллбэков. Тем не менее, для создания годного упаковщика мы их поддержку запилим.

Читать дальше »

Print This Post Пишем упаковщик по шагам. Шаг пятый. Ресурсы.

Среда, 19. Сентябрь 2012
Раздел: C/C++, Windows, Для новичков, автор:

Предыдущий шаг здесь.

Пора усовершенствовать наш упаковщик. Он уже способен упаковывать и запускать самые простые бинарники, имеющие лишь таблицу импорта. Бинарники с экспортами, ресурсами, TLS, DLL с релокациями ему пока что не под силу. Нужно над этим работать. Для начала сделаем обработку второй по важности вещи после импортов - директории ресурсов.

Читать дальше »

Print This Post Пишем упаковщик по шагам. Шаг четвертый. Запускаем.

Вторник, 18. Сентябрь 2012
Раздел: C/C++, Windows, Для новичков, автор:

Предыдущий шаг: здесь.

Появилась новая версия библиотеки для работы с PE-файлами (0.1.4). Перекачайте и пересоберите ее.

Итак, из прошлых шагов мы имеем работающий упаковщик и базовый распаковщик, который пока что ничего не делает. В этом шаге мы добьемся запуска простых упакованных программ (которые не имеют ничего, кроме таблицы импорта и, возможно, релокаций). Первое, что нужно сделать в распаковщике помимо разархивирования данных - это поправить таблицу импорта оригинального файла. Обычно это делает загрузчик, но сейчас для сжатого файла роль загрузчика играем мы.

Читать дальше »

Print This Post Пишем упаковщик по шагам. Шаг третий. Распаковываем.

Понедельник, 17. Сентябрь 2012
Раздел: Assembler, C/C++, Windows, Для новичков, автор:

Предыдущий шаг здесь.

Идем дальше! Пришло время написать распаковщик, именно этим мы начнем заниматься в этом шаге. Обрабатывать исходную таблицу импорта мы пока не будем, так как и в этом уроке нам будет, чем заняться.

Начнем мы вот с чего. Для работы распаковщика нам стопроцентно потребуются две WinAPI-функции: LoadLibraryA и GetProcAddress. В своем старом упаковщике я писал стаб распаковщика на MASM32 и вообще не создавал таблицу импорта. Я искал адреса этих функций в ядре, что несколько сложно и хардкорно, кроме того, это может вызвать неиллюзорные подозрения у антивирусов. Давайте в этот раз создадим обычную таблицу импортов и сделаем так, чтобы загрузчик сам нам сообщил адреса этих функций! Разумеется, набор из двух этих функций в таблице импорта так же подозрителен, как и полное их отсутствие, но ничто нам не мешает в будущем добавить еще другие левые случайные импорты из различных DLL-файлов. Куда загрузчик будет записывать адреса этих двух функций? Пора расширить нашу структуру packed_file_info!
Читать дальше »

Print This Post Пишем упаковщик по шагам. Шаг второй. Пакуем.

Воскресенье, 16. Сентябрь 2012
Раздел: C/C++, Windows, Для новичков, автор:

Предыдущий шаг здесь

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

И мы продолжаем написание собственного упаковщика. В этом шаге пора переходить непосредственно к упаковке PE-файла. Я достаточно давно выкладывал простенький упаковщик, который был малоэффективным по двум причинам: во-первых, он использовал стандартные Windows-функции для упаковки и распаковки данных, обладающие достаточно низкой степенью сжатия и скоростью, во-вторых, паковались все секции PE-файла по отдельности, что не очень-то оптимально. В этот раз я сделаю по-другому. Мы будем считывать данные всех секций сразу, слеплять их в один кусок и упаковывать. В результирующем файле, таким образом, будет только одна секция (на самом деле две, потом поясню, почему), в которой мы сможем разместить и ресурсы, и код распаковщика, и сжатые данные, и вспомогательные таблицы. Мы получаем некоторый выигрыш, потому что не нужно тратить размер на файловое выравнивание, кроме того, алгоритм LZO явно более эффективен, чем RtlCompressBuffer, во всех отношениях.

Читать дальше »

Страница 1 из 212