Как реализовать платные доп. опции в C# приложении (схема)?
Есть C# приложение с открытым исходным кодом(github.com), которое реализует базовый функционал.
Поддержка пользователей осуществляется через Discord канал (~200подписок). 99% пользователей ничего не Commit.
Приложение очень часто обновляется - условно - каждый день. Новый функционал тестируется пользователями, и часть его становится доступна по платной подписке (от 10уе в мес)
Желательно, чтобы разные пользователи не могли юзать приложение, купленное один раз кем-то из них, а также, чтобы пользователь, оплативший опции на одно рабочее место, не мог ими воспользоваться на 10 компьютерах.
Я понимаю, что любая защита ломается - интересует вариант с "защитой от дурака" с минимальными трудозатратами.
Возможно, в моем случае достаточна простенькая обфускация приватной части кода, но я что-то не хочу с этим связываться - паранойя?.
Вариант, который тестирую сейчас и который, вроде как, работает - очень трудозатратен для меня, так как я имею крайне скудные познания в С++.
1. Пользователь генерит уникальный uid своего компьютера(виртуалки) открытой c# функцией и отсылает его мне.
2. На этот uid я генерю и отсылаю обратно key по которому буду идентифицировать пользователя.
3. В папку с C# докидываются нативные релизные С++ dll, в которых реализованы платные опции, функционал которых дергается через DllImport в основном c# приложении.
При этом внутри С++ dll происходит проверка валидности пользовательского key на наличие подключенных опций и соответствие key=>PC(WM) для которого он генерился и в зависимости от этого они работают или выдают мусор.
Возможно ли (если да, то как) реализовать 3 пункт по такой схеме:
3. После запуска C# приложения оно в Runtime запрашивает на сервере(подписывая ключом) доп опции, и получает их в "байт коде" который на лету превращается в работающие функции.
Возможно как то через AssemblyLoadContext - к сожалению совсем не знаком с этими возможностями C#.
Сильно ли сложно будет этот полученный "байт код" превратить в C# и задействовать в основном приложении обойдя лицензию?
И если это прям совсем просто, может есть какие-то варианты с той же обфускацией или еще как.
1. Пользователь генерит уникальный uid своего компьютера(виртуалки) открытой c# функцией и отсылает его мне.
ну так и делается. Но можете сделать чуть по другому.
Генерируете UID и ключ для платного пользователя самостоятельно, выдаете пользователю для регистрации программы.
При каждом обновлении оплаченное приложение отправляет UID платного пользователя и уникальный ID/hash текущей конфигурации компьютера. У вас записывается в базу у какого пользователя какой ID/hash оборудования.
На случай переустановки, храните 2-3 конфигурации.
Если у платного пользователя появляется больше конфигураций - значит юзер "крыса". Можно заблокировать его платный ID с очередным обновлением, пусть приходит за саппортом. В соглашении с пользователем ответственность за нераспространение ключей лежит на нем. Если очень просит можно выдать ему новый ID с ключом, но старый (распространенный) оставлять залоченным.
Saboteur, О! Про "оставлять залоченным" очень интересно - спс за идею, честно говоря еще не думал про детали реализации всей этой катавасии с лицензиями.
Собственно как один из вариантов:
некий hardware-key (например серийник материнки, биоса, hdd) от юзерской машины, участвующий в дешифрации модуля
а шифруется собственно "платная" сборка
то бишь ставится обработчик currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
и в нём дешифрация и AssemblyLoad из потока дешифрации
p.s. это вольный и сильно упрощённый пересказ реализации из коммерческих систем лицензирования для шарпа
Василий Банников, О интересно - спс, мне возможно, подойдет от туда та часть, что для Windows
Сейчас использую простенькую функ, которая упорно не дает серийник с материнки:
public CompKey() {
Dictionary<string, string> ids =
new Dictionary<string, string>();
ManagementObjectSearcher searcher;
//процессор
searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_Processor");
foreach(ManagementObject queryObj in searcher.Get())
ids.Add("ProcessorId", queryObj["ProcessorId"].ToString());
//мать
searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM CIM_Card");
foreach(ManagementObject queryObj in searcher.Get())
ids["MotherBoard"] = queryObj["Product"].ToString()+" "
+ queryObj["SerialNumber"].ToString();
//клавиатура
searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM CIM_KeyBoard");
foreach(ManagementObject queryObj in searcher.Get())
ids["KeyBoardID"]= queryObj["DeviceId"].ToString();
//ОС
searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM CIM_OperatingSystem");
foreach(ManagementObject queryObj in searcher.Get())
ids.Add("OSSerialNumber", queryObj["SerialNumber"].ToString());
//мышь
searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_PointingDevice");
foreach(ManagementObject queryObj in searcher.Get())
ids["MouseID"]= queryObj["DeviceID"].ToString();
//звук
searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_SoundDevice");
foreach(ManagementObject queryObj in searcher.Get())
ids["SoundCardID"]= queryObj["DeviceID"].ToString();
//CD-ROM
searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_CDROMDrive");
foreach(ManagementObject queryObj in searcher.Get())
ids["CDROMID"]= queryObj["DeviceID"].ToString();
//UUID
searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT UUID FROM Win32_ComputerSystemProduct");
foreach(ManagementObject queryObj in searcher.Get())
ids["UUID"]= queryObj["UUID"].ToString();
var sb = new StringBuilder();
foreach(var x in ids)
sb.Append( x.Key + ": " + x.Value + "\r\n");
key = sb.ToString();
}
d-stream
Правильно ли я понял вашу идею?
На сервере хранятся C# dll c реализацией приватных функций.
По запросу от пользователя, я проверяю наличие активных лицензий, привязанных к его key, и шифрую DLL с этим key и отправляю закодированную DLL пользователю.
На PC пользователя нейтив С++ код проверяет соответствие key, вшитого в кодированную DLL, компьютеру на котором он запускается, и если все ок расшифровывает кодированные DLL внутри метода ResolveEventHandler() и подключает их к текущей запущенной версии основного приложения?
wlastas, всё даже немного проще:
файл лицензии например xml с сигнатурой может содержать шифрованый кусок - ту самую dll (сборку)
при проверке валидности лицензии, в том числе по hardware_id - этот кусок дешифруется в памяти и используется, не попадая на диск.
всё честно на голом c#, поэтому от пионеров желательно обфусцировать чем-нить чуть суровее бесплатного дотсфускатора.
Сейчас использую простенькую функ, которая упорно не дает серийник с материнки
не факт что данные о процессоре, материнке и т.п. будут валидными в виртуальных средах. Притом в разных виртуальных средах это будет по-разному. В общем стоит к этому подходить аккуратно дабы не получить один и тот же hardware_id для всех windows sandbox например)
Сейчас тенденция идёт к тому, что вы требуете постоянного подключения к интернету, где непрерывно проверяете лицензию. Причём привязка лицензии идёт к конкретному устройству + IP. Также невозможно одновременно работать с двух разных отпечатков. Смена отпечатков требует каких-то сложных действий, например через личный кабинет или звонок в поддержку. При любых подозрениях просто обрубаете пользователя - путь обращается в службу поддержки или подождёт часик, пока интернет не дадут.
Да базовая программа работает только при наличии постоянного сетевого подключения.
Каким образом наличие "патент/лицензионное соглашение" или же привязка лицензии и "постоянная её проверка" поможет просто отключить эту проверку путем комментирования функции, её вызывающей.
Это же С# с открытым исходником.
Скорее всего у меня какие-то базовые пробелы во всем, что касается реализации безопасности.
Нельзя ли чуть подробнее про "непрерывно проверяете лицензию"
Никаким. Вы можете подать в суд и получить деньги за нелицензионное использование.
просто отключить эту проверку путем комментирования функции, её вызывающей.
Это же С# с открытым исходником.
Любую проверку можно отключить. Для защиты обычно используют обфускаторы. Но лучшая защита - это отсутствие исходного кода платных функций в дистрибутиве, а подключение их как dll.
Griboks, да то, что как (группа) отдельных DLL - это понятно.
Можно ли эти DLL достаточно безопасно подгрузить с сервера в рантайм, или же "запустить прям на сервере" и получить уже "машинный код(IL code)" и какие для этого есть пути.
Я вот сейчас пытаюсь понять предложенный выше вариант с шифрованием и ResolveEventHandler - такой метод вроде выглядит очень перспективно из-за того, что не надо писать приватные DLL на с++,
но вот после расшифровки и подгрузки они же будут в оперативе в виде "IL code" инструкций и из них же можно обратно собрать C#?
Я в этом ничего не понимаю - это вообще сложно - вычленить из работающего c# приложения нужные инструкции и пересобрать в c#? Что (где ) почитать по этой теме?
Тут просто лицензия всего 10уе, а в приватном коде нет ничего революционного - он просто работает чуть лучше и дает немного больше чем другие аналогичнее бесплатные реализации.
Хотя как посмотреть. Всё такие больше 2лет его пилю ;-))