Защита программ и данных с использованием электронных ключей

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

Как же я пришел к выводу об необходимости использования электронных ключей??? Начнем сначала.Нужно было написать защиту по работе на наш программный продукт, я тогда в этом вопросе разбирался слабо (как и сейчас) и первая мысль было естественно привязать свою программу к какой-нибудь железке. Но сказано сделано, так как программа сетевая, то почему бы не пришить ее к MAC-адресу, вообщем за денек другой сделал я определение этого адреса, зашил (так как ПО специфическое этот подход вроде подходил под задачу). Ну вот один из первых вариантов, что у меня получился (этот код не претендует на реальную защиту, просто на простом примере хочу объяснить, чем привязка хуже, я так же не хочу никого обучать взлому и защите программ, поэтому на все возражения, я лучше соглашусь с тем , что я в этом ничего не понимаю). Вообще если вы пишите защиту, я рекомендую вам обзавестись всем тем инструментарием, что пользуеться крекер взламывая Вашу программу. Например SoftIce , а здесь официальный сайт фирмы NuMega которая произвела сей продукт.


unit MainMAC;  
    interface  
    uses  
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, NB30, ExtCtrls;  
    type  
    TGetMACForm = class(TForm)  
       Timer1: TTimer;  
       Timer2: TTimer;  
       procedure FormCreate(Sender: TObject);  
       procedure Timer1Timer(Sender: TObject);  
       procedure Timer2Timer(Sender: TObject);  
       procedure FormClose(Sender: TObject; var Action: TCloseAction);  
    private  
       { Private declarations }  
        procedure Test;  
    public  
       { Public declarations }  
    end;  
       var  
       GetMACForm: TGetMACForm;  
        i:String;  
        N,NP: ^String;  
        Nl,d:Integer;  
        Ln:TStringList;  
    Const N1='0';  
    Const N2='5';  
    Const N3='2';  
    Const N4='1';  
    Const N5='A';  
    Const N6='B';  
    Const N7='-';  
    Const N8='C';  
    implementation  
       {$R *.DFM}  
    type  
      TNBLanaResources = (lrAlloc, lrFree);  
    type  
      PMACAddress = ^TMACAddress;  
      TMACAddress = array[0..5] of Byte;  
    function GetLanaEnum(LanaEnum: PLanaEnum): Byte;  
     var  
       LanaEnumNCB: PNCB;  
     begin  
       New(LanaEnumNCB);  
       ZeroMemory(LanaEnumNCB, SizeOf(TNCB));  
       try  
        with LanaEnumNCB^ do  
          begin  
          ncb_buffer := PChar(LanaEnum);  
          ncb_length := SizeOf(TLanaEnum);  
          ncb_command := Char(NCBENUM);  
          NetBios(LanaEnumNCB);  
          Result := Byte(ncb_cmd_cplt);  
          end;  
       finally  
          Dispose(LanaEnumNCB);  
          end;  
     end;  
    procedure TGetMACForm.FormCreate(Sender: TObject);  
     var  
       LanaEnum: PLanaEnum;  
       I: Integer;  
     begin  
      New(N);  
      New(Np);  
     N^:=N1+N1+N7+N1+N1+N7+N3+N4+N7+N8+N2+N7+N5+N4+N7+N1+N6 ;  
      Ln := TStringList.Create;  
      New(LanaEnum);  
      ZeroMemory(LanaEnum, SizeOf(TLanaEnum));  
        try  
         if GetLanaEnum(LanaEnum) = NRC_GOODRET then  
           begin  
            with Ln do  
               begin  
               Clear;  
               BeginUpdate;  
               nl:= Byte(LanaEnum.length) - 1;  
               for I := 0 to Byte(LanaEnum.length) - 1 do  
                      Add(IntToStr(Byte(LanaEnum.lana[I])));  
               EndUpdate ;  
               end;  
            end;  
       finally  
         Dispose(LanaEnum);  
        end;  
      Test ;  
     end;  
    function ResetLana(LanaNum, ReqSessions, ReqNames: Byte;  
     LanaRes: TNBLanaResources): Byte;  
     var  
       ResetNCB: PNCB;  
     begin  
      New(ResetNCB);  
      ZeroMemory(ResetNCB, SizeOf(TNCB));  
      try  
       with ResetNCB^ do  
         begin  
         ncb_lana_num := Char(LanaNum); // Set Lana_Num  
         ncb_lsn := Char(LanaRes); // Allocation of new resources  
         ncb_callname[0] := Char(ReqSessions); // Query of max sessions  
         ncb_callname[1] := #0; // Query of max NCBs (default)  
         ncb_callname[2] := Char(ReqNames); // Query of max names  
         ncb_callname[3] := #0; // Query of use NAME_NUMBER_1  
         ncb_command := Char(NCBRESET);  
         NetBios(ResetNCB);  
         Result := Byte(ncb_cmd_cplt);  
         end;  
      finally  
        Dispose(ResetNCB);  
      end;  
     end;  
    function GetMACAddress(LanaNum: Byte; MACAddress: PMACAddress): Byte;  
    var  
      AdapterStatus: PAdapterStatus;  
      StatNCB: PNCB;  
     begin  
      New(StatNCB);  
      ZeroMemory(StatNCB, SizeOf(TNCB));  
      StatNCB.ncb_length := SizeOf(TAdapterStatus) + 255 * SizeOf(TNameBuffer);  
      GetMem(AdapterStatus, StatNCB.ncb_length);  
      try  
        with StatNCB^ do  
         begin  
         ZeroMemory(MACAddress, SizeOf(TMACAddress));  
         ncb_buffer := PChar(AdapterStatus);  
         ncb_callname := '* ' + #0;  
         ncb_lana_num := Char(LanaNum);  
         ncb_command := Char(NCBASTAT);  
         NetBios(StatNCB);  
         Result := Byte(ncb_cmd_cplt);  
         if Result = NRC_GOODRET then  
            MoveMemory(MACAddress, AdapterStatus, SizeOf(TMACAddress));  
         end;  
      finally  
        FreeMem(AdapterStatus);  
        Dispose(StatNCB);  
       end;  
     end;  
    procedure TGetMACForm.Test;  
    var  
      LanaNum: Byte;  
      MACAddress: PMACAddress;  
      RetCode: Byte;  
     begin  
       for d:=0 to nl do  
         begin  
         LanaNum := StrToInt(Ln.Strings[d]);  
         RetCode := ResetLana(LanaNum, 0, 0, lrAlloc);  
         if RetCode <> NRC_GOODRET then  
           begin  
           Beep;  
           ShowMessage('Reset Error! RetCode = $' + IntToHex(RetCode, 2));  
           end;  
      New(MACAddress);  
     try  
       RetCode := GetMACAddress(LanaNum, MACAddress);  
       if RetCode = NRC_GOODRET then  
          begin  
          np^ := Format('%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x',  
         [MACAddress[0], MACAddress[1], MACAddress[2],  
          MACAddress[3], MACAddress[4], MACAddress[5]]);  
          if N^=np^ then i:='1';  
         end;  
        finally  
         Dispose(MACAddress);  
        end;  
      end;  
      Dispose(Np);  
      Timer1.Enabled:=true;  
     end;  
    procedure TGetMACForm.Timer1Timer(Sender: TObject);  
     begin  
      If i='1' then Timer2.Enabled:=false  
        else Timer2.Enabled:=true;  
     end;  
    procedure TGetMACForm.Timer2Timer(Sender: TObject);  
     begin  
      Close;  
     end;  
    procedure TGetMACForm.FormClose(Sender: TObject; var Action: TCloseAction);  
     begin  
      Dispose(N);  
     end;  
    end.  

