Strategie pokonania edytorów pamięci do oszukiwania - gry komputerowe [zamknięte]

35

Zakładam, że mówimy o grach komputerowych - coś, co gracz pobiera i uruchamia na swoim komputerze lokalnym. Wiele z nich to edytory pamięci, które pozwalają wykrywać i zamrażać wartości, takie jak zdrowie gracza.

Jak zapobiegacie oszustwom poprzez modyfikację pamięci? Jakie strategie są skuteczne w walce z tego rodzaju oszustwem?

Dla porównania wiem, że gracze mogą: - Wyszukaj coś według wartości lub zakresu - Wyszukaj coś, co zmieniło wartość - Ustaw wartości pamięci - Zatrzymaj wartości pamięci

Szukam dobrych. Dwa, których używam, są mierne:

  • Wyświetlanie wartości jako procent zamiast liczby (np. 46/50 = 92% zdrowia)
  • Klasa niskiego poziomu, która przechowuje wartości w tablicy i przenosi je przy każdej zmianie. (Na przykład zamiast int mam klasę będącą tablicą ints i za każdym razem, gdy zmienia się wartość, używam innego, losowo wybranego elementu tablicy do przechowywania wartości)

Gra jest offline, darmowa i przeznaczona dla jednego gracza.

Edycja: dla potomności rozwiązałem to (w C #) i zamieściłem moje rozwiązanie tutaj , na moim blogu.

ashes999
źródło

Odpowiedzi:

21

Inną możliwością (dla wartości całkowitych) jest posiadanie drugiej zmiennej składowej, która zawiera bitowe uzupełnienie istniejącej zmiennej.

Więc wyobraź sobie health, że masz healthComplement. Twoja implementacja może wyglądać następująco:

// setter sets value and complement
void setHealth(int value){
    health = value;
    healthComplement = ~value;
}

// getter checks if value was changed outside of the setter
int getHealth(){
    if(value != ~healthComplement){
        // launch some cheat-counter-measure, like crashing the game?
        exit;
    }
    return health;
}

Ktoś, kto chce oszukiwać, musiałby poprawnie ustawić zdrowie i uzupełnienie zdrowia, w przeciwnym razie gra się zawiesiłaby.

Myślę, że nie ma sensu zapobiegać takim rzeczom. Ludzie z powodzeniem stworzyli hacki / kody do znacznie bardziej złożonych rzeczy, a to niepotrzebnie zaciemnia kod lub pogarsza działanie gry.

grzmot
źródło
12

Oto jeden schemat, który wymyśliłem, gdy dawno temu ktoś o to prosił. Używanie wartości procentowych lub podwójnych zmiennych tak naprawdę nie działa, ponieważ można wyszukiwać „dowolne” wartości i zamrażać je.

Zamiast tego stwórz potwora typu danych.

Zasadniczo przechowuj swoje ważne wartości jako strukturę dzięki:

  • 32 wskaźniki do bitów
  • W celu szybkiego odczytu, struktura powinna również zawierać bieżącą wartość jako zwykłą liczbę całkowitą

W celu manipulacji zbierz bieżącą wartość z oddzielnie przydzielonych bitów (które mogą być wartościami prawda / fałsz), zmień wartość, a następnie zapisz je z powrotem.

Oto nieprzyjemny kawałek: za każdym razem, gdy manipulujesz wartością, uwolnij i ponownie przydziel bity. Alokacja powinna odbywać się w losowej kolejności , aby dane przeskakiwały w pamięci i nie mogły być łatwo zamrożone.

Dodatkowymi zabezpieczeniami byłoby przechowywanie sum kontrolnych wartości (a także „zwykłego” int) i porównywanie ich również podczas wywołań manipulacji. Jeśli znaleziono niedopasowanie, zrób coś subtelnego, na przykład wyjmij losowy klucz potrzebny do odblokowania drzwi, aby przejść do gry.

Edycja: W celu dalszego przenoszenia pamięci pamiętaj, aby przydzielić nowe „bity” przed uwolnieniem starych.

Edycja: W przypadku gier wieloosobowych można również serializować strukturę danych, kompresować ją i wysyłać przez sieć. Nie jest to najbardziej wydajne wykorzystanie sieci, ale zdecydowanie trudniejsze do wykrycia poprzez sniffowanie pakietów.

Jari Komppa
źródło
9

Co powiesz na przechowywanie zdrowia uzupełnionego losową wartością generowaną przy każdym przechowywaniu. To przynajmniej pokonałoby prosty mechanizm wyszukiwania rosnących / malejących wartości w pamięci, aby znaleźć zdrowie.

class Player
{
  int m_Health;
  int m_RandomHealth;
  Random m_Rnd = new Random();

  public int Health {
    get
    {
      return m_Health ^ m_RandomHealth;
    }

    set
    {
      m_RandomHealth = m_Rnd.Next();
      m_Health = value ^ m_RandomHealth;
    }
  }
}

Ponadto możesz zresetować wartość każdej klatki, nawet jeśli zdrowie się nie zmieni. Zapobiegnie to wyszukiwaniu przez użytkowników zmienionych adresów, gdy ich zdrowie się zmieni, i pozostaną takie same, gdy zdrowie pozostanie takie samo.

Premia: Stwórz nowy obiekt za każdym razem, gdy zmienia się wartość pamięci, i ustaw zdrowie, aby zmieniać losową liczbę, wartość xored i adres w pamięci za każdym razem, gdy ją czytasz :

class Player
{
  int [] m_HealthArray; // [0] is random int, [1] is xored health
  Random m_Rnd = new Random();

  public Player() {
    this.Health = 100;
  }

  public int Health {
    get
    {
      int health = m_HealthArray[0] ^ m_HealthArray[1];
      Health = health; // reset to move in memory and change xor value
      return health;
    }

    set
    {
      m_HealthArray = new int[2];
      m_HealthArray[0] = m_Rnd.Next();
      m_HealthArray[1] = m_HealthArray[0] ^ value;
    }
  }
}
Jason Goemaat
źródło
8

Chociaż pierwszy komentarz podjąłem w wątpliwość sensu poniżenia części odbiorców bez widocznego zysku, nadal uważam to za interesujące pytanie z technicznego punktu widzenia.

Właśnie wpadłem na ten pomysł: to, co robią oszustowie, to znajdowanie wartości, które je zmieniają i zamrażają. Wyszukiwanie odbywałoby się wtedy tylko między śmiercią lub wydarzeniami, które zmieniły zdrowie gracza. Co więcej, oszust może dopracować wyszukiwanie, odfiltrowując to, co się zmieniło, kiedy „nie umierał”.

Co jeśli licznik „zdrowia” zmienia się cały czas? Ustaw wskaźnik i przenieś go do każdej klatki lub każdej klatki N, jeśli wydajność jest zbyt duża. Lub XOR to z losową wartością, która zmienia każdą ramkę (XORing ponownie w stosunku do tej samej wartości do deszyfrowania przed szyfrowaniem z nową losową wartością).

Jeśli masz inne dane w grze, które zmieniają się przez cały czas (w tym pozycje xiy postaci gracza lub licznik czasu), może to utrudnić ustalenie, które ze wszystkich zmieniających się danych to zdrowie. A zamrożenie całego stanu gry nie jest oszustem.

W celu dalszego wprowadzenia w błąd, możesz przechowywać zdrowie w zwykłej zmiennej tylko do zapisu, oznaczonej jako garnek miodu.

Edytuj :

Mimo to oszust może próbować ustalić, która ze zmiennych, która zmienia się przez cały czas, jest tą, która zamrozi próbę i błąd. Możliwym rozwiązaniem byłoby połączenie ze sobą zmiennych.

Przykład:

Zamiast przechowywać zdrowie (h) i pozycję (x), przechowujesz je w dwóch zmiennych a i b, z których możesz później pobrać wartości:

a = x+h; b = x-h
x = (a+b)/2; h = (a-b)/2

W ten sposób, jeśli oszust zamrozi tylko jedną z nich, a następnie poruszy postać, wpłynie to na pozycję i, w zależności od tego, która została zamrożona, h zostanie ujemne (natychmiastowa śmierć). Możesz przełączać się między powyższymi formułami i:

a = x-h; b = x+h
x = (a+b)/2; h = (b-a)/2

W kolejnych klatkach gwarantujesz, że w co najwyżej 2 klatkach po zamrożeniu jednej ze zmiennych zdrowie zmieni się na 0 w chwili zmiany x. Pamiętaj, że przechowujesz tylko aib. Połącz to z ciągłym XOR, jak wspomniano powyżej. Rezultatem jest zbiór zmiennych, które zmieniają każdą klatkę na pozornie losowe wartości, a zamrożenie dowolnej pojedynczej lub ich części powoduje jedynie niepożądane skutki uboczne w grze, a jednym z nich jest natychmiastowa śmierć.

CeeJay
źródło
7

Myślę, że jedna z możliwych opcji może działać (nie znam się zbytnio na tych narzędziach do oszukiwania): nie ukrywaj tego. Wykonuj operacje i oczekuj określonych rezultatów. Po operacji (prawdopodobnie następnej klatce) sprawdź, czy wyniki są takie, jak się spodziewasz. Jeśli odejmiesz zdrowie, a następnie je zweryfikujesz, tylko po to, aby zdrowie nigdy się nie zmieniło, możesz być całkiem pewien (pod warunkiem, że nie masz jakiegoś niejasnego błędu), że został on zablokowany.

Jeśli wykryjesz oszustwo, możesz teraz swobodnie zmieniać zasady gry, jeśli uważasz, że to wystarczające, by maksymalnie rozpaczać, jeśli taki jest twój zamiar. Jeśli Twoim celem jest zignorowanie oszustwa i kontynuowanie działania gry, przenieś zdrowie w inne miejsce w pamięci i zmień wskaźniki, tak aby nowa wartość została wykorzystana, a stara została zignorowana. Powtórz ad infinitum.

James
źródło
6

Wszelkie próby zwykłego zaciemnienia z pewnością zawiodą w teorii, ale w praktyce wystarczy utrudnić przełamanie, że wyzwanie staje się mniej zabawne niż sukces w grze. Nie wiem, jaka jest twoja gra, więc nie będę ryzykować sugerowania metod zaciemniania danych, tym bardziej, że uważam, że są one bezcelowe.

To powiedziawszy, i prawdopodobnie nie polubisz tego, myślę, że szukasz kurtyny pamięci , która jest koncepcją Trusted Computing .

sam hocevar
źródło
3

Jedną z bardzo skutecznych technik stosowanych przez niektórych roguelike (DoomRL, Brogue) jest maskowanie rzeczywistych zmiennych. Oznacza to, że zamiast pokazywać zdrowie jako „2/10”, pokaż je jako „20%”.

Pamiętaj, że większość edytorów pamięci działa dobrze z konkretnym wyszukiwaniem / filtrowaniem wartości. Jeśli nie znasz konkretnej wartości, nadal możesz ją wyśledzić (np. Wyszukać zmienione / zmniejszone wartości po spadku zdrowia gracza), choć zajmuje to znacznie więcej czasu.

Sugeruję także luz anty-cheatowy. Na przykład, jeśli masz grę, w której gracz otrzymuje obrażenia dodatnie, a jego zdrowie nie spadło, możesz łatwo i dokładnie to sprawdzić i wiedzieć, że gracz oszukuje. Zrób coś interesującego, na przykład odradzania się wokół niego uberowi wrogowie :)

ashes999
źródło
2

Napisz sterownik pierścienia 0, który przechwytuje SSDT i rejestruje / blokuje, gdy w Twojej aplikacji zostanie wywołane ReadProcessMemory / WriteProcessMemory. Lepiej byłoby po prostu zalogować się, abyś mógł powoli zmieniać ich zachowanie w czasie, a nie tylko zawieszać się.

Doug-W
źródło
Wygląda na to, że mówisz o szczegółach implementacji niskiego poziomu (Windows API?) Pytam o ogólne strategie wysokiego poziomu.
ashes999
2

Dlaczego miałbyś powstrzymywać graczy przed oszustwami (co oznacza oszustwo w grze dla jednego gracza)? W środowisku dla wielu graczy zadaniem serwera jest wykrycie nienaturalnych zmian i przeciwdziałanie im (zazwyczaj poprzez zignorowanie danych wejściowych lub całkowite zablokowanie winowajcy z serwera), ale w środowisku dla jednego gracza nic się nie dzieje poza oszustem niedźwiedzia przysługa.

IOW, chyba że tworzysz grę wieloosobową, to strata pieniędzy, a jeśli myślisz o utwardzeniu złego miejsca.

jwenting
źródło
2

Zbadaj znane narzędzia do oszukiwania - i często sprawdzaj, czy którekolwiek z najczęstszych są wykrywane (sprawdź nazwy procesów?)

Jeśli jakieś zostaną wykryte, pozwól graczowi oszukiwać (jeśli jest offline, to nieszkodliwe), ale upewnij się, że żadne wyniki / osiągnięcia nie będą publikowane w żadnym internetowym systemie liderów / systemie osiągnięć - po prostu cicho zawieść?

Nie powstrzyma bardziej zdeterminowanych hakerów, ale zmniejszy ryzyko, że bardziej przypadkowe kody zepsują twoje tabele wyników.

(Może denerwować programistów, którzy mają otwarte i zminimalizowane narzędzia programistyczne do legalnych celów i robią sobie przerwę w grze ...)

bluescrn
źródło
7
Byłem wielkim fanem Steam jako platformy dystrybucyjnej i kilka lat temu grałem dużo w Team Fortress II. Pewnego wieczora zrobiłem sobie przerwę od długiej sesji programowania i zostawiłem otwarte Visual Studio i debugger podczas gry. Zostałem zbanowany z TF2 za „oszukiwanie”. Bez odwołania, bez ram prawnych do zniesienia zakazu. Jedno „wykroczenie” i jest stałe dla konta. Nadal nie mam pojęcia, dlaczego - i wierzcie mi, byłem wściekły - poza tym, że głęboko w FAQ widziałem, że posiadanie „edytora pamięci” podczas gry stanowiło oszustwo i było zabronione. Łał.
1

Myślę, że najprostszym rozwiązaniem jest wdrożenie opcji oszukiwania. Wyłącz punktowanie, gdy masz opcję oszukiwania.

Kolejny dobry pomysł, że nie mam pojęcia, jak byś wdrożył. Czy wyłączyć grę, gdy jest zamrożona zmienna pamięci lub coś takiego :)

Powodzenia (Y)

HgMerk
źródło
3
Wdrażanie kodów jest dobrą opcją. Zamykanie gry, nie tyle ... (wyobraź sobie, że pomylisz się i zamknij legalnego gracza).
ashes999