Предисловие:
Этот урок рассчитан на высокий уровень знания языка C# и общей подготовки. Если вы не знаете что такое делегаты, не знаете ничего про компиляцию языка C# или не знакомы с хэш функциями, то лучше не засоряйте себе мозг, а почитайте к примеру Герберта Шилдта
Идея:
Идея заключается в том,что метод(функцию) который мы будем защищать , в нашей программе по сути не будет. Он будет загружаться допустим из файла (или FTP\и.т.п.) в зашифрованном виде, далее расшифровываться с помощью смешанного AES шифрования (ключ расшифровки у нас будет MD5 исполняемого файла + соль). После загрузки и расшифровки исходного кода он будет компилироваться прямо в память, далее мы привяжем функции из свежескомпилированной сборки к нашим делегатам, которые уже находятся в исходной программе.
Плюсы:
1)Достаточно серьёзная защита вашего know how кода
2)Защита самой программы от изменений
3)Возможность модифицировать код не обновляя у всех саму программу (я знаю о чём вы подумали )
4)Возможность профилировать (пример: допустим при запуске программы мы будем генерировать GUID по железу компьютера, дальше мы на нашем FTP создаём файл с именем GUID, в котором зашифрован исходный код, для шифровки которого использовался этот GUID, вместо соли. + код может отличаться (Pro\Alpha\Test версии))
5)Я считаю достаточно лёгкая реализация
6)Придумай сам
Минусы:
1)Необходим интернет для запуска программы (в случае загрузки исходных файлов через интернет)
2)Скачок потребления ресурсов перед запуском
3)Всё таки не даёт 100% защиты от взлома, но это уже будет намного сложнее чем просто обфускация и протекция.
4)Сложной уровень вхождения в понимании концепции
5)Придумай сам
Что мы должны иметь:
1) VS2010\2008
2) Framework 3.5 (можно и ниже\выше, но в нашем случае он)
3) Прямые руки
Алгоритм выполнения программы:
1)Подсчитать хэш функцию нашей программы (себя из себя)
2)Загрузить текстовую строку с зашифрованным кодом (в нашем случае из файла)
3)Расшифровать наш исходный код
Код:
[Ссылки могут видеть только зарегистрированные пользователи. ]
4)Компилировать и связать с существующими делегатами:
Код:
Цитата:
var csc = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v3.5" } }); //Инициализируем компилятор C# кода
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }) { GenerateInMemory = true };//Добовляем параметры компиляции
if (results.Errors.Count >= 1) return false;//Если есть ошибки компиляции то выходим из функции
Type t = results.CompiledAssembly.GetType("Program");//Извлекаем из скомпилированной сборки класс с именем Programm; Обратите внимание, Program - это имя класса в исходном коде, оно должно совпадать
MethodInfo m = t.GetMethod("SumMethod");//Извлекаем из класса Program метод SumMethod; Обратите внимание, в нашем исходном коде должен быть метод с именем SumMethod
SumMethod = (Sum)Delegate.CreateDelegate(typeof(Sum), m);// Создаём делегат со ссылкой на наш метод
return true;
5)Всё! Теперь можем использовать наши делегаты по назначению!
Подготовка к первому запуску:
Что нужно иметь:
1)Исходный код который мы собираемся встраивать (защищать)
2)Программа для шифровки исходного кода (в нашем случае шифруется по AES и Base64) (см исходник)
3)Хэш сумма(MD5) основной программы
Что делаем:
1) Пишем исходный код, смотрим чтобы имя класса и метода(ов) совпадали с именами в нашем исходном проекте
2)[Ссылки могут видеть только зарегистрированные пользователи. ]
Всё! можем запускать!
[Ссылки могут видеть только зарегистрированные пользователи. ]
p.s. В моём примере я защитил метод SumMethod(обычная операция сложения) из файла source.cs. Попробуйте изменить этот метод и зашифровать (encrypt.bat) и посмотреть что будет.
p.p.s.
Статья полностью моя, идея тоже, точно так же как и исходный код.
Рефлексия )))
Ты крут. Лови плюсик
PS: не думаю что тут хотя бы 10 человек поймут как это работает и не думаю что хотя бы 5 смогут это воплотить в реальную программу...
В моём коде нашел небольшое упущение, забыл указать кодировку, и исходный код открывается\закрывается, но русские буквы не отображаются.
Плюс к этому, почему то протектору удается скомпилировать код не везде. Ругается на несуществующий CSharpCodeProvider.
Ещё немного почитал про динамическую компиляцию.
Можно использовать класс [Ссылки могут видеть только зарегистрированные пользователи. ] для динамической компиляции методов, но там используется [Ссылки могут видеть только зарегистрированные пользователи. ].
На ум приходит два варианта:
1)В программе шифровальщике писать код на IL, с помощью [Ссылки могут видеть только зарегистрированные пользователи. ], далее сериализовать DynamicMethod, и далее зашифровать. В исходной программе расшифровываем, десериализируем, и у нас будет рабочий метод.
2)Допустим с помощью ilasm компилировать IL код нашего метода, который мы защищаем. Но вот только как этот IL открыть в исходной программе...
Ну по сути можно сделать проще. Просто скомпилировать сборку, зашифровать её тем же способом. А в протекторе открывать уже скомпилированную сборку, ну и использовать рефлексию.
Добавлено через 8 минут
Цитата:
Сообщение от Sinyss
Есть примерные цифры замедления выполнения?
Просто думаю в больших программах(не 1к сторочек) запуск будет медленней чем у интерпритаторов...
Основной отрезок времени пожирает сама компиляция. Время компиляции можно измерить, ну и она естественно пропорциональна сложности кода.
Другое дело что в больших программах вовсе необязательно таким(или другим) методом защищать весь код. Главное защитить основные методы, такие как генерация ключа, методы шифрования чего то там и т.п.
Но и примем вот такую ситуацию, к примеру защитили вы метод генерации ключа, и далее где то в коде у вас вот такая строчка if(input==key) ... как вы понимаете это сведет на нет все ваши старания. Так что шифрование некоторой функциональной части кода должна быть тоже.
________________
Последний раз редактировалось Puzer; 14.02.2012 в 15:43.
Причина: Добавлено сообщение
Ужас какой, я от лени когда скриптовик писал, то юзал компиляцию для проверки if выражений, и то компилировало с неприятной скоростью Не проще ли
Код:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Action act = () => { Console.WriteLine("=)"); };
var bf = new BinaryFormatter();
byte[] bt;
using (MemoryStream ms = new MemoryStream(1000))
{
bf.Serialize(ms, act);
bt = ms.ToArray();
}
//....
using (MemoryStream ms = new MemoryStream(bt))
((Action)bf.Deserialize(ms))();
Console.ReadKey();
}
}
}
Меня всегда шарп радовал такими плюшками, это ж весомая часть его силы можно сказать Размер около 850 байт выходит, так что можно даже сервер написать, ему посылаешь входные данные, а он тебе Action в ответ Взлому поддаётся, но мало кому захочется дописывать логику...
А вообще жалко, что более-менее хорошие статьи в самом конце форума лежат =(
Думаю имелось в виду нечто вот такое, создать фабричный метод который будет передавать нужные аргументы в область видимости генерируемого делегата
Код:
static void Main(string[] args)
{
Action fn = Create("Hello from Create's scope");
var data = Serialize(fn); // SerializationException, анонимный метод
не помечен атрибутом Serializable
var func = Deserealize<Action>(data);
func.Invoke();
Console.ReadKey(true);
}
public static Action Create(string arg)
{
Action result = () => Console.WriteLine(arg);
return result;
}
public static byte[] Serialize<T>(T obj)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter serializer = new BinaryFormatter();
serializer.Serialize(stream, obj);
return stream.ToArray();
}
}
public static T Deserealize<T>(byte[] data)
{
using (MemoryStream stream = new MemoryStream(data))
{
BinaryFormatter serializer = new BinaryFormatter();
return (T)serializer.Deserialize(stream);
}
}
Но увы задумка хорошая но не выполнимая в таком виде, хотя скорее всего такое можно провернуть воспользовавшись паттерном Command, но я еще не пробовал.
Добавлено через 20 минут
Таки был прав, паттерн команда частично решает проблему
Код:
static void Main(string[] args)
{
HelloWorld cmd = new HelloWorld("ActionSerialization.Program.Main");
var data = Serialize(cmd);
var obj = Deserealize<ICommand>(data);
obj.Action();
Console.ReadKey(true);
}
interface ICommand
{
void Action();
}
[Serializable]
class HelloWorld : ICommand
{
public string From { get; private set; }
public void Action()
{
Console.WriteLine("Hello, world! From " + From);
}
public HelloWorld(string from)
{
From = from;
}
}
Добавлено через 3 часа 51 минуту
Никто не поддерживает беседу
Вот еще один вариант, милота :3
Код:
static void Main(string[] args)
{
SimpleAction cmd = new SimpleAction(new Action<string>(Console.WriteLine), "Hello," +
" world!");
var data = Serialize(cmd);
var obj = Deserealize<ICommand>(data);
obj.Execute();
Console.ReadKey(true);
}
interface ICommand
{
void Execute();
}
[Serializable]
class SimpleAction : ICommand
{
private Delegate _action;
private object[] _args;
public void Execute()
{
_action.Method.Invoke(null, _args);
}
public SimpleAction(Delegate func, params object[] args)
{
_action = func;
_args = args;
}
}
________________
Talk is cheap. Show me the code
— Linus Torvalds
Последний раз редактировалось Yukikaze; 11.03.2014 в 01:24.
Причина: Добавлено сообщение
по-самой теме с методом Puzer-a траблов миллион, да простые вариации это позволяет делать, более сложные, например, небольшой Socket Class у меня уже не скомпилился, думаю проблема в .NET кроется.
По-Action идея была не в целых кусках вывода конкретного, а в отдельных частях, например полная обработка if else блока
По-Action идея была не в целых кусках вывода конкретного, а в отдельных частях, например полная обработка if else блока
Увы, но в .NET замыкания и сериализация не дружат, по этому приходится возводить жалкую функцию метод до статуса всемогущего класса. богохульство, сжечь
Покурив данную тему еще немного я случайно изобрел RPC, отошел на пару шагов от компьютера, словил просветление...
В общем изначальный концепт верен, но нужно научить фабрику генерировать метод в контексте клиента...кошмар...сам себя перестаю понимать
Нужно засуммонить сюда Синуса и еще пару толковых .NET'чиков, нужны свежие головы, а то у меня уже глаз замылился
ЗЫ Писал этот комментарий больше часа, текст переписывал трижды попутно опровергая свои же теории
Попробовал протестить вариант Yukikaze на асинхроноом серве и клиенте, в результате клиент выкинул такую вот ошибочку при десериализации: "Необработанное исключение типа "System.Reflection.TargetInvocationException" - "Не удалось загрузить файл или сборку. Не удается найти указанный файл."
Давайте подымем тему?
По поводу темы Puzer-a:
минус в наличии исходного кода.
Когда дело дойдёт до более-менее читаемого кода можно будет сдампить код, переписать, закинуть обратно.
По поводу того, что предлагает Yukikaze (сериализация Action-ов):
Ограниченность. Тоесть вызвать Console.WriteLine можно. А если например у вас есть своя функция:
Код:
int Multiple(int a, int b)
{
return a * b;
}
То вы не можете просто сериализовать эту функцию и где-то в другой программе десериализировать и вызвать.
Даже если будет функция Multiple без тела.
Есть 3 вариант:
Считывать IL функции, объявлять функцию без тела, при работе программы подгружать IL и приводить его к функции.
В итоге даже если иметь байты IL-а на руках нельзя просто так взять и посмотреть что делает функция. Не так трудоёмкая работа, как рутинная.
________________
We are Ducks. We are birds. We like bread. We cryack. Cryack.
Появилась наводка, что возможно выйдет передать [Ссылки могут видеть только зарегистрированные пользователи. ]. Но я еще не пробовал, времени никак не выкрою разобраться в експрешинах.
А про ваш метод с IL можете дать что-то почитать?
Появилась наводка, что возможно выйдет передать [Ссылки могут видеть только зарегистрированные пользователи. ]. Но я еще не пробовал, времени никак не выкрою разобраться в експрешинах.
А про ваш метод с IL можете дать что-то почитать?
Только гугл.
C# create method from IL
C# replacing IL
C# injecting IL
и тд
я нашёл либу для CIL Инжекта. Но демки у меня не запустились (win 8.1 x64).
Принцип был такой:
Дампились байты IL-a.
Тело функции удалялось, вызывался метод ReplaceCIL, в нём указывались байты и указатель на функцию, в итоге метод имеет другое тело после измены CIL-a. Хотя до запуска она пустая.
Надо искать рабочую реализацию похожего метода.
В итоге у хацкера будет только набор IL опкодов в виде байтов, с ниим что-то делать крайне трудно.
________________
We are Ducks. We are birds. We like bread. We cryack. Cryack.
Демку все же удалось запустить, но она работает не корректно, IL инжект не проходит выдавая встроенное исключение.
[UPD]
И так, я все же нашел работающую версию этой [Ссылки могут видеть только зарегистрированные пользователи. ] -> нужно было скачать "Previous version binaries and demo" или "Previous version source code". Код у меня таки не скомпилился выдав это -> [Ссылки могут видеть только зарегистрированные пользователи. ]. В уже скомпиленых файлах работающими оказались только 2 -> "Test_x64_DotNet20_Release" и "Test_x64_DotNet35_Release", остальные запускались, но в тексбоксе писали это -> "Initialization is failed with error [Error_CLRNotFound]!".
Обрадовавшись что код осказался впринципи работающим, я решил просто закинуть класс "InjectionHelper" в свой проект и затестить его там. Нужная dll -> "Injection32(64)" загрузилась нормально, а вот дальше в текст бокс выкинуло вот эту ошибку -> "Initialization is failed with error [Error_DbgHelpNotFound]!". Я попробывал закинуть к исполняемому файлу программы папку в которой судя по статье находятся эти файлы, а именно -> "х64", но это не помогло. Значит эти файлы нужно привязать к своей программе как-то иначе. Вот, помогите разобраться в этом господа)
Последний раз редактировалось lxxl; 25.09.2015 в 18:54.