Zabezpieczanie programow

asmie
Ten artykuł wymaga dopracowania!

Jeżeli możesz popraw ten artykuł według zaleceń, które możesz znaleźć na stronie [[Artykuły do poprawy]]. Po dopracowaniu tego tekstu można usunąć ten komunikat.

Jak zabezpieczyć swoja aplikacje

Autor: Asmie (-=Nevillon=- Member --> http://nevillon.pac.pl)
Data: 02.V.2003
E-mail: [email protected]

1 Wstęp
2 Jak zabezpieczyć swoja aplikacje
3 Jak zabezpieczyć serwis WWW
4 Inne zabezpieczenia
5 Narzędzia
6 Zakończenie

Wstęp

Witam wszystkich w tekście dotyczącym sposobów zabezpieczania swoich programów, stronek itp. Napisałem ten tekst, ponieważ widzę mnóstwo błędów, jakie popełniają ludzie przy tworzeniu swoich programów. Chcą je w jakiś sposób zabezpieczyć jednak najczęściej wychodzi im to w sposób, który sześciolatek złamie w kilka minut. Patrząc na dzisiejszy rynek oprogramowania, posiadanie Soft Ice'a i podstawowej wiedzy o assemblerze daje możliwość złamania wręcz dowolnego programu. Toteż przeczytaj ten tekst, a może będziesz jednym z ludzi, którzy naprawdę sprawiają wiele problemów crackerom. Fakt jest faktem, ze nie ma zabezpieczenia idealnego, ale celem tego tekstu jest pokazanie metod, które utrudnia życie rożnego rodzaju lam-crackerom, a kto się naprawdę zna na rzeczy to i tak nie będzie miał problemu z naszym programem. Ale ok.. zaczynamy!

Jak zabezpieczyć swoja aplikacje

Nie będę tu pisał o kryptografii itp. gdyż nie jest to celem tego rozdziału. Jeżeli myślisz, ze stare DOS-owskie tricki typu zamiana wektora przerwania 03h na jakiś inny (pewnie 21h, co??:)) nadal działa to czas się obudzić. W Windows IVT (Interrupt Vector Table), czyli tablica wektorów przerwań jest chroniona i każda zmiana na tym poziomie nic nie da i kończy się wyjątkiem ochrony. To jest bez sensu. Tricki anty-debugerowe polegające na przejmowaniu ich przerwań odeszły w niepamięć. W takim razie jak zabezpieczyć program?? Cóż… ogólnie sprawa polega na tym, aby tak namieszać, aby nie było wiadomo, o co w programie chodzi. A dokładniej najlepiej, aby procedura sprawdzająca i weryfikującą hasło była bardzo zagmatwana. Jak piszesz w C++ to wiesz, ze można stworzyć taki kod żeby patrząc na źródło w C++ nie było wiadomo w sumie, co się dzieje, wiec pomyśl, co będą mieli ludzie oglądający ten program spod assemblera:) Ubaw. Pokażę Ci mały przykład:

#define PASSWORD "GoodPassword"
#define PASSWD2	 "BadPassword"
#define PASS_COUNT 4

void check_password(char *password)
{
	int c = 0;
	while (1)
	{
		for (int i = 0; i < PASS_COUNT; i++)
		{
			if (strcmp(password, PASSWD2) == 0)
				continue;	
		}
		c++;
		if (c == 3) break;
	}	
	
	// Tu wlasciwe sprawdzanie hasła
}

