Обход защиты протектора Obsidium

Хакинг взлом icon

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

Еще по теме: Взлом программы защищенной про­тек­тором Enigma

Для тех кто не в теме, Obsidium, вместе с VMProtect и Themida счи­таются одними из самых надежных. Функционал тоже не хилый: вир­туали­зация, анти­отладка, обна­руже­ние VM, защита памяти, про­вер­ка целос­тнос­ти, защита импорта, свой API для интегра­ции в поль­зователь­скую прог­рамму и другие фишки, которые усложняют взлом программы.

Обход защиты Obsidium

Итак, у нас име­ется три­аль­ная вер­сия прог­раммы, которую DIE иден­тифици­рует сле­дующим обра­зом:

С пер­вого взгля­да оче­вид­но, что заг­ружать такое в дизас­сем­блер совер­шенно бес­полез­но: код силь­но сжат или зашиф­рован (на самом деле и то и дру­гое). Сек­ции пус­тые, из импорта име­ются в наличии все­го четыре фун­кции. Поп­робу­ем сра­зу заг­рузить ее в отладчик.

Вос­поль­зуем­ся x64dbg, ибо в нем есть прек­расные пла­гины Scylla и Scylla Hide, которые нам очень при­годят­ся. К при­меру, для того что­бы не палить дебаг­гер, в Scylla Hide сущес­тву­ет спе­циаль­ный про­филь, заточен­ный исклю­читель­но под Obsidium. Вклю­чаем его и начина­ем трас­сиров­ку.

Хож­дение по безум­ному коду про­тек­тора — доволь­но нуд­ное занятие и вдо­бавок небезо­пас­ное, ибо Obsidium, как и дру­гие «взрос­лые» защиты типа VMProtect, уме­ет кон­тро­лиро­вать вре­мя про­хож­дения опре­делен­ных учас­тков кода внут­ри себя. Скрыть собс­твен­ное при­сутс­твие нам не поможет никакая Scylla Hide. Вдо­бавок мы, по моему обык­новению, хотим все сде­лать мак­сималь­но быс­тро и с миниму­мом зат­рат, поэто­му прос­то поз­волим прог­рамме запус­тить­ся до появ­ления на экра­не основно­го окна.

Прог­рамма пре­вос­ходно запус­кает­ся и даже пре­рыва­ется — Scylla Hide прек­расно отра­баты­вает свое наз­начение. В этот момент мы видим рас­шифро­ван­ный и рас­пакован­ный модуль, который попыта­емся сдам­пить при помощи той же Scylla. Для это­го оста­нав­лива­емся где‑нибудь внут­ри кода основно­го модуля и отно­ситель­но дан­ного OEP дела­ем авто­поиск IAT. Пос­ле чего Get Imports выда­ет очень длин­ный спи­сок оши­бок, но при этом все‑таки стро­ит таб­лицу импорта.

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

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

Поп­робу­ем раз­вить пос­леднюю идею тех­ничес­ки. Как я уже говорил, у модуля все­го четыре импорти­руемые фун­кции: GetModuleHandleA, MessageBoxA, RegOpenKeyExA и ImageList_Add. Для начала уста­новим точ­ку оста­нова на GetModuleHandleA. Прог­рамма запус­кает­ся, и точ­ка оста­нова даже отра­баты­вает пару раз, одна­ко пос­ле это­го отладчик зацик­лива­ется с бес­конеч­ными попыт­ками исклю­чения по ACCESS_VIOLATION. Защита явно спа­лила отладчик, и ScyllaHide уже не помога­ет.

От­рицатель­ный резуль­тат не отби­вает у нас охо­ту работать в этом нап­равле­нии, и мы по ста­рой памяти про­буем пов­торить этот трюк с ReadFile. На удив­ление, бряк нес­коль­ко раз впол­не эффектив­но сра­баты­вает и не зацик­лива­ется. Смот­рим вни­матель­но на стек вызовов — пер­вые нес­коль­ко раз там содер­жится пол­ная каша, одна­ко в опре­делен­ный момент появ­ляет­ся осмыслен­ная пос­ледова­тель­ность вло­жен­ных вызовов, сни­зу начина­ющаяся c вызова потока нашей прог­раммы из BaseThreadInitThunk и закан­чива­ющаяся свер­ху в нед­рах модулей user32 и gdi32full. Это наша прог­рамма впер­вые выз­вала LpkDrawTextEx, а он полез за шриф­том на диск.

Вни­матель­но изу­чаем рас­положен­ный в самом низу спис­ка код про­цеду­ры, выпол­няющей­ся в этом потоке, и находим начало этой самой про­цеду­ры — 0xB634EC. Итак, мы наш­ли потен­циаль­ную точ­ку вхо­да и теперь у нас есть за что зацепить­ся в про­цес­се отладки.

