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

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

Life-Hack [Жизнь-Взлом]/Хакинг

#Обучение

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

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

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

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

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

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

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

Тол­ку от этого мало: пос­ле этого бря­ка трас­сиров­ка валит приложение наповал. Но это уже какой-то прог­ресс — далее по нашей стан­дар­тной схе­ме, опро­бован­ной ранее на Obsidium, Enigma и про­чих протекторах, мы про­буем сдам­пить прер­ванный про­цесс при помощи дру­гого спе­циаль­но пред­назна­чен­ного для это­го пла­гина — 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.dll
  • Themidie.dp64
  • HookLibraryx64.dll
  • ScyllaHideX64DBGPlugin.dp64 

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

За­пус­каем иссле­дуемую прог­рамму из под­меню 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). 

Алго­ритм наших дей­ствий таков: 

Вы­пол­няем исходный код метода bool __cdecl QWidget::isVisible(void), резуль­тат в регис­тре AL, регистр RCX прог­рамме уже не инте­ресен, его мож­но исполь­зовать в сво­их целях, что мы и сде­лаем. 

Про­верим, из нуж­ного ли мес­та был выз­ван метод isVisible, пос­коль­ку сек­ция кода садит­ся каж­дый раз на раз­ные адре­са, самое прос­тое и более‑менее надеж­ное — про­верять пос­ледние нес­коль­ко байт адре­са, нап­ример 16 бит, иско­мый адрес вызова дол­жен быть:

????????????BDB7

Ад­рес вызова мы так­же исполь­зуем для отно­ситель­ной адре­сации перемен­ной:

[7FF6F47F6EF8]

Нес­ложно пос­читать, что сме­щение меж­ду ее адре­сом и адре­сом вызова рав­но 0x5AAB44C. 

На вся­кий слу­чай про­веря­ем зна­чение этой перемен­ной (на момент вызова нашего 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, но и любой дру­гой серь­езной защиты, шиф­рующей код и про­веря­ющей его целос­тность.

Источник


Report Page