Кабинетный хак. Патчим инсталляторы MSI, содержащие CAB-архивы

MSI — самый популяр­ный фор­мат инстал­ляторов. Для редак­тирова­ния таких уста­новоч­ных пакетов при­дума­но мно­го удоб­ных инс­тру­мен­тов, но они могут не сра­ботать, если инстал­лятор содер­жит набор CAB-архи­вов. Сегод­ня я рас­ска­жу, как пра­вить дан­ные внут­ри инстал­ляци­онных пакетов MSI в руч­ном режиме, если дру­гими спо­соба­ми сде­лать это не получа­ется.

В сво­их стать­ях я пери­оди­чес­ки под­нимаю тему раз­борки и пат­ча инстал­ляци­онных пакетов. На этот раз я хочу про­дол­жить тему, начатую в статье «Ло­маем инстал­лятор. Как обма­нуть инстал­лятор MSI методом для ленивых», — руч­ной пат­чинг нес­тандар­тных MSI-пакетов, редак­тировать которые тра­дици­онны­ми средс­тва­ми невоз­можно. В той статье мы раз­бирали прав­ку и замену уста­новоч­ного скрип­та, а сей­час поп­робу­ем поменять дан­ные в инстал­лиру­емых фай­лах.

Итак, нам нуж­но внес­ти изме­нения в инстал­лятор MSI, нап­ример заменить в нем один содер­жащий­ся в CAB-архи­ве файл дру­гим. В свя­зи с вос­тре­бован­ностью задачи умные люди при­дума­ли мно­жес­тво редак­торов MSI-пакетов раз­ной сте­пени прод­винутос­ти: Advanced Installer, Master Packager, Orka, MSI Editor и дру­гие. Из них лич­но я осо­бо выделил бы Master Packager: этот редак­тор, на мой взгляд, наибо­лее кор­рек­тно выпол­няет раз­борку, редак­тирова­ние и пересох­ранение MSI-пакетов раз­личной слож­ности.

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

При­чем разоб­рать пакет на сос­тавля­ющие его фай­лы и таб­лицы — как раз задача нес­ложная, проб­лемы начина­ются имен­но в про­цес­се переком­пиляции пат­ченно­го фай­ла. В прин­ципе, вытащить фай­лы из инстал­лятора (как я уже писал в статье «Ло­маем инстал­лятор. Как обма­нуть инстал­лятор MSI методом для ленивых») мож­но и без спец­средств — прак­тичес­ки любой нор­маль­ный архи­ватор (7-Zip, WinRAR и дру­гие) откры­вает MSI, как архив.

С помощью архи­вато­ра мож­но извлечь отту­да сос­тавля­ющие уста­новоч­ный пакет CAB-фай­лы, а они, в свою оче­редь, так­же откры­вают­ся мно­гими архи­вато­рами (собирать CAB из сос­тавля­ющих уме­ют такие прог­раммы, как PowerArchiver, ACDzip или спе­циали­зиро­ван­ный Cabarc). О том, как засунуть исправ­ленный CAB обратно в MSI, я рас­ска­жу поз­же.

В кон­це кон­цов, мож­но раз­ложить MSI на сос­тавля­ющие фай­лы, минуя про­межу­точ­ные CAB, прос­то запус­тив msiexec с клю­чом /a (при этом соз­дает­ся пап­ка со всем содер­жимым пакета MSI). Есть еще более прод­винутый спо­соб — нат­равить на инстал­ляци­онный пакет ути­литу с откры­тым исходным кодом msi2xml. Она запус­кает­ся сле­дующей коман­дой:

msi2xml -c files installation.msi

При этом в каталог files рас­паку­ются все фай­лы про­екта из сос­тавля­ющих его CAB, а так­же соз­дас­тся файл пол­ного опи­сания про­екта installation.msi со все­ми таб­лицами и логикой про­цес­са уста­нов­ки. С исполь­зовани­ем это­го фай­ла ста­новит­ся воз­можным соб­рать инстал­лятор коман­дой

xml2msi -m installation.xml