Пе­реза­пус­каем прог­рамму, в окно дам­па выводим сос­тояние памяти по это­му адре­су. Как и сле­дова­ло ожи­дать, память девс­твен­но чис­та и запол­нена нулями. Запус­каем прог­рамму, сно­ва оста­нав­лива­ясь на ReadFile. Бук­валь­но через пару бря­ков видим по адре­су 0xB634EC готовый рас­пакован­ный код. Радос­тно ста­вим на него бряк, убрав его с ReadFile для ясности. Жмем на про­дол­жение, и вот мы оста­нав­лива­емся на самом вхо­де в рас­пакован­ную и рас­шифро­ван­ную прог­рамму.

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

Начина­ем пошаго­во трас­сировать прог­рамму и бук­валь­но через нес­коль­ко команд натыка­емся на подоз­ритель­ную про­цеду­ру 0xB62030, вызыва­ющую внут­ри себя игно­риру­емую попыт­ку исклю­чения при­мер­но в таком кон­тек­сте:

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

Фак­тичес­ки у нас не оста­лось никаких сом­нений — имен­но эта кон­крет­ная про­цеду­ра 0xB62030 вызыва­ет про­вер­ку лицен­зии Obsidium и запол­няет информа­цией о ней струк­туру по адре­су, на который ука­зыва­ет ds:[C0AEAC].

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

Ес­ли вдум­чиво покопать в IDA сдам­плен­ную прог­рамму, таких ссы­лок на Obsidium мож­но най­ти в коде дос­таточ­но мно­го в мес­тах про­вер­ки лицен­зии. Каж­дый такой вызов уни­кален (лич­но я с ходу не нашел пов­торя­ющих­ся, даже если они выпол­няют оди­нако­вые фун­кции), его код вир­туали­зован и снаб­жен собс­твен­ным анти­отладчи­ком.

Конеч­но, мож­но повозить­ся и осво­бодить код от всех этих вызовов (хотя это тоже не всег­да дос­тижимо, ибо теоре­тичес­ки Obsidium уме­ет вир­туали­зиро­вать отме­чен­ные поль­зовате­лем бло­ки основно­го кода прог­раммы), но про­ще все‑таки сде­лать лоадер и инлайн‑патч, кому как боль­ше нра­вит­ся.

Од­нако я обе­щал рас­крыть метод обхо­да защиты для самых ленивых, безо вся­кого отладчи­ка, ревер­са и ковыря­ния кода. Тот, кто читал мои статьи о Enigma, навер­ное, уже догадал­ся, о чем речь. А речь идет о сбро­се три­ала Obsidium внеш­ними средс­тва­ми — при помощи толь­ко ProcMon, RegEdit и уме­лых рук.

До­пус­тим, у нас есть защищен­ная Obsibium прог­рамма, работа­ющая огра­ничен­ный пери­од, который очень хочет­ся прод­лить. Желатель­но навеч­но. Одна­ко лезть для это­го в отладчик и сни­мать защиту пол­ностью нам лень. При­выч­но запус­каем ProcMon и изу­чаем лог обра­щений прог­раммы к реес­тру при заг­рузке.

Естес­твен­но, мгно­вен­но нашему взо­ру пред­ста­нет огромная прос­тыня, раз­мер которой повер­гает в уны­ние. Одна­ко, на счастье, авто­ры Obsidium (так же как и авто­ры Enigma) осо­бой фан­тази­ей не отли­чают­ся, и по поис­ку Obsidium тут же находит­ся обра­щение к клю­чу (дан­ные GUID уни­каль­ны для каж­дой прог­раммы):

Без осо­бой надеж­ды уда­ляем ключ из реес­тра и переза­пус­каем прог­рамму — естес­твен­но, сбро­са три­ала не про­исхо­дит. Но, как я уже говорил, соз­датели этих защит осо­бой фан­тази­ей не отли­чают­ся, поэто­му пред­полага­ем, что они под­креп­ляют реес­тро­вую про­вер­ку каким‑то хит­ро спря­тан­ным фай­ликом на дис­ке. Начина­ем изу­чать лог обра­щения к фай­ловой сис­теме, и тут нас ждет сюр­приз!

Ав­торы Enigma ока­зались даже более ори­гиналь­ными: они, как ты пом­нишь, хра­нили информа­цию о регис­тра­ции в фай­ле с неп­роиз­носимым наз­вани­ем во вре­мен­ной сис­темной пап­ке. Авто­ры Obsidium для это­го завели спе­циаль­ную под­папоч­ку име­ни себя в под­катало­ге RoamingAppData текуще­го поль­зовате­ля. Путь к фай­лу выг­лядит при­мер­но так (GUID тот же, что и в реес­тре).:

Уда­ляем этот фай­лик вмес­те с вет­кой реес­тра — бин­го, три­ал сбро­шен!

Заключение

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

Еще по теме: Отладка программ с помощью WinDbg

Дима (Kozhuh)

Эксперт в кибербезопасности. Работал в ведущих компаниях занимающихся аналитикой компьютерных угроз. Анонсы новых статей в Телеграме.

Добавить комментарий