InstallShield изнутри. Препарируем виртуалку инсталлятора в x64dbg

В сегод­няшней статье мы под­робнее раз­берем работу вир­туаль­ной машины уста­нов­щика InstallShield в динами­ке при помощи нашего любимо­го отладчи­ка x64dbg на при­мере инстал­лятора одно­го тех­ничес­кого при­ложе­ния. warning

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

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

Прош­ло уже пять лет, мы мно­гому научи­лись, так что пора дос­тать эту тему из под­вала и про­дол­жить раз­бор, пока InstallShield сов­сем не потерял акту­аль­ность. Итак, у нас есть инстал­лятор при­ложе­ния, который при уста­нов­ке зап­рашива­ет дан­ные поль­зовате­ля и серий­ный номер.

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

Что­бы не засорять повес­тво­вание дуб­лирова­нием информа­ции из пре­дыду­щей статьи, опус­тим про­цесс поис­ка, извле­чения и деком­пиляции инстал­ляци­онно­го скрип­та setup.inx — в нашем слу­чае фай­лы setup.inx, data1.cab, data1.hdr и дру­гие лежат в явном виде рядом с уста­нов­щиком setup.exe. Для рас­шифров­ки и деком­пиляции скрип­та исполь­зуем ути­литу isDcc31.exe — это более новый ана­лог ути­литы isDcc, опи­сан­ной в пре­дыду­щей статье, а что поделать, годы летят! Тул­зу мож­но взять, нап­ример, из пос­ледней вер­сии па­кета UniExtract. Кста­ти, этим же инс­тру­мен­том, точ­нее, вхо­дящей в его сос­тав ути­литой IsXunpack.exe мож­но при необ­ходимос­ти рас­паковать и иссле­довать инстал­ляци­онный cab-архив, но нас пока что инте­ресу­ет инстал­ляци­онный скрипт setup.inx. Рас­шифро­выва­ем (unscramble) его сле­дующей коман­дой:

isdcc31.exe -u setup.inx

За­тем деком­пилиру­ем:

isdcc31.exe setup.inx.dec >setup.dec

На этом пред­варитель­ный этап закан­чива­ется и начина­ется про­цесс изыс­каний. Мы получи­ли при­мер­но мегабайт тек­сто­вого кода, в котором нет ни имен перемен­ных или про­цедур, ни тек­сто­вых строк, спо­соб­ных как‑то внес­ти ясность в понима­ние про­цес­са про­вер­ки и чте­ния серий­ного номера. Поэто­му схал­турить, как в пре­дыду­щем слу­чае, у нас не получит­ся — надо рас­чехлять отладчик и кро­пот­ливо раз­бирать вир­туаль­ную машину InstallShield.

Мы уже успе­ли изрядно под­натореть в этом неп­ростом деле, поэто­му дей­ству­ем по стан­дар­тной схе­ме. Ког­да уста­нов­щик висит на окне вво­да серий­ного номера и дан­ных поль­зовате­ля, под­клю­чаем­ся отладчи­ком x64dbg к про­цес­су setup.exe. Для начала поп­робу­ем про­верить самую оче­вид­ную гипоте­зу, что шитый код вир­туаль­ной машины в этот момент находит­ся в памяти про­цес­са. В этом слу­чае мож­но было бы пос­тавить точ­ку оста­нова на обра­щение к какому‑то извес­тно­му учас­тку это­го кода, что­бы отсле­дить его со сто­роны вир­туаль­ной машины.

К сожале­нию, поиск в памяти резуль­татов не дает, что наводит на пред­положе­ние: шитый код не интер­пре­тиру­ется непос­редс­твен­но, а с ним при заг­рузке про­исхо­дит какая‑то обра­бот­ка, воз­можно, даже JIT-ком­пиляция, как в слу­чае с IL или JVM. Конеч­но, таких пыт­ливых спе­циалис­тов, как мы, это досад­ное пре­пятс­твие не пуга­ет, поэто­му поп­робу­ем подой­ти к решению проб­лемы со сто­роны WinAPI.

Ре­зон­но полагая, что для чте­ния тек­ста из поля вво­да обыч­но исполь­зует­ся фун­кция user32.GetDlgItem, ста­вим бряк на нее и сме­ло жмем Next. Это очень рас­простра­нен­ная фун­кция, поэто­му при­ходит­ся прос­кочить с десяток неин­терес­ных нам сис­темных вызовов из раз­личных обра­бот­чиков событий, пока не натыка­емся на инте­ресу­ющее нас мес­то пря­мого обра­щения из InstallShield, стек вызовов которо­го выг­лядит так.

