-Музыка

 -Подписка по e-mail

 

 -Поиск по дневнику

Поиск сообщений в Flashr

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 26.02.2007
Записей:
Комментариев:
Написано: 6574


.Net Copy Protection

Среда, 28 Апреля 2010 г. 01:38 + в цитатник
Проблема быстрого роста торрентов не только в том, что защитить программное обеспечение от копирования невозможно, но еще и в том, что многие программисты даже не задумываются о защите, особенно, если пишешь небольшую программу, предназначенную для ограниченного числа пользователей. Многие думают, что защита копирования - это сложно, либо заранее считают, что их приложение не станет сверх-популярным, а многие даже и не задумываются, что могут заработать на создании простенького приложения. Удивительный факт в мире программистов заключается в том, что для того, чтобы создать популярное приложение, достаточно сделать более простой аналог со сложного коммерческого приложения, решая для пользователя хоть и маленькую, зато частую проблему.

Итак, в этом посте я расскажу про лучшую защиту для .Net программ, в том плане, что просто настроить, - про популярный DeployLX. Открыв их сайт вы обнаружите другую неприятную новость, что экспресс версия этого продукта стоит порядка 200у.е.,а стандарт так вообще под 1000у.е. Лично я бы не поскупился на 200у.е. за стандарт, и вы вероятно тоже хотите стандарт, так как только в нем есть удаленный мониторинг запусков программы. Но платить 1000у.е. - это для нас через чур. Поэтому вот полная бесплатная версия, правда устаревшая. Это выражается тем, что генератор ключей не пойдет на Vista, w7, w2k8, поэтому у меня установлена одна виртуальная машина с winXP. А также генератор работает только в 32х битной среде, но и это легко решается. Та библиотека, что должна идти вместе с вашим ПО и обеспечивать проверку лицензий можно взять после установки в папке redistributable и идет как Any CPU.

Ниже идет пример реализации компонента, от которого нужно наследовать контролы. При помещении их на страницу будет проверена лицензия и если вы в генераторе ключей задали удаленное отслеживание, то и обращение к вашему серверу лицензий для проверки валидности использования программы.
Copy Source | Copy HTML
  1. // Adding the following attribute enables Visual Studio.NET designer license embedding support
  2.     [LicenseProvider(typeof(SecureLicenseManager))]
  3.     public class ProtectedUserControl : UserControl
  4.     {
  5.         ///Компонент защиты
  6.         private SecureLicense _license;
  7.  
  8.         /// <summary>
  9.         /// Базовая функция, нужна ли защита?
  10.         /// Если не наш сервер, то нужна!
  11.         /// </summary>
  12.         protected virtual Boolean NeedProtection
  13.         {
  14.             get
  15.             {
  16.                 return !Request.Url.Host.Contains("localhost");
  17.             }
  18.         }
  19.  
  20.         /// <summary>
  21.         /// Поехала инициализация
  22.         /// </summary>
  23.         /// <param name="e"></param>
  24.         protected override void OnInit(EventArgs e)
  25.         {
  26.             base.OnInit(e);
  27.             if (NeedProtection)
  28.             {
  29.                 try
  30.                 {
  31.                     string keys_dir = Global.LicensePath;
  32.                     if(String.IsNullOrEmpty(keys_dir))
  33.                     {
  34.                         keys_dir = @"C:\Keys";
  35.                     }
  36.                     Exception e0 = null;
  37.                     foreach(string filename in Directory.GetFiles(keys_dir, "*.lic", SearchOption.TopDirectoryOnly))
  38.                     {
  39.                         LicenseFile file = new LicenseFile();
  40.                         file.Load(filename);
  41.                         if(file.Id != null)
  42.                         {
  43.                             LicenseValidationRequestInfo info = new LicenseValidationRequestInfo
  44.                                                                     {
  45.                                                                         LicenseFile = file,
  46.                                                                         SerialNumbers =
  47.                                                                             new[] {"DLXLENT-3TX1-2UQ8-87YR-647Y-16WS"}
  48.                                                                     };
  49.                             try
  50.                             {
  51.                                 _license = SecureLicenseManager.Validate(this, typeof (Components_Campaign), info);
  52.                                 //нашли рабочую лицензию
  53.                                 e0 = null;
  54.                                 break;
  55.                             }catch(NoLicenseException nle)
  56.                             {
  57.                                 //если соединились, но что-то на стороне лицензиара пошло не так, то плюем на это дело
  58.                                 //if(nle.Message.Contains("Cannot validate license at server."))
  59.                                 if(nle.Message.Contains("The license is still encrypted and the internal properties cannot be accessed"))
  60.                                 {
  61.                                     break;
  62.                                 }
  63.                                 e0 = nle;
  64.                             }
  65.                             catch(Exception e1)
  66.                             {
  67.                                 e0 = e1;
  68.                             }
  69.                         }
  70.                     }
  71.                     if(e0!=null)
  72.                     {
  73.                         throw e0;
  74.                     }
  75.                 }
  76.                 catch (Exception e1)
  77.                 {
  78.                     Global.Logger.Error("licence", e1);
  79.                     State.GlobalError = e1.Message;
  80.                     Response.Redirect("~/PurchaseLicense.aspx");
  81.                 }
  82.             }
  83.         }
  84.  
  85.         public override void Dispose()
  86.         {
  87.             if (_license != null)
  88.                 _license.Dispose();
  89.  
  90.             base.Dispose();
  91.         }
  92.  
  93.     }