Przykład ten jest prosty, ale poczatkujących i średnio zaawansowanych crackerów odstrasza dość skutecznie. Deklarujesz sobie jakiś ciąg, jako hasło na podpuchę i najpierw testujesz ta podpuchę. Problemu w analizie dostarczają zagnieżdżone pętle, które do tego są o tyle śmieszne, iż pierwsza jest ze stałym warunkiem i wygląda na nieskończona. Przez te zewnętrzna pętle przechodzi program trzy razy. Natomiast przez wewnętrzna pętle przechodzi 4 razy, a 4 x 3 daje dwanaście obrotów malej pętli. Do tego dolicz, ze analizujący w assemblerze otrzyma następne pętle analizując funkcje strcmp, co urasta do rangi dość dużego problemu. I kiedy już szczęśliwy znajdzie PASSWD2 i wpisze je do okienka z TextBoxem, wyjdzie mu ze znów niepoprawne hasło:) Taki koleś się najpierw zirytuje, a później będzie analizował kod raz jeszcze i jeszcze aż w końcu dojdzie do wnioski ze hasło jest takie, ale to program jest skopany itp.:) Istnieje niewielkie prawdopodobieństwo ze będzie mu się chciało analizować kod dalej i dojdzie do prawdziwego hasła. Zawsze możesz dać jeszcze jakieś inne śmiecie przed poprawna procedura sprawdzająca hasło, zwiększyć ilość odwołań do różnych funkcji itp. Jest to w sumie bardzo prymitywny sposób, gdyż zeruje na nieuwadze i cierpliwości crackera, ale niemniej jednak jest dość skuteczny. Nieporęczny jest głownie w przypadku, gdy liczy się wydajność, gdyż pętle zabierają niestety trochę czasu :(, ale jest to niewiele i w sumie to nie czuć różnicy.

Kolejna sprawa jest sposób porównywania hasła. NIGDY nie dokonuj deszyfracji klucza w programie (mam nadzieje ze, chociaż szyfrujesz klucze :)). Jest to pierwsza i najważniejsza zasada ! Wiec jak sprawdzić czy wprowadzony klucz jest poprawny. Zaszyfruj go i porównaj zaszyfrowane klucze. Ktoś, kto śledzi Twój program ma wtedy utrudnione zadanie, a gdy poprawny klucz jest deszyfrowany to ktoś sobie przejdzie do momentu aż klucz będzie deszyfrowany i tylko go spisze. Od metody szyfrowania zależy bezpieczeństwo Twojego programu wiec staraj się dobierać algorytm, który dobrze szyfruje (nie mowie tu o Triple-DES itp. ;) ). Marzeniem byłby algorytm jednostronny, którego odszyfrowania jest trudniejsze niż brute-force, ale na to nie licz. Najlepiej polegać na przekształceniach matematycznych, które nie posiadają odwrotności. Czyli mówiąc krótko dodawanie, odejmowanie, mnożenie, dzielenie, potęgowanie i pierwiastkowanie odpadają. To samo inne operacje, do których istnieje operacja przeciwna. Swojego czasu chwalony był xor, ale jak wiadomo dwa użycia xor na tych samych argumentach dają pierwotny wynik wiec nie ma sensu tego używać. Dość dobrym pomysłem jest użycie jakiejś zmiennej generowanej na podstawie na przykład daty lub liczby losowej, która później gdzieś umieścimy w programie, a potem użyjemy do szyfrowania klucza wpisanego przez użytkownika.

Dość dobrym sposobem jest umieszczenie procedur i sprawdzającej w oddzielnym DLL-u, do którego się później odwołamy. Ktoś, kto używa Soft-Ice'a debugujac nasz program stara się znaleźć w module nazywającym się tak jak nasz program, często natomiast wychodzi z funkcji, kontrolowanych przez inne moduły. Mniej wprawny Drucker przeoczy to, ze po breakpoincie wylądował w odpowiednim module i tak jak jest napisane w większości tutorow "I ty będziesz crackerem w 24 h" (do twórców tutorow dot. cracku naprawdę nic nie mam, chodzi mi tu o niezbyt myślących odbiorców tych tutorow - nie wszystkich rzecz jasna ! ) ma przejść do modułu zgodnego z nazwa programu. Wiec taki crackerek będzie ciurkał aż dojdzie do modułu głównego z programem, ale to już będzie musztarda po obiedzie :).

Jeżeli chodzi o wykrywanie Soft-Ice'a w pamięci... hmm.. bzdety. Każdy Drucker posiada IcePatch'a który zmienia nazwy itp. VXD-ka od Soft-Ice'a i już możemy skoczyć... Natomiast lepsza rzeczą jest wykrycie pułapki założonej na konkretnej funkcji API. Jak tego dokonać... oto źródełko (dla funkcji CreateFileExA):


sKernel  db  "Kernel32.dll", 0
sFunc    db  "CreateFileExA", 0

func:
invoke GetModuleHandle, offset sKernel
invoke GetProcAddress, eax, offset sFunc
cmp byte ptr [eax], 0CCh
je _pulapka
jmp _brak_pulapki

Należy tylko pamiętać ze nie każde API, które sprawdzamy znajduje się w kernel32.dll i w przypadku innej funkcji należy sprawdzić, w jakim pliku się ona znajduje. Jest to dość dobry sposób na wycwanienie się przed Soft-Icem. Przykładowo, jeżeli piszesz program możesz użyć tego kodu do sprawdzenia czy ktoś nie założył pułapki na znane funkcje do pobierania danych:

a) GetDlgTextItemA
b) GetWindowTextA
c) GetWindowTextW

itp. W sumie to sprawdzaj tylko te funkcje, które Twój program używa.. no to chyba logiczne, :) Ale ta metoda przebija wszystkie inne testujące obecność Soft-Icea. Ten sposób ma jeszcze taka przewagę, ze może wykryć tez inne debuggery z breakpointem na nasze API, bo nasz kod sprawdza czy pierwszy bajt funkcji API nie wynosi 0CCh, który oznacza ze funkcja ma być debuggowana.

Kolejna sprawa to rodzaj zabezpieczenia, jakie należy wybrać. Szczerze mówiąc nie polecam tworzenia zabezpieczenia polegającego tylko i wyłącznie na podaniu klucza do produktu, gdyż jest to najprostsze zabezpieczenie. Ogólnie, obecne zabezpieczenia są mniej lub bardziej głupie, bo wszystkie są na jedno kopyto. Postaram się przedstawić pewien sposób na stworzenie zabezpieczenia, które sprawi troszkę więcej kłopotu. Otóż algorytm jest prosty:

  • jeżeli chce ktoś pełna wersje programu wysyła formularz z danymi
  • do pliku np.: sys.ini w katalogu windows zapisujemy godzinne kiedy formularz został stworzony
  • ta godzina jest dodawana do formularza
  • na podstawie imienia i godziny generowany jest klucz
  • użytkownik dostaje klucz i wpisuje go a program generuje sobie w ten sam sposób właściwy klucz i porównuje go z wpisanym przez użytkownika
  • jeżeli ten sam - pełna wersja... jak nie - autodestrukcja ;))))))

Takie zabezpieczenie tez nie jest jakieś bardzo wymyślne ale przynajmniej jest troszkę bardziej skomplikowane... i na przykład do tego litery imienia mogą być xorowane z jakaś wartością potem jest na przykład wynik xora poddawany jakiejś operacji logicznej z godzina itp. Sprawa jest o tyle fajna ze nawet jak ktoś złamie dla danego imienia klucz, to inni aby go użyć musza czekać do określonej godziny itp. Jaja :) Natomiast mniej wprawiony Drucker będzie się dziwił skąd mu się takie liczby biorą i będzie się temu dość dziwił :) I o to chodzi !

Następnym zabezpieczeniem jakie możemy dodać do naszego programu to suma kontrolna. Taka suma kontrolna (sposób jej generacji jest dowolny ale dobrze aby była tez dość dziwnie generowana np.: suma co 2 bajta z co 5 bajtem :P ) może być zapisana na końcu pliku ale jeszcze lepiej gdzieś gdzie się jej nikt nie spodziewa. Program przed uruchomieniem sprawdzałby sumę kontrolna czy jest taka sama z przed chwila policzona. Fakt... trochę czasu to może trwać ale tez nie zajmuje to aż tak dużo.. a choć po części chroni nas przed pat chowaniem naszego programu. Dlaczego pisze ze suma powinna być dość dziwnie generowana ? Otóż sumę kontrolna można zmienić ręcznie... :) jeżeli będzie łatwy do przewidzenia sposób jej stworzenia to ktoś sam sobie wygeneruje taka sumę kontrolna. Natomiast przy jakimś dzikim sposobie generowania, to taki Drucker sam nie będzie wiedział w jaki sposób taka suma powstaje.

Inna sprawa to odpowiednie ukrycie kodu programu. Obecne disassemblery są na tyle dobre większość sztuczek zawodzi. Co pozostaje ?? Pisać w Visual Basic do którego należy szukać dekompilatora, gdyż disassemblacja nic nie da. :) Nie... otóż dobra organizacja kodu jest podstawa tego aby Twój kod po disassemblacji sprawiał wrażenie trudnego. Rozbicie kodu aplikacji na kilka bibliotek wcale nie jest zbytnio dobrym pomysłem, gdyż mniejsze bloki kodu łatwiej się czyta... ale tez nie jest zła.. Umieszczenie kodu w 2 lub 3 większych modułach, powoduje ze ilość kodu w jednym tez jest, a częste odwołania miedzy modułami potrafią dość dobrze zaciemnić obraz całej sprawy.

Podsumowując, jeżeli połączyłbyś powyższe sposoby, to otrzymałbyś całkiem bezpieczna aplikacje, nad która Drucker musiałby trochę posiedzieć. Wniosek jest jednak jeden: ZADNE ZABEZPIECZENIE NIE JEST TAK DOBRE JAK TO KTORE SAM WYMYSLISZ I KTORE BEDZIE TAK DZIWNE ZE AZ SMIESZNE. Jest to szczera prawda. Im bardziej oryginalne zabezpieczenie tym trudniej na nie wpaść. Większość dobrych crackerów działa momentami schematycznie i próbują złamać dany program wedle wcześniej przyjętego wzorca. Im bardziej ich zaskoczysz, tym trudniej się w tym wszystkim połapią !!

Jak zabezpieczyć serwis WWW

Serwis WWW jest zupełnie oddzielna sprawa. W jaki sposób zabezpieczyć swoja aplikacje WWW aby osoby nieuprzywilejowane nie mogły się do niego dostać. Podstawa to dobra konfiguracja serwera. Na złe skonfigurowanym serwerze to nawet choćbyś wymyślał ze codziennie będziesz zmieniał hasło itp. to i tak Ci się włamią na ten serwis i tak. Jednak o ile konfiguracja serwera to nie Twoja sprawa o tyle zabezpieczenie serwisu już jak najbardziej tak. Najczęściej serwisy zabezpieczane są na hasło i jest to uzasadnione. Hasło, klucz, cokolwiek. Jednak zaraz pokaże jaki największy błąd popełniają twórcy takich serwisów, a wierzcie mi na słowo, ze na takie serwisy natknąłem się już nie raz, ze po stronce z logowaniem, następuje przejście do innej strony, już z treścią. I nie byłoby w tym nic dziwnego, gdyby nie to, iż na następnych stronach już nie jest sprawdzane czy użytkownik przebywający na tej stronie przeszedł autoryzacje. Sprawa jest o tyle poważna, gdyż można na ślepo wpisać na przykład:

http://www.costam.pl/index.html ---> strona z logowaniem

a my walimy

http://www.costam.pl/index2.html

a tu odpala się strona z treścią... Zero autoryzacji zero wszystkiego. Nie wolno tak pisać aplikacji WWW bo idiota na to kiedyś nawet wpadnie. Na każdej stronie serwisu należy koniecznie sprawdzić czy nastąpiła autoryzacja do tego jeszcze zakończona sukcesem :) . Chiba ze mamy układ ramek i na stronie w ramce, która jest cały czas wpisujemy kod sprawdzający poprawność i w ogóle fakt logowania.

Kolejna rzecz to sprawa gdzie przechować ta informacje... Cookies nie są najlepszym wyborem, bo takie cookie można zawsze podrobić. Dobrze jest na przykład pisząc w PHP zapisać sobie w pliku na dysku IP ludzi którzy się logowali i potem z tej listy czerpać dane o tym czy ktoś ma prawo oglądać dana stronę. Przy czym należy pamiętać o tym zęby była również możliwość wylogowania się :).

Maskowanie URL-a... hmm... wiele ludzi twierdzi ze cos im to da. Kolejny mit :) URL jest zamaskowany tylko na pasku adresowym, natomiast w przeglądarce na pasku statusu wyświetlany jest mimo wszystko adres prawdziwej strony wiec nie ma to większego sensu. Każdy idiota patrząc na pasek statusu, dojdzie jaki jest adres danej strony i podstron. Rozwiązanie ? Ukryć swój URL w inny sposób, na przykład zamieniając go na IP po czym IP na liczbę ósemkowa, do tego dokładając cos przed małpa itp. Na przykład adres w postaci:

http://123lalala@3213314

jest chyba cięższy do odszyfrowania niż zapisany w postaci normalnej. Po sieci łazi kilka tekstów dotyczących właśnie ukrywania swojego URL-a wiec poszukaj i poczytaj !

I na koniec nie musze chyba wspominać o tym, ze używamy POST a nie GET :))))))) ale to już chyba banał :P

Inne zabezpieczenia

Inne zabezpieczenia. Chodzi mi tu na przykład o zabezpieczenie programu przed kopiowaniem na inny komputer. Czy łatwo to zrobić ?? Pewnie ze tak, ale nie polecam tworzenia pliku na dysku czy klucza w rejestrze a potem sprawdzenia tego i na tej podstawie stwierdzenia iż albo jest to ten sam komputer albo zostałem skopiowany i straszna krzywda :). Zarówno klucz jak i plik można samemu dorobić wiec trzeba szukać bardziej wyrafinowanych metod :) Mam na myśli na przykład:

a) zapis danych do ostatniego sektora dysku twardego. Tam żadne dane nie są normalnie zapisywane, a my możemy sobie tam zapisać informacje o naszym programie

b) dołączenie do jakiegoś pliku systemowego np.: win.com na koniec jednego bajta, który będzie pełnił role bajta wyznaczającego tożsamość komputera. Jeżeli się zgadza - to ten sam PC, jeżeli nie to znaczy ze program został skopiowany.

c) zapis do pamięci FLASH ---> nie polecam :P

d) pobranie sektora dyskowego w którym znajduje się program i porównanie go z sektorem zapisanym gdzieś wewnątrz programu podczas instalacji

Metod jest dużo... trzeba tylko na nie wpaść !!! :)

Narzędzia

W ostatnim paragrafie tego tekstu przedstawię i krótko opisze narzędzia jakimi posługują się crackerzy do łamania programów:

a) Soft Ice - chyba najbardziej znane narzędzie... bardzo potężny debugger, który ma wszystkie opcje potrzebne do skoszenia każdego programu. W sumie w rękach dobrego crackera jest tak potężny, ze my jako programiści to możemy mu tylko próbować przeszkadzać, co można porównać do rzucania zapałek pod czołg w nadziei ze wybuchnie :P

b) WDasm - disassembler, drugie najbardziej znane i używane narzędzie do łamania programów

c) IDA - coś a`la WDasm... niektórzy twierdza ze lepsze :) inni ze nie ...

d) HexWork/HView - hexedytory, coś do patchowania

Polecam Ci zaopatrzenie i zapoznanie się ze wszystkimi powyższymi narzędziami. Może lepiej zrozumiesz w jaki sposób są łamane programy i
jak się przed tym bronić !

Zakończenie

Na koniec pragnę zaznaczyć ze powyższy tekst może zawierać błędy, wiec proszę o napisanie mi jeśli zauważycie jakiś (merytoryczny, ort, itp. :) ) a skoryguje !! W razie wątpliwości tez piszcie... E-mail na górze:)

Greetz :) (ah, jak zwykle :P )
Neo, Furiza, Haski, Micy, Lord, Noe, Major, JackWild, Drobny i cały Nevillon!! Rzecz jasna największe pozdrowienia dla mojej kochanej dziewczyny Ani! :)

7 komentarzy

niezle ale co do zabezpieczania przed skopiowaniem:mozna odczytac numer seryjny dysku i zapisac na koncu aplikacji . I sprawdzać :P

jestem amator ale sam spotkalem sie z problemem i taki mi sprawił mój wlasny prosty programik a mianowicie podmienianie znaków co tu pisac oto procka
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
klucz:=klucz+key xor $21;
end;
idea taka wpisujesz klucz do okienka edycyjnego załozyłem płapke na funkcie pobierającą klucz po wcisnienciu ok został pobrany łancuch znaków z okienka ale jak wiecie z kluczem nie miał on wiele wspulnego:)

No niby fakt Vogel ale GPL-em nie wyzywisz rodziny .... I wydaj w GPL pod win.... :) to gratulacje :)

Najlepsze zabezpiecznie przed nielegalnym kopiowaniem: wydać program na GPL'u.

Hehe, Hex, jak pisałem w tekście... każde zabezpieczenie jest mniej lub bardziej głupie :P wszystko jest do złamania :) nawet sedes :)))))

To jeszcze nic...
Takie cos łatwo jest obejść SoftIce`em lub czymś podobnym...

całkkiem całkiem