Наш нат­рениро­ван­ный глаз сра­зу рас­позна­ет рекур­сивный вызов под­прог­рамм вир­туаль­ной машины (пов­торя­ющиеся пат­терны в сте­ке выделе­ны стрел­ками). Вдо­бавок ста­ло понят­но, в какой имен­но биб­лиоте­ке эта самая вир­туаль­ная машина сидит, — это модуль issetup.dll, в нашем слу­чае лежащий в катало­ге рядом с setup.exe. Прав­да, хит­рые раз­работ­чики неук­люже запако­вали ее при помощи PECompact, но это даже смеш­но: не отвле­каясь на поиск рас­паков­щика, тупо дам­пим его при помощи Scylla и скар­мли­ваем сдам­плен­ное дизас­сем­бле­ру IDA.

Те­перь начина­ем вдум­чиво изу­чать стек вызовов, опи­раясь на вос­ста­нов­ленный в IDA код issetup.dll. Бук­валь­но на вто­ром свер­ху вызове нас ждет инте­рес­ное откры­тие — user32.GetDlgItem непос­редс­твен­но вызыва­ется из фун­кции, ссыл­ка на которую находит­ся в vftable такого вида.

Для даль­нейше­го понима­ния этой таб­лицы поп­робу­ем сде­лать то, что мы дол­жны были сде­лать еще в пре­дыду­щей статье, но впо­пыхах прос­кочили из‑за баналь­ной лени, а имен­но — разоб­рать сис­тему команд шитого кода интер­пре­тато­ра инстал­ляци­онно­го скрип­та. В прош­лый раз мы, пом­нится, огра­ничи­лись эмпи­ричес­ким нахож­дени­ем опко­дов 0xD (срав­нение на экви­вален­тность) и 0xE (срав­нение на неравенс­тво). На этот раз мы коп­нем пог­лубже и поп­робу­ем вытащить пол­ную сис­тему команд.

Для это­го нам понадо­бит­ся какой‑нибудь деком­пилятор INX в исходных кодах. Мож­но исполь­зовать исходни­ки нашего isDcc c гитах­ба, но, по‑моему, гораз­до удоб­нее и наг­ляднее исполь­зовать для это­го дру­гой деком­пилятор — InstallScript Decompiler, который уме­ет не толь­ко деком­пилиро­вать, но еще и дизас­сем­бли­ровать INX-код. Впро­чем, на мой взгляд, реаль­но деком­пилиро­вать скрип­ты все же удоб­нее isDcc, потому что InstallScript Decompiler изрядно глю­кав.

В ито­ге, покопав­шись в коде InstallScript Decompiler, мы находим модуль Action, содер­жащий спи­сок команд интер­пре­тато­ра (их так и называ­ют — Actions) со сво­ими опко­дами. Их не так мно­го, поэто­му при­веду здесь таб­лицу целиком:

Эм­пиричес­ки подоб­ранные нами в пре­дыду­щей статье акции с опко­дами 0xD (13) и 0xE (14), соот­ветс­твен­но, явля­ются BinEq и BinNEq, что впол­не впи­сыва­ется в рас­смат­рива­емую схе­му. Еще нем­ного покурив исходни­ки деком­пилято­ров, при­ходим к при­мер­ному понима­нию струк­туры шитого кода инстал­ляци­онно­го скрип­та. Раз­берем ее на при­мере фун­кции с условным наз­вани­ем function0, бинар­ный код которой при­веден на сле­дующем рисун­ке.

Де­ком­пилиро­ван­ный в isDcc31 код этой фун­кции выг­лядит так:

function function0(pBool0) begin Label0: 008142:0006: pBool0 = 0; 00814E:0014: lString0 = lString4 ^ "MANUALS\setup.exe"; 00816C:0021: call function438(3,lString0); 00817A:0006: lNumber0 = number0; 008184:000D: lNumber0 = lNumber0 == 1; 008193:0004: if lNumber0 == false then goto label1 ; 00819F:0006: pBool0 = 1; Label1: 0081AD:0024: return; 0081BB:0026: end; end;

Для луч­шего понима­ния при­веду еще ее «дизас­сем­бли­рован­ный» с помощью InstallScript Decompiler код, но я пре­дуп­реждал, дизас­сем­блер там весь­ма спе­цифи­чен:

8137: v {CNumArg} -101 = 0 8137: v {CStrArg} -101 = v {CStrArg} 4 13 "MANUALSsetup.exe" 8137: Func_1107(3, v {CStrArg} -101) 8137: v {CNumArg} -102 = v {CVariantArg} 0 8137: v {CNumArg} -102 = v {CNumArg} -102 7 1 8137: If (else:1) v {CNumArg} -102 8137: v {CNumArg} -101 = 1 81ab: RETURN 81ab: EndFuncAction

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *