WiX-фикс. Исследуем инсталлятор Windows Installer XML Toolset
Инсталляторы бывают разные: и попроще, и посложнее. Иногда среди них попадаются уж совсем навороченные — их исследование превращается в настоящее приключение. Сегодня мы рассмотрим именно такой случай: разберемся, как устроен изнутри инсталлятор WiX.
Эта тема обширна и неисчерпаема, поскольку для удобства и простоты над базовыми системами инсталляции были созданы надстройки, над которыми разработчики соорудили другие надстройки, и так далее. В итоге сам инсталлятор по сложности и красоте интерфейса (к сожалению, и размеру) иногда не уступает инсталлируемому пакету, а разборка и реверс его вызывает профессиональный интерес и доставляет настоящее удовольствие. Пример исследования одного из подобных инсталляторов мы и рассмотрим в сегодняшней статье.
Итак, у нас имеется инсталлятор для некоего технического пакета, оформленный в виде весьма увесистого (размером в несколько гигабайтов) самодостаточного EXE-файла. Интерфейс у инсталлятора продвинутый и стильный, он не похож на визуальное представление ни одного из встречавшихся нам до этого стандартных инсталляционных пакетов. Здесь много элементов управления, которые шустро переключают странички по мере заполнения, приближая начало установки программы. Но вот беда — в определенный момент инсталлятор начинает требовать лицензию, без которой упорно не желает переходить на следующий экран.
Мы уже встречались с подобным поведением программ установки в предыдущих статьях. Однако в этот раз выяснилось, что программа содержит все неприятные фичи, присущие инсталляторам:
- Из весьма увесистого EXE-файла исполняемым является только крохотный и совершенно неинформативный загрузчик.
- Владелец процесса тоже относительно небольшой EXE-файл, запущенный из системной папки временных файлов.
- Перед нами не хорошо изученные msiexec или InstallShield, а что‑то иное.
Попытавшись приаттачиться к запущенному процессу при помощи x64dbg, мы видим, что код явно скомпилирован в процессе исполнения. Этот код до боли напоминает .NET, что косвенно подтверждается наличием в памяти процесса загруженных дотнетовских библиотек (clr.dll, clrjit.dll, mscoree.dll и так далее). Однако ни сам инсталлятор, ни исполняемый файл из временной папки .NET-приложениями не являются.
Дотнетовский отладчик dnSpy хоть и распознает процесс как родной и даже успешно коннектится к нему, но никакого дотнетовского кода при этом не показывает и трассировать приложение не дает. Необходимую зацепку нам дает Detect It Easy.
Очевидно, и сам инсталлятор, и временный файл распознаются DIE как WiX Toolset installer с оверлеем Microsoft Cabinet File (CAB). Про CAB, в принципе, можно было бы догадаться по сигнатуре 4D534346 (MSCF) у оверлеев, но это нам еще пригодится чуть позже.
А пока попробуем вспомнить, что такое WiX и чем именно это знание может помочь в дальнейшем анализе приложения. Это слово из трех букв расшифровывается как Windows Installer XML Toolset и представляет собой (как следует из названия) набор инструментов для создания инсталляционных пакетов на основе XML-описания. Если тебе интересно узнать поподробнее про этот пакет, ты можешь ознакомиться с его особенностями.
Хоть автор и плачется по поводу плохой документированности пакета, информация о нем в открытом доступе есть, и много. Возможно, я когда‑нибудь и сам напишу статью об особенностях внутреннего формата этого типа инсталляторов, но тема нашей сегодняшней статьи лежит в иной плоскости.
Cуть в том, что основное достоинство технологии WiX в ее расширяемости — несмотря на наличие своих собственных интерфейсных шаблонов, в ней имеется возможность подключить кастомный интерфейс пользователя, написанный на .NET (Custom Bootstrapper Application). Что мы и наблюдаем в нашем приложении.
Перед нами же стоит прямо противоположная задача — разобраться с существующим приложением. Для этого его нужно хотя бы разобрать на составные файлы, и, к счастью, WiX предоставляет для этого необходимый инструментарий. Для начала заходим на GitHub и качаем оттуда архив wix311-binaries.zip. Он содержит набор утилит командной строки для работы с WiX-инсталляторами, из которого для нас представляет интерес dark.exe — экстрактор файлов из WiX-архива. Пользоваться им не просто, а очень просто: надо всего‑навсего запустить его из командной строки:
dark.exe <имя нашего инсталлятора.exe> -x <имя каталога, в который будут распаковываться файлы пакета>
После отработки команды в указанном нами каталоге будет создана сложная система подкаталогов с инсталлируемыми MSI-пакетами, ресурсами и библиотеками, необходимыми для установки. К великому сожалению, это работает только в одну сторону. Фарш невозможно провернуть назад, и собрать обратно из этой солянки инсталляционный EXE будет совсем не просто, но мы подумаем об этом позже. А сейчас нас интересуют ресурсы WiX, которые находятся в подпапке UX в корне распакованного каталога. В частности, особый интерес представляет файл BootstrapperCore.config, в секции wix.bootstrapper которого (как мы узнали из прочитанной документации) и содержится имя Custom Bootstrapper Application:
<?xml version="1.0" encoding="utf-8" ?><configuration> <configSections> <sectionGroup name="wix.bootstrapper" type="Microsoft.Tools.WindowsInstallerXml.Bootstrapper.BootstrapperSectionGroup, BootstrapperCore"> <section name="host" type="Microsoft.Tools.WindowsInstallerXml.Bootstrapper.HostSection, BootstrapperCore" /> </sectionGroup> </configSections> <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" /> </startup> <wix.bootstrapper> <host assemblyName="Xeam.VisualInstaller"> <supportedFramework version="v4\Full" /> <supportedFramework version="v4\Client" /> </host> </wix.bootstrapper></configuration>
Похоже, мы вышли на следующий уровень, ибо Xeam Visual Installer — это еще одна надстройка над WiX. А именно система создания визуальных инсталляторов для тех, кому не нравятся «скучные обои» встроенных в WiX шаблонов инсталляционных интерфейсов. Покурив тут же нагугленное руководство по данному пакету, обнаруживаем, что разбираемый нами инсталлятор — полностью шаблонное приложение Xeam Visual Installer, заточенное вот под такой шаблон.
Создатели Xeam Visual Installer избавили своих пользователей от рисования и программирования интерфейса. Им нужно всего‑навсего написать мелкие процедуры валидации вводимых параметров и переходов между экранами. Загрузив в dnSpy библиотеку Bootstrapper.Ui.dll из каталога UX и немного изучив код класса Xeam.VisualInstaller.MainWindow, находим вот такой валидатор лицензии.
Несмотря на кажущуюся сложность, мы уже научились закорачивать подобные методы простым двухбайтовым патчем return 1 по смещению файла 0x2750.
Проверить действенность выбранного метода достаточно просто. Приаттачившись с помощью x64dbg к инсталлятору и найдя поиском по шаблону искомый IL-код, патчим нужные два байта в памяти и убеждаемся, что после окна проверки лицензии переход к следующему окну выполняется без проблем. На этом месте можно было бы и остановиться, установив пакет столь экзотическим способом. Можно также написать лоадер, на лету патчащий процесс инсталлятора, но мы пойдем самым сложным путем: будем патчить инсталляционный пакет.
Как я уже писал, после того как мы разобрали WiX-пакет на составляющие при помощи dark.exe, нельзя так просто взять и собрать пропатченный пакет обратно. Поправить пару байтов в самом пакете тоже нельзя, там компрессия. Поэтому придется покопаться во внутренней структуре пакета, которая, к сожалению, не документирована вообще.
Однако в мире достаточно любопытных энтузиастов, которые озадачивались этой проблемой до нас. В принципе, несложно было бы и самим разобраться во всем, минут пятнадцать погоняв код загрузчика в отладчике, но не буду загромождать повествование подробностями, как получать информацию, а перейду сразу к результату. Если открыть инсталлятор в каком‑нибудь редакторе EXE (например, Hiew), то в его заголовке мы увидим следующие секции:
Number Name VirtSize RVA PhysSize Offset Flag 1 .text 00048FF7 00001000 00049000 00000400 60000020 2 .rdata 0001F760 0004A000 0001F800 00049400 40000040 3 .data 000016FC 0006A000 00000A00 00068C00 C0000040 4 .wixburn 00000038 0006C000 00000200 00069600 40000040 5 .rsrc 0007DECC 0006D000 0007E000 00069800 40000040 6 .reloc 00003DD0 000EB000 00003E00 000E7800 42000040
Обрати внимание на секцию .wixburn, специфическую для каждого WiX-инсталлятора. Именно по ней, кстати, Detect It Easy и определяет принадлежность приложения к этому типу. Перейдя к указанной секции, мы видим такую последовательность байтов.
Она интерпретируется следующим образом:
0 DD Magic number 0xF14300 4 DD Version 2 8 DB 16 DUP (?) Bundled GUID {3F10D12E-D4C3-4DE6-EDBD-079DBB81B6A9}0x18 DD Engine (stub) size 0xEB600 0x1C DD Original checksum 0x1F45150 0x20 DD Original signature offset 0x1F383A8 0x24 DD Original signature size 0x2D78 0x28 DD Container Type (1 = CAB) 1 0x2C DD Container Count 2 0x30 DD Byte count of manifest + UX container 0x1E4CDA2 0x34 DD Byte count of attached container 0x4363C6C9
Попробуем разобраться, что представляют собой эти поля. Engine (stub) size — это размер исполняемого модуля, после него по смещению 0xEB600 от начала файла начинается оверлей. Он представляет собой содержимое каталога UX, то есть ресурсы инсталлятора WiX и Xeam Visual Installer, которые и содержат пропатченный нами файл Bootstrapper.Ui.dll.
Как я уже упоминал, этот оверлей имеет сигнатуру 4D534346 (MSCF), то есть упакован в CAB-архив, что и подтверждает строка Container Type=1. Сразу за UX container по смещению Original signature offset=0x1F383A8 от начала файла начинается подпись файла. После нее по смещению 0x1F383A8+0x2D78=0x1F3B120 от начала файла расположен самый большой attached container, содержащий в себе все файлы инсталлируемого пакета. Этот архив тоже имеет сигнатуру MSCF и тип CAB.
Мораль всех этих изысканий такова: если мы хотим, чтобы инсталлятор корректно работал после перепаковки и замены CAB-контейнера, мы должны поправить как минимум смещения в заголовке секции .wixburn.
Попробуем проделать эту операцию на практике.
Для начала откроем наш файл в редакторе WinHex и разобьем его на три части: 0-0xEB600 — исполняемый образ, 0xEB600-0x0x1F383A8 — UX container и хвост от 0x1F383A8 до конца файла — сигнатура + attached container.
Теперь нам надо заменить в UX container файл Bootstrapper.Ui.dll исправленным. Эта операция на самом деле не такая уж и простая. Распаковывать CAB-файлы умеют практически все архиваторы, однако внутреннее содержимое у них неструктурированное, файлы обезличены, причем они часто имеют номерные названия. Вдобавок мало кто из известных архиваторов умеет собирать CAB-архивы. 7-Zip и WinRAR, к примеру, не умеют, а вот экзотический PowerArchiver умеет, им и воспользуемся.
Выкрутиться с именами файлов тоже довольно просто: поскольку размеры файлов уникальные, достаточно упорядочить эти самые файлы по величине и посмотреть, какой файл в архиве соответствует размером нашему Bootstrapper.Ui.dll. В нашем случае это u5 — переименовываем исправленный Bootstrapper.Ui.dll в u5, закидываем его вместо прежнего и снова собираем CAB при помощи PowerArchiver. Размер при перепаковке, естественно, изменился, поэтому после того, как мы снова склеили три части, поправим заголовок секции .wixburn. Как показывает практика, достаточно поменять Original signature offset для того, чтобы инсталлятор корректно отработал.
В заключение хочу выразить надежду, что полученная в этой статье информация будет использоваться не в деструктивных и криминальных целях, а исключительно на благо общества, например для локализации и совершенствования пакетов без полного пересоздания дистрибутива. А кому‑то, возможно, это откроет путь для создания инсталлятора собственной программы с оригинальным и продвинутым интерфейсом.