Cовременные информационные технологии.Информационная безопасность.

Студент Кузьмич В.Ю.

Национальный горный университет, Украина

Способ защиты ПО разработанного на платформе .NET Framework.

         Платформа .NET очень проста в изучении начинающим программистам. Также она обладает огромными возможностями при написании ПО (программного обеспечения). Преимущество этой платформы в скорости разработки ПО, программист уделяет основную часть внимания реализации самого задания, а не промежуточные действия необходимые для стабильной работы конечного продукта, что нельзя сказать об других ‘двоичных’ платформах.

Основная масса компиляторов под такие языки как С++, Delphi  и др., преобразует исходный текст программ сразу в двоичный код, при выполнении  которого процессору поступают прямые команды. Это значит, что готовую программу невозможно преобразовать обратно в исходный текст полностью, но можно преобразовать двоичный код выполняемого приложения в набор команд на низкоуровневом языке программирование, например: Assembler. Это упростит анализ функций, которые выполняет программа, но на анализ уйдет очень много времени. В наши дни существуют программы, которые преобразуют код Assembler в высокоуровневый язык программирования подобный C, такие как IDA, Olly debugger, но и с ними тоже потратится немало времени, если выполняемый код приложение занимает несколько сотен килобайт. Это дает огромное преимущество перед платформой . NET(dot NET) в защите исходного кода.

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

 Платформа dot NET работает на технологиях Common Language Runtime(CLR), Common Type System(CTS) и Common Language Specification(CLS). Главная роль CLR(стандартная среда выполнения):  это обнаруживать и загружать стандартные типы .NET и управлять ними в зависимости от команд. Также CLR берет на себя обязанность низкоуровневых операций как : управления памятью, межъязыковое взаимодействие и т.д. CTS (стандартная система типов) полностью описывает все типы данных поддерживаемые средой выполнения и их взаимодействие друг с другом и как они будут представлены в конечной сборке. CLS (Стандартные спецификации языка) – это набор правил который описывает взаимодействие типов данных с различными языками.

После компилирования, файлы DLL или EXE называются сборкой (assembly), не имеют ничего общего с обычными (двоичными) файлами. Эта сборка содержит MSIL код подобный коду Assembler, с сохранением метаданных (всей информации о классах, методах, переменных),  вот в чем заключается основной недостаток этой платформы. Этот файл можно дизассемблировать с помощью стандартной утилиты ildasm, которая входит в среду разработки Visual Studio, и мы получим всю нужную нам информацию о методах переменных и т.д. (рис.1-2).

 

 

 

 

 

 

Рис. 1 Названия методов                                                 Рис. 2 Содержание метода         

Теперь можно полностью восстановить исходный код, который писал автор. Для этого уже существуют программы, которые восстанавливают исходный код, основываясь на метаданных взятых из EXE, DLL файла. Например: Reflector рис.3.

 

 

 

 

 

 

 

 

 

               Рис. 3 Интерфейс программы Reflector (где справа восстановленный исходный код).

Один из способов защиты исходного кода предложенный разработчиками платформы .NET это ‘обфускация’. Обфускация (от англ. obfuscate — делать неочевидным, запутанным, сбивать с толку) или запутывание кода — приведение исходного текста или исполняемого кода программы к виду, сохраняющему ее функциональность, но затрудняющему анализ, понимание алгоритмов работы и модификацию при декомпиляции.

‘Запутывание’ кода может осуществляться на уровне алгоритма, исходного текста и/или ассемблерного текста. Для создания запутанного ассемблерного текста могут использоваться специализированные компиляторы, использующие неочевидные или недокументированные возможности среды исполнения программы. Существуют также специальные программы, производящие обфускацию, называемые обфускаторами (англ. Obfuscator).

Пример обфускации:

Исходный текст:

 int COUNT = 100;

 float TAX_RATE = 0.2;

 for (int i=0; i<COUNT; i++)

 {

   tax[i] = orig_price[i] * TAX_RATE;

   price[i] = orig_price[i] + tax[i];

 }

Код после обфускации:

 for(int a=0;a<0144;a++){b[a]=c[a]*0.2;d[a]=c[a]+b[a];}

 

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

Чтобы сохранить все преимущества платформы .NET можно использовать шифрование сборки для ее защиты от ‘восстановления’. Для шифрования и дешифрования будет использоваться отдельная программа скомпилирована для ‘двоичной’ платформы, это усложнит анализ функции шифрования. Алгоритм которой будет считывать в память ЭВМ, сборку, файл с ключем (ключ можно записать как константу в коде программы дешифрования) и дешифровать ее в памяти, после результат будет сохраняться во временный файл и запускаться из ‘двоичной’ программы. После завершения работы сборки временный файл удаляется.

 

 

Функции кода программы шифрования \ дешифрования (С++)

int get_fil_len(char *fname)//функция получения размера файла.

{long res=0;

int z;

FILE *sz;

sz = fopen(fname,"rb");

fseek(sz,0,SEEK_END);

res = ftell(sz);

fseek(sz,0,SEEK_SET);

fclose(sz);

return res;}

char* start_codding(char *buf,int b_len,char *key,int k_len)//функция шифрования //вместо нее можно подставить любую другую. buf – массив для преобразований, //b_len – длина масива, key – массив с ключем, k_len длина ключа.

{     char *res=new char [b_len];

      int per=0;

      for(int i=0;i<b_len;i++)

      {

            if(per=k_len-1)per=0;

            res[i]=buf[i]^key[per];

      }

      return res;

}

void cod(char *file_name,char *key_name,char *res_name)//функция работы с //файлами, где file_name - имя файла для преобразования, key_name - путь к файлу //с ключем, res_name – путь к преобразуемому файлу.

{     int  buf_len=100,key_len=0;   

      FILE *stream_in,*stream_key,*stream_res;

      fopen_s(&stream_in,  file_name, "rb");

      fopen_s(&stream_key,  key_name, "rb");

      fopen_s(&stream_res,  res_name, "wb" ); 

      key_len=get_fil_len(key_name);    

      buf_len=get_fil_len(file_name);

      char *key=new char[key_len];

      char *buffer = new char [buf_len];

      fread( key, sizeof( char ), key_len, stream_key);         

      fread( buffer, sizeof( char ), buf_len, stream_in );

      buffer=start_codding(buffer,buf_len,key,key_len);

      fwrite(buffer,sizeof( char ),buf_len,stream_res);

      fcloseall();

      delete []key;

      delete []buffer;

}

void start_prog_(WCHAR *way)//функция запуска дешифрованной сборки где, way- //путь к сборке.

{int a=-1;

STARTUPINFO cif;

ZeroMemory(&cif,sizeof(STARTUPINFO));

PROCESS_INFORMATION pi;

if (CreateProcess((LPCWSTR)way ,NULL,

      NULL,NULL,FALSE,NULL,NULL,NULL,&cif,&pi)==TRUE)

{     WaitForSingleObject(pi.hProcess, INFINITE);

      CloseHandle( pi.hProcess );

      CloseHandle( pi.hThread );

}}

 

Литература:

1.  Троэлсен Э. С# и платформа .NET.- СПб.: Питер, 2007. – 796 с.

2.  Щупак Ю.А. Разработка приложений для Windows. СПб.: Питер, 2008.-592 с.