Хакер - Obsidium fatality. Обходим триальную защиту популярного протектора

Хакер - Obsidium fatality. Обходим триальную защиту популярного протектора

hacker_frei

https://t.me/hacker_frei

МВК

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

WARNING

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

На при­мере прог­раммы, исполь­зующей одну из пос­ледних вер­сий Obsidium, поп­робу­ем разоб­рать сла­бые и силь­ные мес­та этой защиты. В статье, пос­вящен­ной дру­гому популяр­ному про­тек­тору — Enigmа, я рас­ска­зывал о популяр­ном у раз­работ­чиков спо­собе защиты при­ложе­ния: поль­зовате­лю пре­дос­тавля­ется озна­коми­тель­ный пери­од работы с прог­раммой — мож­но исполь­зовать ее опре­делен­ное вре­мя или выпол­нить опре­делен­ное количес­тво запус­ков. Есть такая воз­можность и у Obsidium. Ска­чав с офи­циаль­ного сай­та демовер­сию защиты (пос­ледняя акту­аль­ная вер­сия — 1.7.3.3), запус­каем ее.

Ин­терфейс Obsidium

Для начала, как нам совету­ют, соз­дадим новый про­ект и в его нас­трой­ках ука­жем защиту при­ложе­ния в виде три­аль­ного пери­ода. Для это­го жмем в левой вер­тикаль­ной панели гра­фичес­кую кноп­ку с шес­терен­ками Settings и откры­ваем вто­рую спра­ва вклад­ку Time trial.

Нас­тра­иваем три­аль­ный пери­од для прог­раммы

Как видишь, здесь при­сутс­тву­ет такой же джентль­мен­ский набор фун­кций, как и в Enigma:

  • три­ал до опре­делен­ной даты;
  • три­ал на опре­делен­ное количес­тво дней;
  • три­ал на опре­делен­ное количес­тво запус­ков.

Нем­ного неоче­вид­ным кажет­ся пред­назна­чение четырех полей вво­да в левой ниж­ней час­ти окна под общим заголов­ком User-defined trial counters. На самом деле, как я уже говорил, для тех при­веред­ливых поль­зовате­лей, которым мало стан­дар­тной защиты по вре­мени или количес­тву запус­ков, Obsidium пред­лага­ет набор сво­их фун­кций для интегра­ции в прог­рамму. В качес­тве одно­го из вари­антов раз­работ­чик может уста­нав­ливать собс­твен­ные три­аль­ные счет­чики, кон­тро­лиру­емые пря­мо из кода во вре­мя выпол­нения прог­раммы. Это четыре целочис­ленные 16-бит­ные перемен­ные, сох­раня­емые в сис­теме до сле­дующе­го запус­ка прог­раммы, зна­чения которых мож­но читать фун­кци­ей int obsGetTrialCounter(DWORD dwCtrIdx) и дек­ремен­тировать фун­кци­ей bool obsDecTrialCounter(DWORD dwCtrIdx, short wValue). Эту фун­кцию мож­но исполь­зовать, нап­ример, если мы хотим, что­бы прог­рамма пос­ле десяти сох­ранений бло­киро­вала фун­кцию Save и про­дол­жала даль­ше работать как ни в чем не бывало, без даль­нейшей воз­можнос­ти сох­ранить файл на диск. Этот инс­тру­мен­тарий пре­дос­тавля­ет поль­зовате­лям более гиб­кие воз­можнос­ти управле­ния три­алом.

За­щита уста­нов­лена!

Пос­ле уста­нов­ки нуж­ных парамет­ров защиты жмем вто­рую сни­зу кноп­ку в левой панели с над­писью PROTECT, затем во вклад­ке Executables выбира­ем файл защища­емой прог­раммы и, наконец, нажима­ем кноп­ку Protect All. Теперь наше при­ложе­ние под защитой демовер­сии Obsidium, о чем нам будет напоми­нать раз­дра­жающее окош­ко при каж­дом ее запус­ке.

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

Obsidium v1.5.4.x - [ v1.6.x.x - 1.x.x ] - Obsidium Software - www.obsidium.de *ACM , Overlay : FE4711... Nothing detected

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

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

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

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

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

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

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

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

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

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

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

02DC091A | 0F0B | ud2 |

02DC091C | 0F0B | ud2 |

02DC091E | EB 01 | jmp 2DC0921 |

02DC0920 | 70 EB | jo 2DC090D |

02DC0922 | 0115 F7F0EB05 | add dword ptr ds:[5EBF0F7],edx |

02DC0928 | D2A4A7 872CEB1E | shl byte ptr ds:[edi+1EEB2C87],cl |

02DC092F | EB 04 | jmp 2DC0935 |

02DC0931 | BD A4FCBFEB | mov ebp,EBBFFCA4 |

02DC0936 | 05 D21DA654 | add eax,54A61DD2 |

02DC093B | 5D | pop ebp |

02DC093C | 8B5424 30 | mov edx,dword ptr ss:[esp+30] |

02DC0940 | EB 03 | jmp 2DC0945 |

02DC0942 | A9 CBF2EBBA | test eax,BAEBF2CB |

02DC0947 | EB 04 | jmp 2DC094D |

02DC0949 | A2 29F963EB | mov byte ptr ds:[EB63F929],al |

02DC094E | 03D8 | add ebx,eax |

02DC0950 | A2 55EBE1EB | mov byte ptr ds:[EBE1EB55],al |

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

00B63567 | 8B15 ACAEC000 | mov edx,dword ptr ds:[C0AEAC] |

00B6356D | 8B12 | mov edx,dword ptr ds:[edx] |

00B6356F | 8B92 1C030000 | mov edx,dword ptr ds:[edx+31C] |

00B63575 | 8B92 94010000 | mov edx,dword ptr ds:[edx+194] | [edx+194]:L"Unregistered trial version\r\nYou have 30 day(s) left"

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

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

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

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

До­пус­тим, у нас есть защищен­ная Obsibium прог­рамма, работа­ющая огра­ничен­ный пери­од, который очень хочет­ся прод­лить. Желатель­но навеч­но. Одна­ко лезть для это­го в отладчик и сни­мать защиту пол­ностью нам лень. При­выч­но запус­каем ProcMon и изу­чаем лог обра­щений прог­раммы к реес­тру при заг­рузке. Естес­твен­но, мгно­вен­но нашему взо­ру пред­ста­нет огромная прос­тыня, раз­мер которой повер­гает в уны­ние. Одна­ко, на счастье, авто­ры Obsidium (так же как и авто­ры Enigma) осо­бой фан­тази­ей не отли­чают­ся, и по поис­ку Obsidium тут же находит­ся обра­щение к клю­чу \HKEY_CURRENT_USER\SOFTWARE\Obsidium\{XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX} (дан­ные GUID уни­каль­ны для каж­дой прог­раммы).

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

Ав­торы Enigma ока­зались даже более ори­гиналь­ными: они, как ты пом­нишь, хра­нили информа­цию о регис­тра­ции в фай­ле с неп­роиз­носимым наз­вани­ем во вре­мен­ной сис­темной пап­ке. Авто­ры Obsidium для это­го завели спе­циаль­ную под­папоч­ку име­ни себя в под­катало­ге RoamingAppData текуще­го поль­зовате­ля (путь к фай­лу выг­лядит при­мер­но так: C:/Users/<Username>/AppData/Roaming/Obsidium/{XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX}, GUID тот же, что и в реес­тре). Уда­ляем этот фай­лик вмес­те с вет­кой реес­тра — бин­го, три­ал сбро­шен!

В ИТОГЕ

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

Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei



Report Page