Skocz do zawartości

Pisanie cheatów na przykładzie gry AssaultCube w C#

Polecane posty

Autor tematu Napisano (edytowany)

Pisanie cheatów na przykładzie gry AssaultCube

                                                                      w języku C#

 

 

Na wstępie chciałbym zaznaczyć, że jest to pierwszy poradnik jakikolwiek napisałem w życiu, aczkolwiek mam nadzięje, że materiały tutaj zawarte okażą się przydatne.  Poniższa treść jest skierowana do osób, które posiadają podstawową wiedzę na temat programowania i nie będę tutaj tłumaczył podstawowych rzeczy.

 

Aby rozpocząć naszą prace na starcie należy się zaopatrzeć w kilka programów:

  1. Cheat Engine.
  2. IDE ( Według uznania, ja korzystam z Visual Studio )

 

Za pomocą programu 'Cheat Engine' będziemy szukać wskazników w pamięci, których potem będziem używać w naszym programie w C#.

 

Czym jest wskaźnik?

 

Wskażnik ang. pointer jest to taki typ danych, który przechowuje adres innej komórki w pamięci.

Najlepiej opisuje wskaznik poniższy obrazek.

 

Pointer

 

Wkazniki mogą mieć kilka poziomów np. wskaznik wskazuje na wskaznik, który wskazuje na wskaznik itd.

Często takie coś nam utrudnia prace podczas pisania.

 

 

 

Na początku musimy wybrać proces w cheat engine.

 

FWRWXZt.png

 

 

Następnie odszukanie pierwszej interesującej nas wartości, ja na początku odszukałem stan życia postaci.

 

Więc aby to zrobić w polu 'value' wpisujemy wartość 100 i klikamy 'first scan'.

W tym momencie waszym oczom ukarze się duża liczba wyników, dlatego też musimy troche zadać obrażeń naszej postaci.

 

VzPuyfI.png

 

 

Na czerwono widzimy, że wartość w pamięci się zmieniła i jest teraz na czerwono, u mnie postać ma 7 punktów życia i adres z taką wartością dodałem też do listy, dla pewności możemy podmienić tę wartość - Jeśli się zmieni mamy pewność, że adres jest prawidłowy.

 

Możemy tak zrobić i poszukać każdej wartości, która będzie potrzebna do naszego cheata, jedna te adresy zmieniają się za każdym razem kiedy uruchomimy gre, dlatego potrzebujemy znalezc wskaznik, który jest statyczny - Jest stały dla konkretnego Buildu gry.

 

 

Szukanie wskaźników

Aby znaleźć wskaznik, musimy sprawdzić jaka instrukcja może wpisywać do tej komórki w pamięci, w tym celu zaznaczmy dynamiczny adres i klikamy f5, Nic na razie nam się nie pokaże ponieważ musimy jeszcze trochę zadać obrażenia naszej postaci.

 

 

M1HPwGJ.png

 

Widzimy, że instrukcją, która ma dostęp do zapisu tej pamięci jest to instrukcja 'mov [edx + 00000F8], eax'

Operacja ta kopiuje wartość z rejestru eax do rejestru edx przy tym dodająć offset 0xF8.

 

offset jest to najprościej mówiąć 'scieżka' do odnalezienia wskaznika do dynamicznej komórki w pamięci. Jednak do tego potrzebujemy adres Entity gracza.

 

Klikająć 'More information' dostaniemy bardziej szczegółowe informacje na temat tego adresu.

 

G0CB17N.png

 

Możemy tutaj przeczytać, że prawdopodobnie wartość wskaznika potrzebna do odnalezienia tego wskaznika jest to '0279A1C0' i w tym przypadku tak też jest.

 

Dlatego też kopiujemy ten adres i w polu 'value' zaznaczmy 'hex'  wklejamy ten adres i klikamy 'first scan'

 

ysO3Mbw.png

 

 

Po zakończeniu procesu skanowania naszą uwagę na pewno przyciągną zielone adresy, są to statyczne adresy (W tym przypadku Entity Gracza), które posłużą nam do odnalezienia wskaznika.

 

h8esuK9.png

 

Jeden z tych zielonych adresów jest to adres, którego szukamy jest nim 'ac_client.exe+10F4F4' dodajemy go do listy poniżej.

Widzymy, że po dodaniu do listy w kolumnie adres pokazuje wartość '0050F4F4' ta wartość będzie nam potrzebna aby wydobyć wskaznik życia.

 

Przechodzimy teraz do 'Memory View' klikamy CTRL+D w polu "Group 1" wpisujemy adres, który wcześniej skopiowaliśmy i klikamy CTRL+N

Naszym oczom ukaże się lista z zielonym kolorem tekstu, to jest właśnie to czego szukaliśmy.

 

Tutaj będziemy szukać, które offsety będziemy pózniej używać w naszym programie.

 

H34GDxD.png

Znalazłem odpowiednie wartości i je nazwałem, następnie dodajemy je do swojej listy i zapisujemy ją.

Wartości po lewej od myślnika są to offsety.

 

 

Wszytkie nasze pobrane wskazniki powinny wyglądać mniej więcej tak:

 

45AcmCX.png

 

 

Dane zebrane, pora na pisanie kodu!

Stwórzmy w Visual Studio nowy projekt 'Windows forms app', Ja nazwe go 'BasicCheat'

następnie musimy utworzyć jakieś gui dla cheat'a. W tym celu należy otworzyć plik Form1.cs i z lewej klikamy 'toolbox' tworzymy nasz interface, ja stworzyłem coś takiego:

 

SwdSoZM.png

 

 

Warto też nadać sobie im odpowiednie nazwy, przez które będziemy się odnosili do nich w kodzie, aby było po prostu łatwiej zapamiętać je.

Po kliknięciu dwa razy na obiekty, które dodaliśmy w designerze środowisko automatycznie utworzy nam odłowania do nich w kodzie.

 

 

Po tych operacjach kod będzie wyglądał tak:

using System;
using System.Windows.Forms;

namespace BasicCheat {
  
    public partial class Form1 : Form {
      
        public Form1() {
            InitializeComponent();
        }

        private void enable_Click(object sender, EventArgs e) {

        }

        private void disable_Click(object sender, EventArgs e) {

        }

        private void exit_Click(object sender, EventArgs e) {

        }

        private void health_CheckedChanged(object sender, EventArgs e) {

        }

        private void checkBox2_CheckedChanged(object sender, EventArgs e) {

        }

        private void ammo_CheckedChanged(object sender, EventArgs e) {

        }

        private void granades_CheckedChanged(object sender, EventArgs e) {

        }
    }
}

Zacznijmy od stworzenia sobie zmiennych, które nam będą potrzebne do interakcji z GUI

Tworzymy je na początku klasy jako jej pola.

 

private bool unlimitedHealth = false;
private bool unlimitedArmour = false;
private bool infiniteGranades = false;
private bool infiniteAmmo = false;

 

Kiedy już je mamy zaczniemy z nimi pracować, zacznijmy od funkcji wyłączenia naszej aplikacji, Musiby obsłużyć wyłączenie przez kliknięcie przyciusku 'Exit' oraz przez zamknięcie X'em. 

// Metoda zamykająca aplikacje, wraz z zabiciem wszystkich jej podprocesów.
private void ApplicationExit() {
  System.Diagnostics.Process.GetCurrentProcess().Kill();
  Application.Exit(); 
}

// Nadpisanie metody wbudowanej, po kliknięciu X'a
protected override void OnFormClosing(FormClosingEventArgs e) {
   ApplicationExit();
}

// Po naciśnięciu przycisku exit
private void exit_Click(object sender, EventArgs e) {
   ApplicationExit();
}

Teraz obsłużmy przyciski enable i disable.

 

 // Po kliknięciu przycisku 'enable';
        private void enable_Click(object sender, EventArgs e){
            processes = Process.GetProcessesByName("ac_client");                // Pobieramy proces o nazwie 'ac_client';
            Thread thread = new Thread(cheatMain);                      // Definicja wątku, w którym będzie uruchomiona
                                                                            // główna funkcja;

            enabled = true;         									// Uruchomienie cheata;


            // Jeśli długość zwróconego przez metode GetProcessesByname() nie równa się 0 - czyli proces istnieje;
            if(processes.Length != 0) {
                thread.Start();           								// Uruchomienie nowego wątku;

                statusValue.Text = "enabled";       					// Zmiana tekstu na 'enabled';
                statusValue.ForeColor = System.Drawing.Color.Green;         // Zmiana koloru tekstu na zielony;
            } else {  // W przeciwnym wypadku
                statusValue.Text = "process not found";  					// Zmien tekst;
                statusValue.ForeColor = System.Drawing.Color.DarkRed; 		// Zmien kolor;

                enabled = false; 								// Wyłącz cheata;
            }
            
        }
 // Po kliknięciu przycisku disable;
private void disable_Click(object sender, EventArgs e){
  if(processes.Length != 0) {
    enabled = false;
    statusValue.Text = "disabled";				
    statusValue.ForeColor = System.Drawing.Color.Red;
  }
}

Przejdzmy do pobrania wartości z checkboxów i odpowienio ich przechowania w zmiennych, które stworzyliśmy wyżej.

 

// Po interakcji z checkboxem
private void health_CheckedChanged(object sender, EventArgs e){
  if(health.Checked){      					// jeśli wciśniety
    unlimitedHealth = true;					// ustaw wartość zmiennej na true;
  } else { 						// Jeśli nie
    unlimitedHealth = false;			// na false;
   }
}

 

 

Robimy to samo dla reszty.

 

private void armour_CheckedChanged(object sender, EventArgs e) {
  if(armour.Checked) {
    unlimitedArmour = true;
  } else {
    unlimitedArmour = false;
  }
}

private void granades_CheckedChanged(object sender, EventArgs e) {
  if(granades.Checked) {
    infiniteGranades = true;
  } else {
    infiniteGranades = false;
  }
}

private void ammo_CheckedChanged(object sender, EventArgs e) {
  if(ammo.Checked) {
    infiniteAmmo = true;
  } else {
    infiniteAmmo = false;
  }
}

Doszliśmy prawie do końca, teraz wszystko co musimy zrobić to stworzyć kolejnych pól(zmiennych) w klasie

oraz dodać bilbioteke w rozszerzeniu dll do naszego projektu. biblioteke pobieramy tutaj.

 

Przechodzimy do okienka po lewej stronie naszego środowiska klikamy prawym na 'References' i add reference z dołu klikamy browser i wybieramy VAMemory.dll z dysku.

 

Aby wszystko poprawnie działało trzeba jeszcze dodać plik manifest do projektu, można to zrobić klikająć new > file i wybieramy manifest

Podmieniamy linijke

<requestedExecutionLevel level="Invoker" uiAccess="false" />

na

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

 

Deklaracje zmiennych w naszej klasie powinny wyglądać tak:

 

private Process[] processes = null;         // Obiekt process, który jest potrzebny do sprawdzenia;
// czy proces jest uruchomiony;

private IntPtr processBaseAdress = IntPtr.Zero;                 // Statyczny wskaznik procesu;
private IntPtr playerBaseAddress = IntPtr.Zero;                 // Statyczny wskaznik Etity Gracza;

// Wartości, które się zmienią po wybraniu odpowiednich opcji w gui;
private bool unlimitedHealth = false;
private bool unlimitedArmour = false;
private bool infiniteGranades = false;
private bool infiniteAmmo = false;

// Cheat włączony lub wyłączony
private bool enabled = true;

private int value = 0x3E7; // Wartość, którą  będziemy wpisywać do pamięci, 
// jest to 999 w systemie dziesiętnym;

 

Teraz bierzemy się za metode główna cheat'a w której będziemy edytować wartości w pamięci.

private void cheatMain() {
  VAMemory memory;             // Deklaracja obiektu, który będzie nam potrzebny do edycji pamięci;

  var offsets = new {                             // Anonimowa klasa z offsetami, które pobralismy z 
    health = 0xF8,                                    // Cheat engine. Można je sprawdzić klikając na odpowiedni wskaznik
    armour = 0xFC,

    granades = 0x158,

    AssaultRifleAmmo1 = 0x150,
    SniperRifleAmmo1 = 0x14C,
    PistolAmmo1 = 0x13C,

    AssaultRifleAmmo2 = 0x128,
    SniperRifleAmmo2 = 0x124,
    PistolAmmo2 = 0x114,
  };

  Process cproc = processes[0];                           // pobranie procesu;

  foreach(ProcessModule module in cproc.Modules) {               
    if(module.ModuleName.Contains("ac_client")) {               
    	processBaseAdress = module.BaseAddress;                 // Przypisanie adresu procesu do zmiennej;
    }
  }

  memory = new VAMemory("ac_client");          

  playerBaseAddress = (IntPtr) memory.ReadInt32(processBaseAdress + 0x0010F4F4);          // Pobranie wskaznika do Entity Gracza

  while(enabled) {                        						// Jeśli przycisk enable został wcześniej wciśnięty

    if(unlimitedHealth) {                   						// Jeśli zaznaczono checkbox
      memory.WriteInt32(playerBaseAddress + offsets.health, value);               // Zapisz do pamięci wartość 
    }

    if(unlimitedArmour) {
      memory.WriteInt32(playerBaseAddress + offsets.armour, value);
    }

    if(infiniteAmmo) {
      memory.WriteInt32(playerBaseAddress + offsets.AssaultRifleAmmo1, value);
      memory.WriteInt32(playerBaseAddress + offsets.AssaultRifleAmmo2, value);
      memory.WriteInt32(playerBaseAddress + offsets.SniperRifleAmmo1, value);
      memory.WriteInt32(playerBaseAddress + offsets.SniperRifleAmmo2, value);
      memory.WriteInt32(playerBaseAddress + offsets.PistolAmmo1, value);
      memory.WriteInt32(playerBaseAddress + offsets.PistolAmmo2, value);
    }

    if(infiniteGranades) {
      memory.WriteInt32(playerBaseAddress + offsets.granades, value);
    }

	Thread.Sleep(100);
  }
}

 

 

Dobrneliśmy do końca tego 'poradnika' Mam nadzięję, że przybliżyło to wam mniej więcej jak przebiega proces pisania cheatów do gier.

Kod zródłowy możecie sobie pobrać i  dostępny jest na moim repozytorium: https://github.com/sectasy0/gcexample

 

Wszelkie pytania odnośnie kodu itp możecie kierować do mnie w wiadomościach prywatnych jak i pod tym tematem.

 

Edytowano przez _sectasy

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Pani Kasia

Bądź aktywny! Zaloguj się lub utwórz konto

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto

Zarejestruj nowe konto, to proste!

Zarejestruj nowe konto

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się

  • Kto przegląda   0 użytkowników

    Brak zalogowanych użytkowników przeglądających tę stronę.

×
Okienko zamknie się za 5 sekund...