Итак вроде все понятно определяем MAC-адрес, проверяем его , если не совпадает, то вырубаем программу. Все вроде здорово и прекрасно. Но лезем например в WDasm32 и можем наблюдать, что наша привязка как на ладони (еще один из секретов хорошей защиты, забегая вперед, если ваш код защиты обнаружил, что программа используеться нелицезионно, она не должна выводить никаких сообщений юзеру, а если и выводить, то делать это позже и как бы независимо, а лучше просто работать неправильно)

Без комментариев, несколько минут и я могу поставить любой MAC-адрес у себя же в программе, по-мойму все ясно и очевидно, даже если вы зашифруете эту или любую другую строку, вам придеться ее где то разворачивать и где то сравнивать ее с чем то. И крекер по-любому найдет тот злополучный jmp, на котором строилась вся Ваша защита. Вообще это простой пример привязки и не хочеться углубляться в проблему, но даже если вы запакуете(зашифруете) Вашу программу крекер сможеть получить ее сделав дамп памяти (разворачивать упаковщику в памяти Вашу программу все равно придется ) хоть это и несколько усложняет жизнь крекеру, но не спасет Вашу программу от взлома. Подробнее что с вашей программой можно сделать и как это делается читайте здесь. Эту ссылку я даю потому что программист который пишет защиту, должен представлять, что делает крекер когда снимает его защиту (и желательно опережать его в ходе его мыслей). А вот еще секрет хорошей защиты - никогда не рассказывайте как Вы реализовали свою защиту кому-либо и где-либо, эта информация, если попадет в руки крекера, будет как карта для него по Вашему коду, сами знаете насколько легче идти куда-либо с картой, чем без нее. И еще один секрет хорошей защиты, прорабатывайте по возможности алгоритм защиты вместе с проектированием (по возможности) структуры Вашего программного обеспечения, что бы код защиты как бы сроднился с кодом программы.


Выслушаю все комментария и замечания по поводу данной статьи. Сразу хочу предупредить, я не отвечаю на письма где меня просят выслать что либо или будет ли продолжение(и подобные письма не несущие какой-то содержательной части), я их читаю, но отвечать на них у меня времени нет. Также , если кто обнаружит неточности, да и просто грамматические ошибки в статье, за это прошу заранее извинение.
Теги:
электронный ключ
Добавлено: 01 Августа 2018 07:33:47 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...