Skocz do zawartości
  • 👋 Witaj na MPCForum!

    Przeglądasz forum jako gość, co oznacza, że wiele świetnych funkcji jest jeszcze przed Tobą! 😎

    • Pełny dostęp do działów i ukrytych treści
    • Możliwość pisania i odpowiadania w tematach
    • System prywatnych wiadomości
    • Zbieranie reputacji i rozwijanie swojego profilu
    • Członkostwo w jednej z największych społeczności graczy

    👉 Dołączenie zajmie Ci mniej niż minutę – a zyskasz znacznie więcej!

    Zarejestruj się teraz

Nomadmemory, wykorzystywanie baseadresow i offsetow


Rekomendowane odpowiedzi

Opublikowano

Dzień dobry. Postanowiłem napisać bota z wykorzystaniem odczytywania adresów, jednak mam pewien dylemat.

Chodzi o to, że chcę odczytać wartość HP z gry, jednak adres za każdym razem jest inny, więc postanowiłem poszukać bazowego adresu, choć nie wiem czy robię to dobrze. Korzystałem z tego:


Nie wiem czy w ogóle o to chodzi, jednak jakieś tam te offsety są, tylko jak teraz wykorzystać to w AutoIT?

No wiec na przykład jak na tym filmie wyżej mam takie coś http://prntscr.com/6eowlb

Czy to wystarczy?

 

Tzn, nie chcę marnować tak dużo czasu żeby wykonywać te zadania dla kilku adresów, jeśli nawet nie wiem czy chodzi o te pointery, bo później jeśli uzyskam ten baseadres to domyślam się, że wystarczy podstawić do tego:

 

(dane stąd http://prntscr.com/6eowlb )

Global $PID = ProcessExists("costam.costam") 

Const $offset[3] = [158, 1C, 77C]

$memarray = _MemoryOpen($PID)
While 1
$mem =_MemoryPointerRead(0x000102C0,$memarray, $offset) 
WEnd
_memoryclose($CID)

Czy takie coś powinno zadziałać? Czy szukam tego złą metodą?

Bot do gry NosTale:


[bOT] NosBota 1.3v

Opublikowano

Źle, bo nie rozumiesz wskaźników. Postaram się łatwo wytłumaczyć:

Każda zmienna ma jakiś adres w pamięci. W większości przypadków poszukiwana przez nas zmienna nie ma stałego adresu i jest on inny po każdym wczytaniu/zrestartowaniu gry. Są zmienne z takimi(stałymi) adresami, ale przeważnie są to wskaźniki. Wskaźnik to taka zmienna, którą wartością jest adres innej zmiennej. W tym przypadku:

  • Masz adres bazowy pewnego modułu aplikacji(bass.dll) i wiesz że pierwszy z szukanych wskaźników jest 0x102C0 bajtów za nim(to jest offset).
  • Pod tym adresem znajduje się pierwszy wskaźnik. Ale nie wskazuje on bezpośrednio na drugi, tylko na wartość 0x158(kolejny offset) bajtów przed nim.
  • Mamy już drugi, ale to też nie jest wskazujący idealnie na trzeci. Wskazuje on na jakąś wartość, a szukany wskaźnik jest 0x1C(itd.) bajtów przed nią.
  • Mamy już trzeci, ale to jeszcze nie jest koniec. Do wartości wskazywanej przez niego należy dodać 0x77C i dostajemy adres czwartego wskaźnika.
  • Wartością czwartego wskaźnika jest już szukana wartość.

Bez implementacji cache-owania(jeszcze trudniej) za każdym odczytem/ustawieniem musisz przelecieć wszystkie te warstwy. Lista kroków "dobierania się" do wartości:

  1. Pobierz adres bazowego modułu(bass.dll)
  2. Odczytaj wartość komórki pamięci, która znajduje się 0x102C0 bajtów za nim
  3. Wartość ta jest kolejnym adresem, należy do niej dodać 0x158 i odczytać wartość będącą pod tym adresem
  4. Wartość ta jest kolejnym adresem, należy do niej dodać 0x1C i odczytać wartość będącą pod tym adresem
  5. Wartość ta jest kolejnym adresem, należy do niej dodać 0x77C i odczytać wartość będącą pod tym adresem
  6. Wartość ta jest adresem szukanej liczby

Oczywiście dla dowolnej innej wartości proces ten będzie inny(inny moduł, inne offsety, inna ilość kroków).

 

Generalnie wskaźniki i grzebanie w pamięci to bardzo niskopoziomowa i dość skomplikowana rzecz, przed automatycznym rozpracowywaniem wielowarstwowych wskaźników warto poćwiczyć na prostszych rzeczach. Najpierw poćwicz samo pisanie w AutoIT(przerzucenie się na język w którym bardziej to wszystko widać, np. C++ też by bardzo ci pomogło) oraz pracowanie z pamięcią innych procesów(bo nie zaczyna się od wielowarstwowych wskaźników, uruchom sobie Tutorial dostarczany z Cheat Engine'm i samodzielnie rób kolejne kroki) - inaczej szybko się pogubisz w tym wszystkim.

Opublikowano

@Fireho

Ok, no to trochę bardziej skomplikowane niż mi się wydawało, myślałem, że do base adresu wystarczy dodać tylko te wszystkie offsety i mamy adres. Co do pisania w innych językach to raczej w drugą stronę, piszę więcej w c++ a w autoit chciałem wykonać tylko ten jeden konkretny projekt. A to video jest w ogóle na temat, czy oglądałem całkiem coś innego?

Bot do gry NosTale:


[bOT] NosBota 1.3v

Opublikowano

@Fireho

Ok, no to trochę bardziej skomplikowane niż mi się wydawało, myślałem, że do base adresu wystarczy dodać tylko te wszystkie offsety i mamy adres. Co do pisania w innych językach to raczej w drugą stronę, piszę więcej w c++ a w autoit chciałem wykonać tylko ten jeden konkretny projekt. A to video jest w ogóle na temat, czy oglądałem całkiem coś innego?

Jeśli piszesz w C++ to wiesz o co chodzi z wskaźnikami jako takimi, a jeśli wiesz jak rozmieszczana jest pamięć w klasach, to powinieneś to skojarzyć z kolejnymi wskaźnikami i offsetami(jak nie wiesz to pisz a wyjaśnię). Film jest na temat używania pointer scannera, a to jest po prostu sposób na szybkie i łatwe znajdowanie adresów bazowych i offsetów.

Opublikowano

@Fireho

Co do wskaźników, to "jako tako" wiem o co chodzi, natomiast z offsetami spotykam się pierwszy raz, jednak domyślam się, że podobnie jak base adres nie zmieniają się po zrestartowaniu gry.

Właśnie tak jak napisałem na początku, kombinowałem z tym dodawaniem offsetów do tego base adresu, ale widzę, że raczej nie robiłem tego tak, jak powinienem. :P

Bot do gry NosTale:


[bOT] NosBota 1.3v

Opublikowano

A więc:


Załóżmy przykładowe:

class Bohater {
CObrazek* img;
CEkwipnek* eq;
CStan* statsy;
};

Obiekt takiej klasy zajmuje równo 12 bajtów, bo dane są umieszczane po kolei. Dlaczego? Gdybyśmy spróbowali zadeklarować wskaźnik Bohater* wskaznik; to byłby to wskaźnik na początek takiego obiektu. Pierwsze cztery bajty od tego wskaźnika zajmowałby img(offset 0x0, wskaźnik przechowywany jest w bajtach znajdujących się {0x0, 0x1, 0x2, 0x3} bajtów od wskaźnika na obiekt), kolejne 4 eq(0x4, {0x4, 0x5, 0x6, 0x7}) i ostatnie 4 statsy(0x8).
Więc tak naprawdę zapis:

wskaznik->statsy;

Znaczy:

*(wskaznik + offsetPolaStatsy);

Czyli 8 bajtów po wartości wskaźnika. I właśnie to jest offset.

A w czytaniu pamięci przekłada się to na to:

  • Mamy bazowy wskaźnik, który zawsze będzie w tym samym miejscu(bass.dll + 0x102C0)
  • Wskazuje on na obiekt jakieś klasy A. Posiada on(obiekt) pole APOLE(nazwy fikcyjne ofc.), które ma offset 0x158.
  • APOLE jest wskaźnikiem na obiekt klasy B. Posiada on pole BPOLE, które ma offset 0x1C.
  • itd..

Rozumiesz? Mamy dostępne wskaźniki na jakiś obiekt, wiemy w którym jego polu znajduje się kolejny wskaźnik, który jest wskaźnikiem na jeszcze inny obiekt itd.. I z grubsza dlatego są offsety.

Owszem, robiłeś to źle. Przykład czegoś co działa:

 

template <typename TYP> Memory::Pointer<TYP>::operator TYP() {
	void* temp = data;
	unsigned i;
	for (i = 0; i < (offsets.size()-1); ++i)
		temp = mem->Read<void*>(temp, offsets[i]);
	return mem->Read<TYP>(temp, offsets[i]);
}

 

Nie jest to co prawda AutoIT, ale chodzi o zasadę działania. Czyli po kolei odczytujemy kolejne wskaźniki, dodajemy do nich kolejne offsety, wychodzi nam kolejny wskaźnik i to samo. Do czasu aż dostaniemy adres interesującej nas wartości.

Opublikowano

@Fireho

Ok chyba trochę łapię, jednak nadal trochę nie rozumiem drugiej części:

template <typename TYP> Memory::Pointer<TYP>::operator TYP() {
    void* temp = data;
    unsigned i;
    for (i = 0; i < (offsets.size()-1); ++i)
        temp = mem->Read<void*>(temp, offsets[i]);
    return mem->Read<TYP>(temp, offsets[i]);
}

ta pętla jest w dobrym miejscu? Przecież za każdym razem przypisuje się do zmiennej temp inna wartość, czyli równie dobrze można by było od razu przypisać ostatni element tablicy offsets(?)

A tak podsumowując, to wychodzi tak jak mówiłem(?) do tego base adresu powinienem dodać offset 0, później do tego co wyjdzie (do tego adresu który otrzymam) offset 1 i tak dalej do końca offsetów? Czy nadal źle to rozumiem?

Bot do gry NosTale:


[bOT] NosBota 1.3v

Opublikowano

 

 

@Fireho
Przecież za każdym razem przypisuje się do zmiennej temp inna wartość, czyli równie dobrze można by było od razu przypisać ostatni element tablicy offsets(?)

Nie. Może graficzne przedstawienie z pięknymi kolorami tęczy pozwoli ci to zrozumieć. Wygląda to tak:

586691425998614699239.png

 

  •  
  • Jakiś fragment pamięci, nie wiemy jaki
  • Nasz stały adres danego modułu, możemy go pobrać z systemu. Jest to nasz punkt odniesienia, bo poza nim nie znamy niczego poza offsetami.
  • Nasz pierwszy offset, określa jak daleko od adresu bazowego 2 znajduje się wskaźnik 4
  • Jest to pierwszy wskaźnik, wskazuje on na jakieś miejsce w pamięci
  • Zupełnie inny fragment pamięci
  • Nieznane miejsce w pamięci. Nie jest ono stałe ani przypisane do konkretnego modułu, więc nie możemy go pobrać z systemu. Wiemy natomiast że jest ono wskazywane przez wskaźnik 4, i to wszystko co możemy o tym miejscu powiedzieć.
  • Drugi offset! Określa on jak daleko od miejsca 6 znajduje się kolejny wskaźnik.
  • Drugi wskaźnik, wskazuje on na jeszcze kolejne miejsce w pamięci.
  • Zupełnie inny fragment pamięci, to ten docelowy.
  • Znowu jakaś nieznana pozycja w pamięci. Też nie możemy jej zapamiętać jak offsetów/pobrać z systemu jak adresu bazowego modułu, tylko musimy odczytać ją z wskaźnika 8.
  • Trzeci offset. Określa on jak daleko od pozycji 10 znajduje się szukana wartość 12.
  • Szukana wartość.

Oczywiście to przykładowy przypadek. Kolejne wskaźniki należy odczytywać przez _MemoryRead i dodawać do nich kolejne offsety - tak jak ja to zrobiłem.



Wybacz, na samym początku tematu nie doczytałem dokładnie tego kodu co podałeś. Skoro MemoryPointerRead(myslałem że zwykłe MemoryRead) robi to wszystko za ciebie, to możesz w gruncie rzeczy się tym wszystkim nie przejmować. Co prawda kod i tak jest zły(liczby w systemie szesnastkowym zapisuje się z przedrostkiem 0x, ta pętla nigdy się nie skończy, nigdzie nie używasz $mem). Ale wybacz pomyłkę.

Opublikowano

@Fireho

No ok, ale skoro nie znamy tego miejsca szóstego (mówię tak jak przedstawiłeś je na obrazku), to jak mamy do niego dodać drugi offset? Do czego mamy go dodać skoro nie znamy wartości numer 6? A może nie musimy go znać? Czy to ten wskaźnik numer 4 wskazuje na punkt szósty? Tzn pole nr 6 to pole nr 4 (te same wartości)?

Btw. Te adresy i offsety można szukać tak jak tutaj w punkcie 8?

http://www.mpcforum.pl/topic/249928-tut-pierwszy-raz-z-cheat-engine/

Bot do gry NosTale:


[bOT] NosBota 1.3v

Opublikowano

 

@Fireho

No ok, ale skoro nie znamy tego miejsca szóstego (mówię tak jak przedstawiłeś je na obrazku), to jak mamy do niego dodać drugi offset? Do czego mamy go dodać skoro nie znamy wartości numer 6? A może nie musimy go znać? Czy to ten wskaźnik numer 4 wskazuje na punkt szósty? Tzn pole nr 6 to pole nr 4 (te same wartości)?

Btw. Te adresy i offsety można szukać tak jak tutaj w punkcie 8?

http://www.mpcforum.pl/topic/249928-tut-pierwszy-raz-z-cheat-engine/

Powtarzam, nie zauważyłem że używasz jakiegoś _MemoryPointerRead, które robi to za ciebie. Wybacz za przypadkowe utrudnianie ci życia, a błędy w kodzie wypisałem w edycji posta wyżej.

 

A co do samego pytania, to adres 6 oczytujemy z wskaźnika 4(ale jak już mówiłem, nie musisz się tym przejmować bo biblioteka robi to za ciebie).

Opublikowano

@Fireho

Wiem, kod był tylko taki przykładowy, ale nadal nie rozumiem za bardzo którego offseta mam użyć, no bo na przykład rozważmy ten przypadek:

http://prntscr.com/6eowlb

Mam ten bazowy adres (chyba) 000102C0

I teraz mam:

$mem =_MemoryPointerRead(0x000102C0,$memarray, $offset) 

$memarray to wiadomo, natomiast co z tablicą offsetow? w końcu chyba nie wysyłam całej tablicy tylko mam wysłać jeden jej element, czyli np. &offset[1], który offset mam wysłać? A może to jest zapisane poprawnie, tzn funkcja przyjmuje całą tablicę offsetow? Bo coś właśnie to nie za bardzo chce działać :P

A co do tego "błędu" to chyba nawet lepiej, przynajmniej się zainteresowałem tą pamięcią i trochę o tym poczytałem.

Bot do gry NosTale:


[bOT] NosBota 1.3v

Opublikowano

Ok, przykładowy kod wygląda tak:

#include <NomadMemory.au3>

$pid = ProcessExists("minesweeper.exe")
Local $off[2] = [NULL, 0x120]
$mem = _MemoryOpen($pid)
$val = _MemoryPointerRead(0xFF79AA38, $mem, $off)
MsgBox(0, "test", $val[1])
_MemoryClose($mem)

Oczywiście przeklejony nie będzie działać u ciebie bo zmieni się adres bazowy(0xFF79AA38) i ew. system.

 

Po minucie czytania opisu funkcji(warto czytać te biblioteki .au3) dowiedziałem się że:

  • Pierwszym elementem tablicy z offsetami ma być NULL
  • _MemoryPointerRead zwraca dwuelementową tablicę, z czego pierwszy element([0]) zawiera adres właściwej wartości, a drugi([1]) samą wartość
Opublikowano

@Fireho

Ok, czytałem ten opis i wiedziałem o tych elementach i returnie, tylko właśnie zastanawiałem się czemu funkcja przyjmuje całą tablicę, wydawało mi się to trochę nielogiczne, no ale widocznie tak jest. Ok, dzięki za wszystko, w niedługim czasie postaram się pobrać te adresy do interesującej mnie gry. Jeszcze raz dzięki.

Bot do gry NosTale:


[bOT] NosBota 1.3v

Zarchiwizowany

Ten temat przebywa obecnie w archiwum. Dodawanie nowych odpowiedzi zostało zablokowane.

×
×
  • Dodaj nową pozycję...