Хакер - Фемида дремлет. Как работает обход защиты Themida

Хакер - Фемида дремлет. Как работает обход защиты Themida

hacker_frei

https://t.me/hacker_frei

МВК

Themida или «Фемида» — одна из самых наворо­чен­ных защит для соф­та. Вмес­те с WMProtect ее отно­сят к нас­толь­ко суровым про­тек­торам, что некото­рые прог­раммис­ты, которым жаль денег на чес­тную покуп­ку этих инс­тру­мен­тов (а сто­ят они недеше­во), прос­то ими­тиру­ют их наличие, что отпу­гива­ет ленивых и неопыт­ных хакеров. Сегод­ня мы погово­рим о том, как сбро­сить три­ал при­ложе­ния, защищен­ного нас­тоящей Themida.

WARNING

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

Нес­мотря на широкую популяр­ность Themida, высокую сто­имость взло­ма и, соот­ветс­твен­но, боль­шое количес­тво ману­алов и виде­оуро­ков в сети, они поч­ти всег­да бес­полез­ны. Они в основном опи­сыва­ют сня­тие ста­рых вер­сий защиты 1 и 2. Уже третья вер­сия устро­ена нас­толь­ко слож­нее, что в паб­лике нет уни­вер­саль­ных инс­тру­мен­тов и скрип­тов для ее взло­ма или обхо­да. И даже на ста­рых вер­сиях всег­да находят­ся какие‑то нюан­сы, исклю­чающие воз­можность уни­вер­саль­ного однокно­поч­ного решения.

По­это­му я не буду оста­нав­ливать­ся сей­час на «тра­дици­онных» спо­собах обхо­да «Фемиды», а вмес­то это­го опи­шу наибо­лее прос­той и понят­ный метод иссле­дова­ния, взло­ма и пат­ча одно­го из защищен­ных гра­фичес­ких при­ложе­ний. Эта прог­рамма исполь­зует Themida треть­ей вер­сии, а мы, как обыч­но, воору­жим­ся лишь минималь­ным набором дос­тупных инс­тру­мен­тов (x64dbg и его пла­гины).

Нуж­но заметить, что для обна­руже­ния Themida не сле­дует силь­но полагать­ся на DetectItEasy. С этим про­тек­тором гораз­до луч­ше работа­ют Nauz File Detector и ExeInfo.

Nauz File Detector
ExeInfo

Ви­зуаль­но же на при­сутс­твие «Фемиды» в фай­ле как бы намека­ет наличие меж­ду сек­циями .idata и .pdata двух сек­ций с неп­роиз­носимы­ми наз­вани­ями из 8 слу­чай­ных сим­волов. Впро­чем, в треть­ей вер­сии раз­работ­чики уже не стес­няясь пря­мо называ­ют сек­ции .themida и .boot. Код гаран­тирован­но зашиф­рован, упа­кован и ста­тичес­кому ана­лизу и ревер­су не под­дает­ся.

По­это­му поп­робу­ем заг­рузить нак­рытую Themida прог­рамму в наш любимый отладчик x64dbg. Разуме­ется, все пло­хо: при стар­те отладчик про­вали­вает­ся в вир­туаль­ную машину, столь страш­ную на вид, что отби­вает­ся вся­кая охо­та с ней раз­бирать­ся. Хотя в отдель­ных слу­чаях это­го не избе­жать, ска­жем, при при­вяз­ке кон­крет­ной прог­раммы к компь­юте­ру или дон­глу. В любом слу­чае этот путь неп­рост и тер­нист: вир­туаль­ная прог­рамма мгно­вен­но палит отладчик, вдо­бавок она отсле­жива­ет тай­минг похож­дения отдель­ных учас­тков сво­его кода.

Схо­ду при­атта­чить­ся к уже активно­му про­цес­су тоже нель­зя, раз­работ­чики и это пре­дус­мотре­ли. Поп­робу­ем сде­лать чуть хит­рее — воз­можно, по прош­лым стать­ям ты пом­нишь замеча­тель­ный анти‑анти­отла­доч­ный пла­гин ScyllaHide для x64dbg, в котором спе­циаль­но для ленивых уже под­готов­лены готовые про­фили под каж­дую популяр­ную защиту. Конеч­но же, подоб­ный про­филь там есть и для Themida, прав­да, не шиб­ко силь­но он нам поможет: при заг­рузке прог­раммы он не спа­сает от анти­отладчи­ка, одна­ко при­атта­чить­ся к запущен­ному при­ложе­нию уже поз­воля­ет.

Тол­ку, прав­да, с это­го нем­ного: пос­ле такого бря­ка трас­сиров­ка валит прог­рамму наповал. Но это уже прог­ресс — далее по стан­дар­тной схе­ме, опро­бован­ной нами ранее на Enigma, Obsidium и про­чих, про­буем сдам­пить прер­ванный про­цесс при помощи дру­гого спе­циаль­но пред­назна­чен­ного для это­го пла­гина — Scylla.

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

00007FF6F4FA521D | E9 DB2F5F00 | jmp 7FF6F55981FD

00007FF6F4FA5222 | E9 E5E31D00 | jmp 7FF6F518360C

00007FF6F4FA5227 | E9 25064500 | jmp 7FF6F53F5851

00007FF6F4FA522C | E9 375E5900 | jmp 7FF6F553B068

00007FF6F4FA5231 | 73 D5 | jae 7FF6F4FA5208

00007FF6F4FA5233 | CF | iretd

00007FF6F4FA5234 | 0026 | add byte ptr ds:[rsi],ah

00007FF6F4FA5236 | 49:89ED | mov r13,rbp

...

00007FF6F55981FD | 48:83EC 08 | sub rsp,8

00007FF6F5598201 | E9 E6DB0200 | jmp 7FF6F55C5DEC

...

00007FF6F55C5DEC | 48:83EC 08 | sub rsp,8

00007FF6F55C5DF0 | 48:83EC 08 | sub rsp,8

00007FF6F55C5DF4 | 48:81EC 08000000 | sub rsp,8

00007FF6F55C5DFB | 48:891C24 | mov qword ptr ss:[rsp],rbx

00007FF6F55C5DFF | 8F0424 | pop qword ptr ss:[rsp]

00007FF6F55C5E02 | 8F0424 | pop qword ptr ss:[rsp]

00007FF6F55C5E05 | E9 83000100 | jmp 7FF6F55D5E8D

...

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

По счастью, умные люди уже под­суети­лись и соз­дали для нас чудес­ный пла­гин Themidie, который поз­воля­ет бес­проб­лемно трас­сировать при­атта­чен­ный про­цесс под Themida. Для его исполь­зования необ­ходимы пос­ледние вер­сии отладчи­ка x64dbg и пла­гина ScyllaHide, про которые я писал выше. Заг­рузи с GitHub пос­леднюю вер­сию Themidie и извле­ки Themidie.dll и Themidie.dp64 в пап­ку пла­гинов x64dbg. В ито­ге там дол­жны обя­затель­но при­сутс­тво­вать четыре фай­ла: Themidie.dllThemidie.dp64HookLibraryx64.dll и ScyllaHideX64DBGPlugin.dp64.

Заг­ружа­ем x64dbg и с чувс­твом пол­ного удов­летво­рения обна­ружи­ваем в под­меню Plugins (Модули) допол­нитель­ный пункт Themidie. В опци­ях ScyllaHide отклю­чаем все, кро­ме чек­бокса Kill Anti-Attach.

Нас­трой­ка пла­гина Themidie

За­пус­каем иссле­дуемую прог­рамму из под­меню Plugins-Themidie-Start. Если мы все сде­лали пра­виль­но, то дол­жно появить­ся вот такое окно.

За­пуск прог­раммы в отладчи­ке

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

00007FF630D5C10F call sub_7FF630D5D970

00007FF630D5C114 mov rdx, [rax]

00007FF630D5C117 mov rcx, rax

00007FF630D5C11A call qword ptr [rdx+8]

00007FF630D5C11D test al, al

00007FF630D5C11F jz loc_7FF630D5CEE4

00007FF630D5C125 lea rcx, [rsp+0EA8h+var_810]

00007FF630D5C12D call sub_7FF631797E20

00007FF630D5C132 xor bl, bl

Ес­ли бы на при­ложе­нии не было навеше­но «Фемиды», мож­но было бы пить шам­пан­ское: делов то — поп­равить пару бай­тиков условно­го перехо­да je. Но тут начина­ется самое инте­рес­ное. Исходный код у нас зашиф­рован и упа­кован, ревер­сить вир­туаль­ную машину Themida, как мы уже успе­ли убе­дить­ся, — вещь дос­таточ­но тру­доем­кая. При­чесать и деоб­фусци­ровать сдам­плен­ный чуть рань­ше код, конеч­но, чуть поп­роще, но все рав­но задача выг­лядит весь­ма неп­ростой.

Са­мым лег­ким вари­антом кажет­ся исполь­зование лоаде­ра или инлайн пат­ча. Что­бы не кодить лоадер, поп­робу­ем вто­рой вари­ант. Суть в том, что если нель­зя про­пат­чить код самого защищен­ного при­ложе­ния, то мож­но поп­робовать сде­лать это из какой‑нибудь незащи­щен­ной внеш­ней биб­лиоте­ки, бла­го, QT-шных либ рядом с прог­раммой валя­ется в изо­билии, и кон­тро­ля целос­тнос­ти на них нет. Слег­ка поковы­ряв код, обна­ружи­ваем бли­жай­ший к нашей раз­вилке обфусци­рован­ный вызов фун­кции bool __cdecl QWidget::isVisible(void) биб­лиоте­ки qt5widgets.dll.

00007FF6EE96BDAE | 49:8BCF | mov rcx,r15

00007FF6EE96BDB1 | FF15 B1B75401 | call qword ptr ds:[7FF6EFEB7568]

00007FF6EE96BDB7 | 84C0 | test al,al

00007FF6EE96BDB9 | 0F85 EB020000 | jne 7FF6EE96C0AA

00007FF6EE96BDBF | 48:8D8C24 20090000 | lea rcx,qword ptr ss:[rsp+920]

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

mov rax,qword ptr ds:[rcx+28]

mov eax,dword ptr ds:[rax+8]

shr eax,F

and al,1

ret

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

00007FF630D5C11A call qword ptr [rdx+8]

воз­вра­щал в AL ненуле­вое зна­чение. Еще нем­ного покопав­шись в отладчи­ке, прев­раща­ем дан­ный метод вот в такую пос­ледова­тель­ность дей­ствий:

mov rcx,qword ptr ds:[7FF6F47F6EF8]

mov rcx,qword ptr ds:[rcx+10]

movzx eax,byte ptr ds:[rcx+A1E]

Это озна­чает, что для того, что­бы прог­рамма почувс­тво­вала себя лицен­зион­ной, нуж­но уста­новить по адре­су [[[7FF6F47F6EF8]+10]+A1E] любое ненуле­вое зна­чение бай­та (нап­ример, 1). Алго­ритм наших дей­ствий таков:

  1. Вы­пол­няем исходный код метода bool __cdecl QWidget::isVisible(void), резуль­тат в регис­тре AL, регистр RCX прог­рамме уже не инте­ресен, его мож­но исполь­зовать в сво­их целях, что мы и сде­лаем.
  2. Про­верим, из нуж­ного ли мес­та был выз­ван метод isVisible, пос­коль­ку сек­ция кода садит­ся каж­дый раз на раз­ные адре­са, самое прос­тое и более‑менее надеж­ное — про­верять пос­ледние нес­коль­ко байт адре­са, нап­ример 16 бит, иско­мый адрес вызова дол­жен быть ????????????BDB7.
  3. Ад­рес вызова мы так­же исполь­зуем для отно­ситель­ной адре­сации перемен­ной [7FF6F47F6EF8]. Нес­ложно пос­читать, что сме­щение меж­ду ее адре­сом и адре­сом вызова рав­но 0x5AAB44C.
  4. На вся­кий слу­чай про­веря­ем зна­чение этой перемен­ной (на момент вызова нашего isVisible она зап­росто может быть еще не ини­циали­зиро­вана) и уста­нав­лива­ем зна­чение [[[7FF6F47F6EF8]+10]+A1E] в 1.

Те­перь, ког­да алго­ритм понятен, ищем в коде биб­лиоте­ки qt5widgets.dll бли­жай­ший пус­той кусок дос­таточ­ной дли­ны и уста­нав­лива­ем обра­бот­чик isVisible на него. Поп­равлен­ный и допол­ненный код isVisible выг­лядит так:

00007FFDB8EFF04F | 48:8B41 28 | mov rax,qword ptr ds:[rcx+28]

00007FFDB8EFF053 | 8B40 08 | mov eax,dword ptr ds:[rax+8]

00007FFDB8EFF056 | C1E8 0F | shr eax,F

00007FFDB8EFF059 | 24 01 | and al,1 <- В AL результат isVisible

00007FFDB8EFF05B | 48:8B0C24 | mov rcx,qword ptr ss:[rsp] <- Адрес вызова, точнее, возврата

00007FFDB8EFF05F | 81E1 FFFF0000 | and ecx,FFFF <- Берем от него младшие 16 бит...

00007FFDB8EFF066 | 48:81F9 B7BD0000 | cmp rcx,BDB7 <- И проверяем их на ????????????BDB7

00007FFDB8EFF06D | 74 01 | je qt5widgets.7FFDB8EFF070

00007FFDB8EFF06F | C3 | ret <- Если вызов не оттуда то ничего не делаем и просто возвращаем AL

00007FFDB8EFF070 | 48:8B0C24 | mov rcx,qword ptr ss:[rsp] <- Адрес вызова, точнее, возврата

00007FFDB8EFF074 | 48:81C1 41B1E805 | add rcx,5E8B141 <- В rcx адрес [7FF6F47F6EF8]

00007FFDB8EFF07B | 48:8B09 | mov rcx,qword ptr ds:[rcx]

00007FFDB8EFF07E | 48:85C9 | test rcx,rcx

00007FFDB8EFF081 | 74 EC | je qt5widgets.7FFDB8EFF06F <- Если [7FF6F47F6EF8] не инициализированна то тоженичего не делаем

00007FFDB8EFF083 | 48:8B49 10 | mov rcx,qword ptr ds:[rcx+10]

00007FFDB8EFF087 | C681 1E0A0000 01 | mov byte ptr ds:[rcx+A1E],1 <- Устанавливаем признак лицензированности и возвращаем AL

00007FFDB8EFF08E | C3 | ret

Все это кажет­ся каким‑то извра­щени­ем, но это работа­ет: внеш­няя стан­дар­тная биб­лиоте­ка QT при обра­щении к ней из нуж­ного мес­та дела­ет нак­рытую Themida прог­рамму «лицен­зион­ной», даже не модифи­цируя ее код.

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

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



Report Page