И конечно же нужно привести возможный код сервера лицензий, работающий с Mysql, так как из документации вы не сможете получить его автоматически для mysql базы:
Copy Source | Copy HTML
  1. using System;
  2. using System.Data;
  3. using MySql.Data.MySqlClient;
  4. using System.Collections;
  5. using System.Web.Services;
  6. using DeployLX.Licensing.v3;
  7. using DeployLX.Licensing.Management.v3;
  8.  
  9. [WebService(
  10.     // WARNING: Do not change the Namespace
  11.     Namespace="http://www.xheo.com/licensing/v3_0",
  12.     Description="Implements a license server for the DeployLX\u00AE Licensing copy protection system. Additional info at http://www.deploylx.com.",
  13.     Name="DeployLX|Licensing\u00AE Server" )]
  14. public class MyLicenseServer : LicenseServer
  15. {
  16.     public MyLicenseServer( )
  17.     {
  18.         KeyFolder = Server.MapPath("~/Keys");
  19.         DeployLxSerialNumbers = new string[] { "DLXLENT-3TX1-2UQ8-87YR-647Y-16WS" };
  20.         Global.Logger.Info("License for " + Context.Request.ServerVariables["REMOTE_ADDR"]);
  21.     }
  22.  
  23.     private const string LicenseTableName = "License";
  24.     private const string ActivationTableName = "Activation";
  25.  
  26.     private string ConnectionString
  27.     {
  28.         get
  29.         {
  30.             return @"Server=servername;Database=license;User=username;Password=password;";
  31.         }
  32.     }
  33.  
  34.  #region Validate...
  35.     protected override bool Validate( ServerRequestContext context, bool autoValidate )
  36.     {
  37.         if( ! base.Validate( context, autoValidate ) )
  38.             return false;
  39.  
  40.  
  41.         // TODO: Add any custom validation logic
  42.  
  43.         using( MySqlConnection conn = new MySqlConnection( ConnectionString ) )
  44.         {
  45.             conn.Open();
  46.             using (MySqlCommand cmd = new MySqlCommand(string.Concat("SELECT IsValid FROM ", LicenseTableName, " WHERE SerialNumber = @SerialNumber"), conn))
  47.             {
  48.                 string sn = context.SerialNumber;
  49.                 cmd.Parameters.Add( new MySqlParameter( "@SerialNumber", sn) );
  50.  
  51.                 object obj = cmd.ExecuteScalar();
  52.                 if(int.Parse(obj.ToString())== 0)
  53.                     return false;
  54.             }
  55.         }
  56.             return true;
  57.     }
  58.     #endregion
  59.  
  60.  
  61.     #region Activation Support
  62.  
  63.     #region CanActivate...
  64.  
  65.     protected override bool CanActivate( ServerRequestContext context, ActivationLimit limit, ref ActivationProfile suggestedProfile )
  66.     {
  67.         bool result = false;
  68.  
  69.         using( MySqlConnection conn = new MySqlConnection( ConnectionString ) )
  70.         {
  71.             conn.Open();
  72.             using (MySqlCommand cmd = new MySqlCommand(string.Concat("SELECT * FROM ", ActivationTableName, " WHERE SerialNumber = @SerialNumber"), conn))
  73.             {
  74.                 cmd.Parameters.Add( new MySqlParameter( "@SerialNumber", context.SerialNumber ) );
  75.  
  76.                 using( MySqlDataReader reader = cmd.ExecuteReader( CommandBehavior.SingleResult ) )
  77.                 {
  78.                     if( reader == null || !reader.Read() )
  79.                     {
  80.                         result = true;
  81.                     }
  82.                     else
  83.                     {
  84.                         ArrayList results = new ArrayList();
  85.                         int matchingIdIndex = -1;
  86.  
  87.                         do
  88.                         {
  89.                             object[] values = new object[ 5 ];
  90.                             values[  0 ] = reader[ "SerialNumber" ];
  91.                             values[ 1 ] = reader[ "ReferenceId" ];
  92.                             values[ 2 ] = reader[ "ProfileHash" ];
  93.                             values[ 3 ] = reader[ "DateActivated" ];
  94.                             values[ 4 ] = reader[ "AllowNewMachine" ];
  95.                             if( (int)values[ 1 ] == suggestedProfile.ReferenceId )
  96.                                 matchingIdIndex = results.Count;
  97.                             results.Add( values );
  98.                         }
  99.                         while( reader.Read() );
  100.  
  101.                         if( matchingIdIndex != -1 )
  102.                         {
  103.                             object[] values = results[ matchingIdIndex ] as object[];
  104.                             result = ( values[ 4 ] != DBNull.Value && (bool)values[ 4 ] ) || CheckProfile( context, limit, values[ 2 ] as string, (DateTime)values[ 3 ], (int)values[ 1 ], out suggestedProfile );
  105.                         }
  106.                         else
  107.                         {
  108.                             result = true;
  109.                             return result;
  110.                         }
  111.  
  112.                         if( !result )
  113.                         {
  114.                             for( int ix =  0; ix < results.Count; ix++ )
  115.                             {
  116.                                 if( ix != matchingIdIndex )
  117.                                 {
  118.                                     object[] values = results[ ix ] as object[];
  119.                                     result = ( values[ 4 ] != DBNull.Value && (bool)values[ 4 ] ) || CheckProfile( context, limit, values[ 2 ] as string, (DateTime)values[ 3 ], (int)values[ 1 ], out suggestedProfile );
  120.                                     if( result )
  121.                                         break;
  122.                                 }
  123.                             }
  124.                         }
  125.  
  126.                     }
  127.                 }
  128.             }
  129.         }
  130.  
  131.         return result;
  132.     }
  133.     #endregion
  134.  
  135.     #region RecordActivation...
  136.     protected override void RecordActivation( ServerRequestContext context, ActivationLimit limit, ActivationProfile profile )
  137.     {
  138.         using( MySqlConnection conn = new MySqlConnection( ConnectionString ) )
  139.         {
  140.             conn.Open();
  141.             using( MySqlTransaction transaction = conn.BeginTransaction() )
  142.             {
  143.                 using( MySqlCommand cmd = new MySqlCommand( String.Format(
  144. @"IF EXISTS ( SELECT * FROM {0} WHERE SerialNumber = @SerialNumber AND ReferenceId = @ReferenceId )
        UPDATE {0} SET ProfileHash = @ProfileHash, DateActivated = @DateActivated, AllowNewMachine = 0 WHERE SerialNumber = @SerialNumber AND ReferenceId = @ReferenceId
    ELSE
        INSERT INTO {0} ( SerialNumber, DateActivated, ProfileHash, ReferenceId, AllowNewMachine ) VALUES ( @SerialNumber, @DateActivated, @ProfileHash, @ReferenceId, 0 )", ActivationTableName), conn, transaction))
  145.                 {
  146.  
  147.                     cmd.Parameters.Add( new MySqlParameter( "@SerialNumber", context.SerialNumber ) );
  148.                     cmd.Parameters.Add( new MySqlParameter( "@ReferenceId", profile.ReferenceId ) );
  149.                     cmd.Parameters.Add( new MySqlParameter( "@ProfileHash", profile.Hash ) );
  150.                     cmd.Parameters.Add( new MySqlParameter( "@DateActivated", DateTime.UtcNow ) );
  151.                     // Additional ordered parameters since not all MySql providers support named parameters
  152.                     cmd.ExecuteNonQuery();
  153.  
  154.                 }
  155.  
  156.                 transaction.Commit();
  157.             }
  158.         }
  159.     }
  160.     #endregion
  161.  
  162.     #region CanDeactivate...
  163.     protected override bool CanDeactivate( ServerRequestContext context, ActivationLimit limit, DeactivationPhase phase, ref ActivationProfile suggestedProfile )
  164.     {
  165.         // Only allow deactivation if the license was previously activated and has not already
  166.         // been deactivated.
  167.         bool result = false;
  168.  
  169.         using( MySqlConnection conn = new MySqlConnection( ConnectionString ) )
  170.         {
  171.             conn.Open();
  172.             using (MySqlCommand cmd = new MySqlCommand(string.Concat("SELECT * FROM ", ActivationTableName, " WHERE SerialNumber = @SerialNumber"), conn))
  173.             {
  174.                 cmd.Parameters.Add( new MySqlParameter( "@SerialNumber", context.SerialNumber ) );
  175.  
  176.                 using( MySqlDataReader reader = cmd.ExecuteReader( CommandBehavior.SingleResult ) )
  177.                 {
  178.                     if( reader == null || !reader.Read() )
  179.                     {
  180.                         // Never activated so it cannot be deactivated
  181.                     }
  182.                     else
  183.                     {
  184.                         ArrayList results = new ArrayList();
  185.                         int matchingIdIndex = -1;
  186.  
  187.                         do
  188.                         {
  189.                             object[] values = new object[ 5 ];
  190.                             values[  0 ] = reader[ "SerialNumber" ];
  191.                             values[ 1 ] = reader[ "ReferenceId" ];
  192.                             values[ 2 ] = reader[ "ProfileHash" ];
  193.                             values[ 3 ] = reader[ "DateActivated" ];
  194.                             values[ 4 ] = reader[ "AllowNewMachine" ];
  195.                             if( (int)values[ 1 ] == suggestedProfile.ReferenceId )
  196.                                 matchingIdIndex = results.Count;
  197.                             results.Add( values );
  198.                         }
  199.                         while( reader.Read() );
  200.  
  201.                         if( matchingIdIndex != -1 )
  202.                         {
  203.                             object[] values = results[ matchingIdIndex ] as object[];
  204.                             result = (bool)values[ 4 ] == false;
  205.                         }
  206.                     }
  207.                 }
  208.             }
  209.         }
  210.  
  211.         return result;
  212.     }
  213.     #endregion
  214.  
  215.     #region RecordDeactivation...
  216.     protected override void RecordDeactivation( ServerRequestContext context, ActivationLimit limit, ActivationProfile profile )
  217.     {
  218.         using( MySqlConnection conn = new MySqlConnection( ConnectionString ) )
  219.         {
  220.             conn.Open();
  221.             using( MySqlTransaction transaction = conn.BeginTransaction() )
  222.             {
  223.                 using( MySqlCommand cmd = new MySqlCommand( String.Format(
  224. @"UPDATE {0} SET AllowNewMachine = 1 WHERE SerialNumber = @SerialNumber AND ReferenceId = @ReferenceId", ActivationTableName), conn, transaction))
  225.                 {
  226.  
  227.                     cmd.Parameters.Add( new MySqlParameter( "@SerialNumber", context.SerialNumber ) );
  228.                     cmd.Parameters.Add( new MySqlParameter( "@ReferenceId", profile.ReferenceId ) );
  229.  
  230.                     cmd.ExecuteNonQuery();
  231.  
  232.                 }
  233.  
  234.                 transaction.Commit();
  235.             }
  236.         }
  237.     }
  238.     #endregion
  239.  
  240.     #endregion
  241.  
  242.     #region Extension & Subscription Support
  243.  
  244.     #region CanExtend...
  245.     protected override bool CanExtend( ServerRequestContext context, IExtendableLimit limit, out int extensionAmount )
  246.     {
  247.         // TODO: Determine if the limit can be extended.
  248.         extensionAmount = -1;
  249.         return false;
  250.     }
  251.     #endregion
  252.  
  253.     #region RecordExtension...
  254.     protected override void RecordExtension( ServerRequestContext context, IExtendableLimit limit )
  255.     {
  256.         // TODO: Record the extension
  257.     }
  258.     #endregion
  259.  
  260.     #endregion
  261.  
  262.  
  263.  
  264.     #region Register...
  265.     protected override bool Register( ServerRequestContext context, LicenseValuesDictionary registrationInfo, bool autoRegister )
  266.     {
  267.         if( ! base.Register( context, registrationInfo, autoRegister ) )
  268.             return false;
  269.             // TODO: Add any custom validation logic
  270.             using (MySqlConnection conn = new MySqlConnection(ConnectionString))
  271.             {
  272.                 conn.Open();
  273.                 using (MySqlTransaction transaction = conn.BeginTransaction())
  274.                 {
  275.                     string sn = context.SerialNumber;
  276.                     string xml = context.License.ToXmlString();
  277.                     if (autoRegister)
  278.                     {
  279.                         using (MySqlCommand cmd = new MySqlCommand(
  280.         String.Format(@"INSERT INTO {0} ( SerialNumber, LicenseXml, IsValid ) VALUES ( @SerialNumber, @LicenseXml, 1 ) ON DUPLICATE KEY UPDATE LicenseXml=@LicenseXml", LicenseTableName), conn, transaction))
  281.                         {
  282.                             cmd.Parameters.Add(new MySqlParameter("@SerialNumber", sn));
  283.                             cmd.Parameters.Add(new MySqlParameter("@LicenseXml", xml));
  284.  
  285.                             cmd.ExecuteNonQuery();
  286.                         }
  287.                     }
  288.                     else
  289.                     {
  290.                         using (MySqlCommand cmd = new MySqlCommand(
  291.                                     String.Format(@"INSERT INTO {0} ( SerialNumber, LicenseXml, IsValid ) VALUES ( @SerialNumber, @LicenseXml, 1 ) ON DUPLICATE KEY UPDATE LicenseXml=@LicenseXml", LicenseTableName), conn, transaction))
  292.                         {
  293.                             // Repeat parameters since not all MySql providers support named parameters
  294.                             cmd.Parameters.Add(new MySqlParameter("@SerialNumber", sn));
  295.                             cmd.Parameters.Add(new MySqlParameter("@LicenseXml", xml));
  296.  
  297.  
  298.                             cmd.ExecuteNonQuery();
  299.                         }
  300.                     }
  301.  
  302.                     transaction.Commit();
  303.                 }
  304.         }
  305.  
  306.         return true;
  307.     }
  308.     #endregion
Метки:  

 

Добавить комментарий:
Текст комментария: смайлики

Проверка орфографии: (найти ошибки)

Прикрепить картинку:

 Переводить URL в ссылку
 Подписаться на комментарии
 Подписать картинку