Избавление .NET программы от регистрации на примере BEM

Не так давно я решил изучить, а заодно попробовать поправить одну библиотечку, избавив программу работающую на данной библиотеке от лицензии.

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

Покопавшись в программе через Reflector, обнаружил, что все основные библиотеки программы написаны сторонней компанией, более того писались они не для конкретного проекта, а являлись целым Фреймворком. Фреймворк писался как раз для бухгалтерских программ. Для такого рода направленности Фреймворка, в него впихнули необычайно много ненужного функционала (к примеру, работу с FTP). Было переопределено невероятное количество системных пространств имен.

А так же в одной из библиотек данного Фреймворка было определено пространство имен, классы которого отвечали за проверку корректности лицензии. В связи с этим я решил погуглить с целью поиска информации и документации к данному Фреймворку. Через пару минут поисков, стало ясно, что данный Фреймворк является закрытым. Почему закрытым? — да потому, что распространяется он по неизвестным критериям, по крайней мере я не нашел информации по этому поводу на сайте разработчика. Так же на сайте разработчика не удалось найти справочную информацию по данному Фреймворку. Искал всю эту информацию по тому, что как выше было сказано, за лицензирование программы отвечал именно этот Фреймворк, частичка души все же наивно полагала найти keygen к этому Фреймворку, ведь система лицензии для всех программ, базирующихся на этом Фреймворке единая. Это означало, что кто – то для одной из программ на этом Фреймворке сделал keygen. Но, увы, поиски не увенчались успехом.

Да кстати название Фреймворку NetDec.

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



Метод, отвечающий за проверку лицензии, был найден очень быстро, нужно было только отследить обработчик нажатия на кнопку ОК в форме ввода лицензии.



Для удобства анализа кода все-таки пришлось искать аналог Reflector только с деобфускатором на борту. Таковым оказался DisSharp.

Как видно лицензия в программе является цифровой подписью на алгоритме DSA. Для создания цифровой подписи берется так называемый код компьютера (так они его назвали, углубляться в генерацию данного кода не стал) далее код компьютера соединяется с серийным номером, и в методе DSACryptoServiceProvider::VerifyData сверяются с цифровой подписью.

В метод в переменной text1 передается XML строка для восстановления объекта DSA. Немного покопавшись, был найден XML файл, в котором и был объект для восстановления DSA. Первым делом был брошен взгляд на поиск приватного ключа (ну чем черт не шутит), увы, естественно разработчики от него избавились. Сам XML файл был вшит в ресурсы библиотеки. Первым делом были приняты попытки сгенерировать свой объект DSA, и вшить полученный XML в ресурсы библиотеки, заменив старый.

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

Поиски на тему замены ресурсов программным путем адекватного решения не дали. Руки как то отпустились. Хотя уже в принципе я доказал, что обойти лицензию можно вшив свой DSA объект в библиотеку, и на этом же объекте сделать генератор лицензий.
На время пришлось забросить в дальний ящик все это дело, и заняться работой. По прошествии некоторого времени, мне снова кто то сказал об этой программе и о ее защищенности. Снова взялся за разбор.

Посидев, подумав некоторое время, вспомнив, что IL компилятор не компилирует строки, в голову пришла идея (которая покажется многим очень примитивной и глупой) взглянуть на внутренности библиотеки через простой блокнот. Да-да прострой notepad. Поиск по ключевому слову DSAKeyValue сразу же вывел мне XML данные DSA объекта. В голову сразу же пришла идея по регулярному выражению программно заменить этот объект на свой. Взялся за кодинг, реализовав задуманное, столкнулся с тем, что библиотека отказалась вообще работать. Ожидая подобный результат, перед полной заменой я попробовал частичную замену объекта, после чего библиотека отлично работала, только не кушала даже официальную лицензию.

Предварительно я сверил длину официального объекта DSA в XML файле и своего (под длиной подразумевается количество символов), размеры полностью совпадали.

Далее я просто программно считал всю библиотеку в массив байтов, зафиксировал при чтении первое и последнее вхождение в xml объект dsa заменил эти байты на свои, и сохранил обратно в файл.
Забыл сказать, DSA объект генерировался на лету, и при его генерации в xml строку можно указать добавлять к данным приватный ключ или нет, я установил это значение в false, в оригинальной сигнатуре его ведь нет. Сразу же когда инициализировался объект DSA, создавались цифровая подпись с серийным номером, которые сохранялись в файл лицензии.

Проделав вышеописанные шаги, мой xml объект DSA должен был отражаться в рефлекторе. Оставалось только попробовать запустить программу. Прищурился и попытался запустить программу. Как и ожидалось, программа запустилась безо всяких вопросов лицензии.

Спустя некоторое время я обнаружил статью на хабре, про инъекции MSIL кода в сторонние сборки по средством библиотеки Mono.Cecil. Через пару минут был сделан код, который добавляет простой return в начало метода проверки лицензии тем самым лицензия успешно подтверждается!

private void Injector(string path) {

 var assembly = AssemblyDefinition.ReadAssembly (path);

        foreach (var typeDef in assembly.MainModule.Types)
        {
          foreach (var method in typeDef.Methods)
          {
             string mp = "(";
                    
             for (int i = 0; i < method.Parameters.Count; i++)
             {
                mp += method.Parameters[i].ParameterType+",";
             }

             mp = mp.Trim(' '); mp = mp.Trim(',');

             mp += ")";

             if (mp == "(System.Byte[],System.String,System.String)")
             {
               var ilProc = method.Body.GetILProcessor();
                       
               Instruction badInstruction = Instruction.Create (OpCodes.No);
               Instruction firstInstruction = ilProc.Body.Instructions [0];

             ilProc.InsertBefore (firstInstruction, Instruction.Create (OpCodes.Ret, firstInstruction));

               ilProc.InsertBefore (firstInstruction, badInstruction);
             }
           }
        }

        assembly.Write(path);
}


Данный способ срабатывает, потому что принцип проверки лицензии в данном Фреймворке невероятно простой. В методе вызывающем метод проверку лицензии установлен обработчик исключений, если в методе проверке лицензии происходит исключение, значит лицензия неправильная или ее нет вообще, если исключений нет, то все в порядке можно стартовать.

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

if (mp == "(System.Byte[],System.String,System.String)"){
//…
}


Вот в принципе и все.

P.S. Данная статья не коем образом не предназначалась пособием по крекингу и т.п. Не нужно быть гуру в программировании чтобы понять, что проделанные выше действия примитивные, а порой и смешны своей реализацией.
  • +19
  • 3,4k
  • 8
Поделиться публикацией

Комментарии 8

    0
    Примерно такими же методами «в лоб» я занимался когда только начинал этим интересоваться. Редакторы ресурсов, блокноты и т.д. :)
      0
      Reflexil вам в помощь… чего уж самому писать-то…
        0
        решение по поводу mono.cecil было решено использовать после ковыряний reflexil'a
        +2
        Они бы еще на питоне свою супер крутую лицензионную защиту написали бы :)
          0
          в предачу контора писавшая фреймворк, является «квалифицированным обучающем центром», по крайней мере так сказали знакомые
            0
            Ну, из того, что контора является «квалифицированным обучающем центром» не следует, что защита должна быть реализована добротно или идеально. Вот если бы контора специализировалась на защите программных продуктов от взлома, вот тогда было бы прикольно.
              0
              Согласен с вами! Просто делать метод проверки лицензии на исключения, это как минимум не хорошо. То есть если по каким то причинам при проверки лицензии возникло исключение, но лицензия при этом правильная, то все равно выйдет сообщение о неправильной лицензии!
                +1
                Ну, тут просто.
                Допустим происходит исключение при проверке лицензии. Что же делать? Естественно генерировать сообщение о неправильной лицензии, потому как факт её правильности не подтверждён.
                Это на мой взгляд правильно: если библиотека куплена, то в конечном счёте последует обращение к разработчикам: неисправность починят или подскажут, что было сделано не так. На всех остальных — пофиг.

                По поводу «нехорошо»: не думаю, что у разработчика было время хорошо подумать над алгоритмом проверки лицензии — выделили ему часок на всё про всё. Код со своей работой на базовом уровне справляется и ладно.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое