Dawidds Opublikowano 10 Maja 2008 Udostępnij Opublikowano 10 Maja 2008 Wiele osób stosuje do zapisu stanu gry pliki INI. Są dobre, a zarazem dość proste w obsłudze. Ale mają jedną wadę: wszystko widać w nich jak na dłoni. Ale spokojnie, z pomocą przychodzi Dawidds :) W tym artykule przedstawię sposób na proste, ale neizawodne zabezpieczenia naszego pliku. Każdy będzie mógł oglądać jego zawartość - ale, jeśli go zmodyfikuje, gra wykryje modyfikację pliku. Więc, zaczynajmy :) 1. Strategia działania Czyli jak to zabezpieczenie ma działać. Użyjemy do tego celu chyba najpopularniejszego algorytmu świata - MD5 (hash). Tak będzie wyglądał nasz przykładowy plik *.save: [ZAPIS] wartosc1=7 wartosc2=455 wartosc3=12 [SECURITY] control_code=6cdd033862e7b6e2fa68d9b3f57d5cf9 wartosc1 będzie pobrana ze zmiennej global.wartosc1 (analogicznie 2 i 3). A control_code to nic innego, jak hash naszego tajnego ciągu znaków oraz wszystkich zapisywanych do pliku wartości. 2. Ale co to jest hash...? Już wyjaśniam :P Hash jest dość specyficznym algorytmem. Czy zahashujemy 1 znak, czy może milion znaków, i tak zawsze wynikiem będzie 32-bitowy ciąg znaków (32 znaki - litery i cyfry). A co za tym idzie.... pomyśl..... nie da się go odkodować. Zresztą - zahashować można każdy, nawet nie wiem jak długi ciąg znaków. Co by było, gdyby dało się zahashować grę ważącą 4GB do 32 znaków, po czym ją rozkodować...? Teraz to brzmi logicznie, nie :P ? Ale chwila. Skoro nie da się tego rozkodować, to to jest bez sensu! Niekoniecznie. Wyjaśnię to na przykładzie strony internetowej. Rejestrujesz się na stronie. Twoje hasło zostaje zapisane w bazie danych w postaci zakodowanej. Próbujesz się zalogować. System hashuje wpisane przez ciebie hasło (to do logowania) i porównuje ten hash z tym w bazie danych. Jeśli jest taki sam - i hasło musi być takie same. Co prawda ma to wadę, otóż można wpisać zupełnie inny ciąg znaków, a system i tak stwierdzi, że hasło jest prawidłowe. Ale szanse na to są.... cóż, niewielkie :) Przykładowy hash możesz zobaczyć w kodzie powyżej. Złożony jest z literek oraz cyferek. 3. Algorytm dla GM'a Zaraz zaraz. Ale skąd ja mam wiedzieć, jak się hashuje pliki? Spokojnie. Skorzystamy z gotowego kodu ;) Stwórz funkcję i nazwij ją "md5". Wpisz do niej: GML { var str,uint,grp,rol,i,j,h,len,pos,w,a,b,c,d,e,f,temp,digest; str = argument0; if (!variable_global_exists("MD5k")) { globalvar MD5k,MD5g,MD5r,MD5s; grp = "00010203040506070809101112131415"; grp += "01061100051015040914030813020712"; grp += "05081114010407101300030609121502"; grp += "00071405120310010815061304110209"; rol = "07121722071217220712172207121722"; rol += "05091420050914200509142005091420"; rol += "04111623041116230411162304111623"; rol += "06101521061015210610152106101521"; for(i=0; i<64; i+=1) { MD5k = floor(abs(sin(i+1))*(1 << 32)); MD5g = real(string_copy(grp,i*2+1,2)); MD5r = real(string_copy(rol,i*2+1,2)); MD5s = 32 - MD5r; } } uint = $FFFFFFFF; h[0] = $67452301; h[1] = $EFCDAB89; h[2] = $98BADCFE; h[3] = $10325476; len = 8 * string_length(str); str += chr(128); while ((string_length(str) mod 64) != 56) str += chr(0); for (i=0; i<64; i+=8) str += chr(len >> i); pos = 0; for (j=0; j<string_length(str); j+=64) { for (i=0; i<16; i+=1) { w = ord(string_char_at(str,pos+4)); w = ord(string_char_at(str,pos+3)) | (w << 8); w = ord(string_char_at(str,pos+2)) | (w << 8); w = ord(string_char_at(str,pos+1)) | (w << 8); pos += 4; } a = h[0]; b = h[1]; c = h[2]; d = h[3]; for (i=0; i<64; i+=1) { if (i < 16) f = (d ^ (b & (c ^ d))); else if (i < 32) f = (c ^ (d & (b ^ c))); else if (i < 48) f = (b ^ c ^ d); else f = (c ^ (b | (~d))); temp = d; d = c; c = b; e = uint & (a + f + MD5k + w[MD5g]); b = uint & ((uint & (e << MD5r) | (e >> MD5s)) + b); a = temp; } h[0] = uint & (h[0] + a); h[1] = uint & (h[1] + b); h[2] = uint & (h[2] + c); h[3] = uint & (h[3] + d); } digest = ""; for (j=0; j<4; j+=1) { for (i=0; i<32; i+=8) { digest += string_char_at("0123456789abcdef",1+($F & h[j] >> i+4)); digest += string_char_at("0123456789abcdef",1+($F & h[j] >> i)); } } return digest; } Mówiłem, że nie jest prosty :P ? Ale reszta kodów będzie bardziej zrozumiała ^^ Więc, możemy już brać się za system save'owania ^^ 4. Zwykły zapis stanu gry. Przyjmijmy, że w naszej grze będą 3 wartości do zapisu: global.wrtosc1, global.wartosc2 oraz global.wartosc3. Stwórz funkcję "save" Wpisz do niej: GML ini_open("save.save"); ini_write_real("ZAPIS", "wartosc1", global.wartosc1); ini_write_real("ZAPIS", "wartosc2", global.wartosc2); //Grupa "ZAPIS", nazwa klucza "wartosc2", zawartosć klucza z globalnej wartosc1 ini_write_real("ZAPIS", "wartosc3", global.wartosc3); //UWAGA!!! //Jeżeli zapisujesz liczbę używaj ini_write_real(), jeżeli tekst ini_write_string()! //Odczytywać musimy te wartości tak samo, jak je zapisywaliśmy (real albo string) ini_close(); Jednak to nie wszystkie wartości do zapisu. Musimy przecież zapisać jeszcze zapisać zahashowaną wersję całego save'a. 5. Funkcja generująca hash (do zapisu) Teraz stworzymy funkcję generującą hash. Ale będziemy mieli dwie funkcje generujące hash - do wczytania i do odczytania. Teraz stworzymy tą do zapisania ;) Stwórz funkcję "gen_hash_save", wpisz do niej: GML wynik = "tutajwpiszbylejakiciagznakowktoregoikomuniepokazuj_"; wynik = wynik + string(global.wartosc1); wynik = wynik + string(global.wartosc2); //Dopisujemy do naszego tajnego ciągu znaków wszystkie wartości do zapisania wynik = wynik + string(global.wartosc3); //UWAGA!!! //Muszą się tutaj znaleźć wszystkie wartości, które będziemy zapisywać. return md5(wynik) Chyba nie trzeba wyjaśniać tego kodu :) 6. Poprawa funkcji save() Teraz musimy poprawić funkcję save(). Musi ona zapisywać hasha naszych wartości. Także nowa, zmodyfikowana wersja tej funkcji będzie wyglądała tak: GML ini_open("save.save"); ini_write_real("ZAPIS", "wartosc1", global.wartosc1); ini_write_real("ZAPIS", "wartosc2", global.wartosc2); ini_write_real("ZAPIS", "wartosc3", global.wartosc3); ini_write_string("SECURITY", "control_code", gen_hash_save()); //UWAGA!!! //Jeżeli zapisujesz liczbę używaj ini_write_real(), jeżeli tekst ini_write_string()! //Odczytywać musimy te wartości tak samo, jak je zapisywaliśmy (real albo string) ini_close(); 7. Generowanie hasha (odczyt) Teraz się nie pogób. Napiszemy funkcję, która odczyta wszystkei wartościz naszego pliku, oraz zwróci ich zahashowaną wartość. Jeśli to, co zwróci ta funkcja będzie takie samo jak to, co jest w pliku (control_code) - save jest prawidłowy. jeśli nie - wywalimy odpowiednie okienko =] Stwórz funkcję "gen_hash_load", i wpisz do niej: GML ini_open("save.save"); wynik = "tutajwpiszbylejakiciagznakowktoregoikomuniepokazuj_"; //Taki sam, jak o odczytu! wynik = wynik + string(ini_read_real("ZAPIS", "wartosc1", 0)); //Grupa, klucz, domyślna wartość (jeśli taki klucz by nie istniał) wynik = wynik + string(ini_read_real("ZAPIS", "wartosc2", 0)); wynik = wynik + string(ini_read_real("ZAPIS", "wartosc3", 0)); ini_close(); return md5(wynik); 8. Wykończenie - funkcja wczytująca wartości Teraz podsumowanie tego kodu. Funkcja, która sprawdzi wszystkie funkcje, których teraz używaliśmy i wczyta, lub nie wczyta (jeśli zmodyfikowano save) naszą grę. Stwórz funkcję "load", wpisz do niej: GML ini_open("save.save"); if(ini_read_string("SECURITY", "control_code", "") == gen_hash_load()) //Jeśli hash wszystkich wartości (gen_hash_load()) jest taki sam ja ten wpisany w pliku (control_code) - gra jest poprawna { ini_open("save.sgsave"); global.watosc1 = ini_read_real("ZAPIS", "wartosc1", 0); global.watosc2 = ini_read_real("ZAPIS", "wartosc2", 0); global.watosc3 = ini_read_real("ZAPIS", "wartosc3", 0); show_message("Zaladowano gre."); } else { show_message("Nepoprawny plik *.save"); } ini_close(); ------------------------------------------------- I to by było na tyle. Proszę wybaczyć ewentualne bugi - pisałem ten kod 100% z głowy. Ale myślę, że nie było literówek :) Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
lenin Opublikowano 10 Maja 2008 Udostępnij Opublikowano 10 Maja 2008 Ciekawy artykuł, ale jak mam się bawić tyle z zaszyfrowaniem tego to ja już wolę zwykły plik zapisu :P I oceniać nie będę, bo ciężko by mi było teraz to sprawdzić. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Luksor Opublikowano 10 Maja 2008 Udostępnij Opublikowano 10 Maja 2008 Brzmi super, postaram się przetestować. :) Literówka: Stwórz funkcję "gen_hash_load(), i wpisz do niej: tak chyba powinno być ;p Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Dawidds Opublikowano 10 Maja 2008 Autor Udostępnij Opublikowano 10 Maja 2008 lenin: Ale tu nie ma żadnej filozofii :P Po prostu, jak chcesz zapisać coś więcej to musisz to dodać do tych funkcji :P PS: Pisałem to wczoraj... po pół godzinie się Firefox zaciął :P Teraz pisałem w Notepad++i co chwila było Ctrl+S ^^ Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Harv Opublikowano 10 Maja 2008 Udostępnij Opublikowano 10 Maja 2008 nie da się go odkodować Toż to rok '90! Google -> md5 decoder =/ I co? Da się rozkodować. A zabezpieczenie IMO dobre, ale i tak nie powstrzyma wszystkich... Niestety. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Dawidds Opublikowano 10 Maja 2008 Autor Udostępnij Opublikowano 10 Maja 2008 Harv: Twoje IQ powala na kolana.... Że brute force'm się da rozkodować, to wiem. Ale przy bardzo długim tym słowie, do którego dopisujesz wartości save'a nie masz szans. Superkomputer by to kilka miesięcy liczył. I znalazł by masę innym opcji.... Zresztą, jak jesteś taki dobry: proszę bardzo. Rozkoduj mi to: 6cdd033862e7b6e2fa68d9b3f57d5cf9 (ten przykładowy, z tego arta ;>) PS: Większość takich dekoderów działa słownikowo. Mają olbrzymią bazę hashów i je porównują... Co do rozgryzienia: napisać se program, który modyfikuje save pod wszystkie możliwości, i klika co chwilę LOAD :P Edit: Zresztą dałem dobre porównanie.... mielibyśmy niezłą kompresję, jakby dało się rozkodować. Miliard znaków ściąga się na 32 ^^ Call Of Duty 4 byłoby na dyskietce ^^ Masa luzu by została ^^ Edit2: Ale po takim idio... imbecylu jak ty nie spodziewałem się pozytywnej oceny ;) Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Tymon Opublikowano 10 Maja 2008 Udostępnij Opublikowano 10 Maja 2008 Obecnie przez google można wyszukać parę milionów hashy MD5 więc wyciągnięcie prostych hasełek jest dziecinne proste. Do tego chyba lepiej zastosować CRC. Zabezpieczenie tego typu jest o tyle lipne, że przy uszkodzeniu pliku konfiguracyjnego żegnamy się z wszystkimi naszymi ustawieniami. Jeśli ktoś przechowuje w configu informacje o ostatnim levelu etc. to może być to nieźle wkurzające. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
propaganja Opublikowano 10 Maja 2008 Udostępnij Opublikowano 10 Maja 2008 2. Ale co to jest hash...? Dobry hasz nie jest zły :sp_ike: :thumbsup: a tak ogólem to wporządku art moze sie komus przyda Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Borek Opublikowano 10 Maja 2008 Udostępnij Opublikowano 10 Maja 2008 Widzę, że trochę się napociłeś :) Odejmuję 10% warna :thumbsup: Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Yoda Opublikowano 10 Maja 2008 Udostępnij Opublikowano 10 Maja 2008 Ech te całe operacje bitowe, szyfrowania itp. to coś o czymś zupełnie nie mam pojęcia ;p A art ładny postarałeś się :) Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Snake Opublikowano 10 Maja 2008 Udostępnij Opublikowano 10 Maja 2008 I tak bardzo łatwo to obejść ( bez użycia dekompilatora ) :P Ale mimo wszystko dobra robota :) Jeszcze np. XORem zaszyfrować plik i będzie nieco bezpieczniej ;P Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Dawidds Opublikowano 10 Maja 2008 Autor Udostępnij Opublikowano 10 Maja 2008 Dokładnie. W internecie jest wiele ciekawych DLL'i (nawet dodatków do GM'a) służących do szyfrowania. Wtedy kilkoma linijeczkami kodu można zaszyfrować plik... Artykuł głównie ma przedstawiać jakieś podstawowe zabezpieczenie :) Lepiej takie niż w ogóle :P Choć z drugiej strony - po co się męczyć nad szyfrowaniem, skoro ktoś użyje dekompilatora :P ? Zabezpieczenie tego typu jest o tyle lipne, że przy uszkodzeniu pliku konfiguracyjnego żegnamy się z wszystkimi naszymi ustawieniami. Jeśli ktoś przechowuje w configu informacje o ostatnim levelu etc. to może być to nieźle wkurzające.To ktoś sobie dostosuje ten kod do swoich potrzeb. Ja na przykład w grze przechowuję wiele informacji "poboczny" - a jakoś nie gubię się :P Jedyne, co jest głupie, to to, że jeśli chcę dodać nową wartość do zapisu muszę ją dorobić w 4 funkcjach - nie 2. Ale da się żyć :P W sumie można by to na tablicy postawić... ale to już przesadne kręcenie by było :P Edit: Borek: :> Edit2: Obecnie przez google można wyszukać parę milionów hashy MD5 więc wyciągnięcie prostych hasełek jest dziecinne proste.Dokłądnie... ja zawsze, nawet w dodawaniu haseł do bazy danych w PHP sztucznie przedłużam hasło, tak jak to jest tutaj (supertajnyciagznakow+hasło usera). Bo w przeciwnym razie ten MD5 jest trochę bez sensu :/ Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Administratorzy gnysek Opublikowano 10 Maja 2008 Administratorzy Udostępnij Opublikowano 10 Maja 2008 Ale hash chyba nie jest tylko i wyłącznie 32 bitowym wynikiem funkcji hashującej... jest po prostu jej wynikiem. Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Dawidds Opublikowano 10 Maja 2008 Autor Udostępnij Opublikowano 10 Maja 2008 Ale hash chyba nie jest tylko i wyłącznie 32 bitowym wynikiem funkcji hashującej... jest po prostu jej wynikiem.Ale ma 32 znaki jakby nie było :P Czepiasz się szczegółów ^_^ A, skoro bit odpowiada jednemu znakowi...? Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
PsichiX Opublikowano 10 Maja 2008 Udostępnij Opublikowano 10 Maja 2008 Bajt odpowiada jednemu znakowi (0-255). Bit to wartość true lub false przyjmuje Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Maxius Opublikowano 11 Maja 2008 Udostępnij Opublikowano 11 Maja 2008 Fajne! ale jednak chyba zostanę przy normalnym zapisywania bez zabezpieczeń :) Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Rekomendowane odpowiedzi
Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto
Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.
Zarejestruj nowe konto
Załóż nowe konto. To bardzo proste!
Zarejestruj sięZaloguj się
Posiadasz już konto? Zaloguj się poniżej.
Zaloguj się