Та­кая низ­коуров­невая опе­рация помога­ет, если не работа­ют опи­сан­ные выше методы. Но, к сожале­нию, пересоб­ранный подоб­ным спо­собом инстал­лятор тоже не всег­да ока­зыва­ется работос­пособ­ным. Дей­стви­тель­но, быва­ют слу­чаи, ког­да авто­мати­зиро­ван­ные спо­собы не сра­баты­вают. Нап­ример, если у нас име­ется мно­готом­ный неп­рерыв­ный архив, в середи­не которо­го нуж­но поменять один файл, не тро­гая осталь­ную струк­туру. По тра­диции я рас­ска­жу, как руками про­пат­чить файл в CAB-архи­ве без его пол­ной перепа­ков­ки.

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

Рас­пишем эту струк­туру попод­робнее:

struct CFHEADER{ // 1 красный — сигнатура файла MSCF u1 signature[4] u4 reserved1 // 2 желтый — размер файла в байтах u4 cbCabinet u4 reserved2 // 3 зеленый — смещение до первой записи CFFILE u4 coffFiles u4 reserved3 // 4 голубой — minor-байт версии файла u1 versionMinor // 5 фиолетовый — major-байт версии файла u1 versionMajor // 6 белый — количество записей CFFOLDER в файле u2 cFolders // 7 черный — количество записей CFFILE в файле u2 cFiles // 8 оранжевый — флаги формата: 3 = 1 (cfhdrPREV_CABINET у файла есть предыдущий том) | 2 (cfhdrNEXT_CABINET у файла есть следующий том) u2 flags // 9 желтый — ID кабинета, один и тот же у всех томов архива u2 setID // 10 красный — порядковый номер данного тома u2 iCabinet; // Эти поля не обязательны и в архиве не присутствуют, поскольку бит 2 поля flags не установлен u2 cbCFHeader; u1 cbCFFolder; u1 cbCFData; u1 abReserve[]; // 11 зеленый — необязательное имя предыдущего тома, строка, заканчивающаяся 0, в нашем случае Data1.cab u1 szCabinetPrev[]; // 12 белый — необязательное имя предыдущего диска, в нашем случае пустое u1 szDiskPrev[]; // 13 фиолетовый — необязательное имя следующего тома, в нашем случае Data12.cab u1 szCabinetNext[]; // 14 белый — необязательное имя следующего диска, в нашем случае пустое u1 szDiskNext[];};

Что­бы поменять файл в архи­ве, надо для начала его най­ти. В этом пла­не нам инте­ресен мас­сив записей CFFILE. Каж­дая из записей в этом мас­сиве содер­жит дан­ные о фай­ле, хра­нящем­ся в архи­ве. На начало мас­сива ука­зыва­ет поле coffFiles. Пос­мотрим, как оно выг­лядит в реаль­ном фай­ле.

Рас­пишем под­робно и эту струк­туру:

struct CFFILE{ // 1 красный — размер распакованного файла в байтах u4 cbFile; // 2 желтый — смещение до начала файла в распакованном блоке folder u4 uoffFolderStart; // 3 зеленый — номер структуры CFFOLDER, содержащей данные файла в общей таблице folder’ов // В этом примере равно ifoldCONTINUED_FROM_PREV (0xFFFD) u2 iFolder; // 4 желтый — дата создания файла u2 date; // 5 фиолетовый — время создания файла u2 time; // 6 голубой — атрибуты файла u2 attribs; // 7 оранжевый — имя файла, заканчивающееся нулем u1 szName[];};

Нас­тало вре­мя пояс­нить нес­коль­ко момен­тов. Ты, видимо, уже обра­тил вни­мание, что не все содер­жащи­еся внут­ри CAB-архи­ва фай­лы име­ют свои ори­гиналь­ные име­на. Ска­жем так, прак­тичес­ки все фай­лы, содер­жащи­еся внут­ри архи­ва CAB, исполь­зуют некие про­межу­точ­ные «тех­ничес­кие» име­на, чтоб нам жизнь медом не казалась. В этом мож­но убе­дить­ся, открыв CAB-файл в любом под­держи­вающем дан­ный фор­мат архи­вато­ре. По сути, «тех­ничес­кие» име­на — это сок­ращен­ный GUID, который инстал­лятор прис­ваивает каж­дому фай­лу в пакете.

Источник: xakep.ru

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *