pprogramowanie;

Zwięźle na temat programowania

Facebook

Edycja pamięci procesów

25 lipca 2012, kategoria: C++

Windows udostępnia nam funkcje pozwalające odczytywać i zapisywać pamięć innych procesów. Służą do tego ReadProcessMemory i WriteProcessMemory. Przed odczytaniem pamięci procesu musimy uzyskać jego uchwyt (HANDLE) i posiadać odpowiednie prawa dostępu. Najlepszym darmowym programem do operacji na pamięci procesu jest CheatEngine oraz TSearch. Naszą aplikacją testową będzie Saper, ponieważ tę grę każdy posiada.

Znajdywanie adresów

Jest to artykuł skierowany raczej do początkujących, pominę sprawę ASLR oraz wskaźników. Zajmiemy się adresami statycznymi.

Każdy bajt aplikacji jest zapisany w pamięci procesu i posiada swój unikalny adres. Aby odczytać np. czas z gry saper musimy wiedzieć pod jakim adresem się znajduje. Otwieramy grę Saper a następnie program CheatEngine. Wybieramy z listy proces o nazwie winmine.exe. Teraz możemy przeprowadzać dowolne operacje na pamięci Sapera. Standardowo ilość min w grze Saper to 10 - a więc musimy znaleźć tą wartość. W polu Value wpisujemy wartość 10, Scan type należy ustawić na Exact Value, Value type proponuję ustawić na 4Bytes więcej nie będzie potrzebne. Skanujemy proces w poszukiwaniu wpisanej wartości, klikając na przycisk First Scan.

Prawdopodobnie adresów pojawi się dość dużo, dlatego przełączamy się na okienko sapera i zmniejszamy ilość min (klikamy prawym na dowolne pole). Ilość min została zmniejszona z 1 na 9. Powtarzamy czynność wyszukania wartości, wpisując w pole Value liczbę 9 (aktualna ilość min) i klikając w przycisk Next Scan. Czynność powtarzamy aż na liście nie zostanie tylko jeden adres (pamiętaj aby za każdym razem klikać Next Scan). Gdy mamy już tylko jeden adres, jest to z pewnością adres min w grze. Wartość możemy dowolnie modyfikować poprzez dwukrotne kliknięcie i wpisanie nowej wartości min. W celu znalezienia adresu odpowiedzialnego za czas, robimy dokładnie to samo jak wyżej. Skanujemy proces aż zostanie tylko jeden adres.

Znaleziony przeze mnie adres wskazujący na ilość min w Saperze to 1005194, Twój adres może się różnić jeżeli posiadasz inną wersję gry.

Odczytywanie wartości z pamięci

Znaleźliśmy interesujące nas adresy pamięci. Deklarujemy je w kodzie programu, należy pamiętać że są one w formacie heksadecymalnym, więc aby zadeklarować je w C++ należy dodać przedrostek 0x, w VisualBasic &H a w delphi $.

Szukamy okna o tytule „Saper” za pomocą funkcji FindWindow, zwraca ona hwnd okna. Jeżeli nie znajdzie okna (tzn aplikacja jest zamknięta) zwraca 0. Za pomocą funkcji GetWindowThreadProcessId pobieramy uchwyt (HANDLE) aplikacji, tutaj wymagany jest wcześniej odczytany hwnd. Następnie za pomocą OpenProcess otwieramy proces Sapera, określając prawa dostępu. Prawa dostępu można znaleźć w MSDN więc nie będę ich kopiował. Nas interesuje jedynie PROCESS_VM_READ, co pozwoli na odczyt.

Uwaga! Bądź precyzyjny używając flag dostępu w funkcji OpenProcess. Jeżeli chcesz odczytywać wartości użyj PROCESS_VM_READ, jeżeli zapisywać PROCESS_VM_WRITE | PROCESS_VM_OPERATION. Nigdy nie używaj PROCESS_ALL_ACCESS, jeżeli nie jest to wymagane! Flaga ta nadaje wszystkie uprawnienia i nie działa na wszystkich systemach bez praw do debugowania (to już funkcja SeDebugPrivilege). Konsekwencją tego będzie np. brak możliwości odczytywania pamięci na koncie ograniczonym w Windows Vista/7.

Kolejnym krokiem jest użycie naszej właściwej funkcji ReadProcessMemory. Jej najważniejsze argumenty to uchwyt otwartego procesu, adres bazowy, bufor do którego zapisujemy wartość i wielkość bufora. Funkcja jest typu BOOL tzn zwraca true lub false w razie niepowodzenia.

#include <iostream>
#include <windows.h>

using namespace std;

int main()
{
    int miny = 0x1005194; //adres min
    int czas = 0x100579C; //adres czasu
    
    long ilosc_czas = 0;
    long ilosc_miny = 0;
    
    DWORD processId;
    HANDLE hProcess;
    
    HWND hSaper = FindWindow("Saper", NULL); //hwnd okna
    
    if(hSaper) //jezeli gra jest uruchomiona
    {
        GetWindowThreadProcessId(hSaper, &processId); //pobieramy uchwyt
        hProcess = OpenProcess(PROCESS_VM_READ, false, processId);
        ReadProcessMemory(hProcess, (LPVOID)czas, &ilosc_czas, sizeof(long), 0); //odczytujemy
        ReadProcessMemory(hProcess, (LPVOID)miny, &ilosc_miny, sizeof(long), 0); //odczytujemy
        CloseHandle(hProcess);
    }
    
    cout << "Aktualna ilosc min: " << ilosc_miny << endl;
    cout << "Aktualna wartosc czasu: " << ilosc_czas << endl;
    
    system("PAUSE >nul");
    return(0);
}

Zapisywanie wartości do pamięci

Wszystko odbywa się tak jak w przypadku odczytywania. Używamy funkcji WriteProcessMemory. Określamy adres oraz wartość jaką chcemy zapisać. Zwróć uwagę na flagi OpenProcess, w przeciwieństwie do ReadProcessMemory tutaj wymagana jest jeszcze PROCESS_VM_OPERATION.

#include <iostream>
#include <windows.h>

using namespace std;

int main()
{
    int czas = 0x100579C; //adres czasu

    int nowy_czas = 3;

    DWORD processId;
    HANDLE hProcess;

    HWND hSaper = FindWindow("Saper", NULL); //hwnd okna

    if(hSaper) //jezeli gra jest uruchomiona
    {
        GetWindowThreadProcessId(hSaper, &processId); //pobieramy uchwyt
        hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, false, processId);
        WriteProcessMemory(hProcess, (LPVOID)czas, &nowy_czas, sizeof(int), 0); //zapisujemy
        CloseHandle(hProcess);
    }

    cout << "Czas zostal ustawiony na 3 sekundy" << endl;

    system("PAUSE >nul");
    return(0);
}

Podsumowanie

Poznanie działania funkcji służących do operacji na pamięci danego procesu, jest absolutną podstawą. Do bardziej zaawansowanych działań potrzebny Ci będzie debbuger oraz znajomość Assemblera. Nie musisz umieć programować w asmie, ja też nie umiem. Wystarczy znać podstawowe rozkazy, wiedzieć czym jest stos, rejestry oraz przerwania.

repozytorium: /cpp/edycja-pamieci-procesow.md

licencja: Creative Commons BY-SA 3.0

autor: Karol Trybulec

Wszystkie artykuły przechowywane są w repozytorium Github. Jeżeli uważasz, że coś można poprawić, możesz nanieść swoje zmiany. Wszelkie poprawki po zaakceptowaniu stają się automatycznie widoczne na blogu.