Неядерный реактор. Взламываем протектор .NET Reactor

Для защиты при­ложе­ний .NET от отладки и ревер­са сущес­тву­ет мно­жес­тво спо­собов (шиф­рование, ком­прес­сия и дру­гие), а так­же спе­циаль­ных про­тек­торов, таких как, нап­ример, Agile.Net и Enigma. О взло­ме мно­гих из них мы уже пи­сали. Сегод­ня я рас­ска­жу, как побороть еще один популяр­ный про­тек­тор и обфуска­тор — .NET Reactor.

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

При­ложе­ние в отладчи­ке dnSpy

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

Воз­вра­щаем­ся в отладчик dnSpy и про­буем пот­расси­ровать работа­ющую прог­рамму. А вот и хорошая новость: в при­ложе­нии нет анти­отладчи­ка, при­ложе­ние прек­расно запус­кает­ся и трас­сиру­ется, при­чем при трас­сиров­ке «пус­тых» методов во вклад­ке Call Stack вид­но, что счет­чик команд переме­щает­ся по невиди­мому коду и про­вали­вает­ся в вызовы. Погуляв всле­пую по коду, мы обна­ружи­ваем еще одну хорошую новость: не все методы пере­име­нова­ны, некото­рые наз­вания впол­не осмыслен­ны, и мож­но даже нащупать про­цеду­ру про­вер­ки валид­ности (на скрин­шоте выше — isValid). Тело дан­ного метода скры­то, но наз­вание и индекс извес­тны, и это уже что‑то.

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

de4dot v3.1.41592.3405 Copyright (C) 2011-2015 [email protected]
Latest version and source code: https://github.com/0xd4d/de4dot

Detected .NET Reactor 4.8

Не­обра­ботан­ное исклю­чение: System.Security.Cryptography.CryptographicException: Недопус­тимая дли­на дан­ных для дешиф­рования.
в System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
в de4dot.code.deobfuscators.DeobUtils.AesDecrypt(Byte[] data, Byte[] key, Byte[] iv) в D:ade4dot-cexde4dot-cexde4dot.codedeobfuscatorsDeobUtils.cs:стро­ка 87
в de4dot.code.deobfuscators.dotNET_Reactor.v4.EncryptedResource.DecrypterV1.Decrypt(EmbeddedResource resource) в D:ade4dot-cexde4dot-cexde4dot.codedeobfuscatorsdotNET_Reactorv4EncryptedResource.cs:стро­ка 225
в de4dot.code.deobfuscators.dotNET_Reactor.v4.EncryptedResource.Decrypt()

Вер­сии пос­вежее фор­мулиру­ют ошиб­ку лаконич­нее:

Latest version and source code: http://www.de4dot.com/
21 deobfuscator modules loaded!

Detected .NET Reactor 4.8

ERROR:
ERROR:
ERROR:
ERROR: Hmmmm… something didn’t work. Try the latest version.

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

Труд­ности нас не оста­нав­лива­ют: в кон­це кон­цов, мы уже научи­лись раз­бирать более кру­тые обфуска­торы типа Agile бук­валь­но изнутри на самом низ­ком уров­не. Заг­ружа­ем нашу зло­получ­ную прог­рамму в отладчик x32dbg и вспо­мина­ем все то, о чем я писал в пре­дыду­щей статье. Дабы не тра­тить вре­мя на пов­торение, опус­каю длин­ное опи­сание теоре­тичес­кой час­ти про­цес­са. Вкрат­це: заг­ружа­ем биб­лиоте­ку cljit.dll, отла­доч­ные сим­волы к ней и ста­вим точ­ку оста­нова на вход JIT-ком­пилято­ра CILJit::compileMethod.

Ука­зан­ный спо­соб работа­ет, то есть при каж­дом вызове ком­пилято­ра в поле ILCode струк­туры CORINFO_METHOD_INFO мы видим рас­шифро­ван­ный IL-код каж­дого метода. В прин­ципе, мож­но ана­лизи­ровать код и даже пат­чить на лету, но это дол­го и уто­митель­но, вдо­бавок нас ждет еще одна лож­ка дег­тя. Напом­ню, что в пре­дыду­щей статье я опи­сывал слег­ка жуль­ничес­кий спо­соб опре­делить индекс ком­пилиро­ван­ной про­цеду­ры. Суть его сос­тоит в том, что хендл ftn (пер­вое двой­ное сло­во в струк­туре CORINFO_METHOD_INFO), если его исполь­зовать как ука­затель, ука­зыва­ет на оди­нар­ное сло­во — индекс метода в .NET метада­те EXE-модуля.

Источник: xakep.ru

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *