Terve vaan kaikille..
Elikkäs, olen tässä väsäillyt tämän päivää tälläistä ohjelmien muisti editoria.
Eli tarkoituksena on, että on jokin ohjelma jossa on esim. luku 15 ja tällä minun ohjelmallani olisi tarkoitus päästä muokkaamaan tuota 15 vaikka luvuksi 20. Noh tämähän toteutetaan ohjelmallani niin, että käyttäjä syötää haku kenttään 15 ja painaa nappia "Scan". Nytten ohjelmani alkaa tutkimaan muisti osotteita väliltä 0x00000000 - 0xFFFFFFFF ja poimii ylös sieltä kaikki muistipaikat joissa löytyi luku 15.
...nytten törmäämmekin ongelmaan. Eli kun ohjelmani alkaa etsimään tuota lukua 15 niin ohjelma lopettaakin vastaamisen, koska 4294967295 muisti paikan tutkimiseen kestää armottoman kauan. Eli onko kellään tietoa, miten saisin mah. nopeasti löydettyä nuo kaikki muistipaikat joista löytyy luku 15.
Saman tyylisiä ohjelmia esim. (ArtMoney, CheatEngine).
Kiitos....
Ylemmät kaks gigaa 0x80000000 -> 0xFFFFFFFF voit unohtaa kokonaan, kun se osuus on kernelin puolella. Alemmasta kahdesta gigasta on suurin osa tyhjää, joten ei kannata sitä tyhjää käydä läpi. Voit käyttää vaikka VirtualQueryEx APIa selvittäessä mistä kohdasta muistia löytyy dataa ja mistä ei. Muisti on jaettu pageihin, eli yleensä 0x1000 tavun kokoisiin osuuksiin. Pagen koko voi hyvin eksoottisissa tapauksissa vaihdella, joten otetaan sekin huomioon. Muistin läpikäyminen vois näyttää kutakuinkin tältä:
MEMORY_BASIC_INFORMATION mbiBuffer; SYSTEM_INFO si; GetSystemInfo(&si); DWORD dwAlku = 0x00000000, dwLoppu = 0x7FFFFFFF; DWORD dwNyt = dwAlku; while(dwNyt < dwLoppu) { if(!VirtualQueryEx(hProcess, (LPVOID)dwNyt, &mbiBuffer, sizeof(MEMORY_BASIC_INFORMATION))) { // virhe break; } if(mbiBuffer.State != MEM_FREE) // lötyykö dataa? kokeile == MEM_COMMIT, jossei tää toimi { // nyt voitas johonkin vektoriin heittää // tän alueen baseaddress ja koko, tai // suorittaa sen arvon ettiminen suoraan tässä. // miten vaan parhaaksi näet dwNyt += mbiBuffer.RegionSize; // RegionSize = tämän muistialueen koko (voi olla useampia pageja) } else dwNyt += si.dwPageSize; // ei dataa tässä kohdassa, seuraava page }
Samalla voitais halutessa myös skipata paget joiden suojaus on PAGE_READONLY tai jokin vastaava, niistä kun tuskin mitään mielenkiintoista löytyy. Sitten kun on tiedossa mistä kannattaa etsiä, niin datan lukemiseen voidaan käyttää ReadProcessMemoryä tai ZwMapViewOfSection APIa. En tiedä onko näiden välillä merkittäviä nopeuseroja, mutta RPM:ää käyttäessä kannattaa lukea koko region/muistialue kerralla puskuriin ja sitten aloittaa etsiminen. Hakualgoritmeista en osaa yhtään mitään sanoa, mutta riippuu minkä kokoista ja muotoista muuttujaa/arvoa ollaan ettimässä.
Elikkäs siis kun tiedot on löydetty pitääkö minun luea RPM:llä tuo BaseAddressi tyyliin...
if(window.mbiBuffer.State != MEM_FREE) { ReadProcessMemory(window.handle, (void*)window.mbiBuffer.BaseAddress, (void*)&window.value, window.mbiBuffer.RegionSize, &window.bytes); window.curAddr += window.mbiBuffer.RegionSize; }
...tällä hetkellä RPM antaa valueeseen koko ajan tuloksen 2020893505 hex:nä 0x78746341. Sitten vielä, kun esim. Windows Pinballissa pisteiden osoite on 0x01F1D212, niin tällä hetkellä koodini scannaus ei missään kohdassa edes tarkista tuota muisti kohtaa???
Tossa testailin niin ihan hyvältä näytti: http://nopaste.com/p/aLuwZ7Olpb/txt. Tuosta muutes hyvin näkee, että jos skannaa vain alueen 0x00000000 -> 0x6FFFFFFF, niin nopeus noin kaksinkertaistuu. Yleensä kaikki muisti 0x6FFFFFFF:n yläpuolella kuuluu järjestelmän DLL:ille, joten sen voi suht huoletta jättää myös pois :D
Tarkoitus olis tosiaan lukea tuhansia tavuja kerralla, ja kuvittelisin että yrität änkeä kaiken neljän tavun muuttujaan. Varaa muistia RegionSizen kokoinen pötkö, johon luet kaiken datan ja käyt sen vaikka tavu tavulta läpi.
Joo ymmärrän kyllä mitä tarkoitat periaatteessa, mutta tuon tavu tavulta lukeminen on vielä vähä auki. Eli siis olisiko se jotenkin näin?
int Start = (DWORD)window.mbiBuffer.BaseAddress; int Stop = (DWORD)window.mbiBuffer.BaseAddress(DWORD)window.mbiBuffer.RegionSize; if(window.mbiBuffer.State != MEM_FREE && window.mbiBuffer.Protect != PAGE_READONLY) { int PSize = Stop - Start; char *memData = new char[PSize]; ReadProcessMemory(window.handle, (void*)window.curAddr,(LPVOID)memData, PSize, &window.bytes); for( int i = 0; i < PSize; i++ ) { int Adr = Start+(i*sizeof(char)); cout << memData[i] << endl; } delete [] memData; window.curAddr += window.mbiBuffer.RegionSize; } else window.curAddr += window.si.dwPageSize;
Kutakuinkin joo. Stopin määrittelystä sulta on varmaan unohtunu +-merkki noiden välistä. Stop - Start kyhäelmän sijaan voisit käyttää suoraan mbiBuffer.RegionSizeä, tai miten vaan haluat. Eiköhän tuo toimi, mutta kannattaa castata memData[ i ] jollakin tavalla DWORD-pointteriks, jos haluut etsiä neljän tavun arvoa (tällöin tulee vähemmän roskaa mukana)
Minustakin tuon pitäisi olla täysin oikein, mutta jostain syystä en vaan löydä memDataa tutkiessani mistään sellaista jonoa joka vastaisi Windows Pinballsissa minun pisteitäni. :/
Edit1:
Tässä olisi nytten tämän hetkinen koodini. Eikös tuon pitäisi olla aivan oikein vai voiko sen pisteet etsiä suoraan tuolta memDatasta?
GetSystemInfo(&window.si); if(!VirtualQueryEx(window.handle, (LPVOID)window.curAddr, &window.mbiBuffer, sizeof(MEMORY_BASIC_INFORMATION))) { return false; } if(window.mbiBuffer.State != MEM_FREE && window.mbiBuffer.Protect != PAGE_READONLY) { //Luodaan testinä vain jotkut string tyyppiset muuttujat string charri, t; //Määritetään muisti alue muuttujat int Start = (DWORD)window.mbiBuffer.BaseAddress; int Stop = (DWORD)window.mbiBuffer.BaseAddress+(DWORD)window.mbiBuffer.RegionSize; //Haetaan muisti alueen koko int PSize = Stop - Start; //Luodaan muuttuja joka sisältää alueen Start - Stop välillä oleva data char *memData = new char[PSize]; //Luetaan datat muistista muuttujaan memData ReadProcessMemory(window.handle, (void*)window.curAddr, (LPVOID)memData, PSize, &window.bytes); //Aloitetaan for-silmukka for( int i = 0; i < PSize; i++ ) { //Hetaan nykyinen osoite int Adr = (DWORD)window.mbiBuffer.BaseAddress+(i*sizeof(char)); //Silmukka joka hakee memDatasta aina 4 seuraavaa t stringiin for(int a = 0;a < 5;a++){ charri = memData[i+a]; t.insert(a, charri); } //Testi mielessä väkeretty alkeellinen haku, eli pelissä pisteitä minulla on 5950 eli etsitään memDatan if(t.substr(0,1) == "5") if(t.substr(1,1) == "9") if(t.substr(2,1) == "5") if(t.substr(3,1) == "0") MessageBox(NULL, t.c_str(), "Message", MB_OK); t = ""; } delete [] memData; window.curAddr += window.mbiBuffer.RegionSize; } else window.curAddr += window.si.dwPageSize;
Onhan hProcess varmasti oikein? Ja voihan se pistemäärä olla muistissa myös liukulukuna tai jopa tekstinä tai jollain aivan muulla tavalla.
hProcess on oikein, testasin sen RMP:llä sillai, että hain sen suoraan sen pisteen osotteella 01F1D212. Tosin kun siirsin datan 01F1D212 pisteen osoitteesta charri taulukkoon tulos oli jotain ihme merkkejä ja kun hain 01F1D212 piste osotteesta datan int muuttujaan niin tulos oli oikea.
Testasin myös niin, että muutin tuon memDatan tyypin int:si, mutta ei auttanut...
Eli jos haen suoraan
//Haen piste määrän suoraan pisteen osoitteesta ja tulos on oikea... ReadProcessMemory(window.handle, (void*)0x01F1D212, (void*)&intMuuttuja, 4, &window.bytes); // jos taas haen näin ReadProcessMemory(window.handle, (void*)window.curAddr, (void*)&memData, PSize, &window.bytes); //ja käyn läpi memDatan sisällön tavu tavulta, niin en löydä tuota pistettä
Öö... Koodis yrittää etsiä merkkijonoa 5950, ja sitähän ei nyt olla hakemassa. Tarkoitus on etsiä "int-tyyppinen" muuttuja. Anna memData-pointterin tyypin olla vain char*. Vertailun voit hoitaa typecastauksella niin pääset vähimmällä vaivalla:
if(*(DWORD*)(memData+i) == 12500) // (DWORD*) == 4 tavua, (WORD*) == 2 tavua, (BYTE*) == 1 tavu... { // löyty luku 12500 } // näin C:ssä, toivottavasti toimii C++:ssa
Ahaa... Aivan, tuolla tavallahan onnistuu erikokoisten tietojen hakeminen. Kiitos paljon sinulle Deffi, olet ollut suureksi avuksi! :)
...tämä olikin ohjelmani v0.01 viimeinen silaus. Voin heittää nimesi softan kiitos puolelle jos vain haluat/kehtaat. :D
...paitsi, että nyt kun kokeilin tuota, niin minulla ainakin tuo data pysyy saman mitä charista tuli? :/ ...tai ei tuo ainakaan toimi.
Tulostat/näytät sen väärällä tavalla.
char szTeksti[256]; wsprintf(szTeksti, "%d", *(DWORD*)(memData+i)); MessageBox(NULL, szTeksti, "Message", MB_OK);
edit. tai varmaan parempi lukea se data int- tai DWORD-muuttujaan, ja sitten käsittelet sitä kuten mitä tahansa muuttujaa :s
DWORD dwValue = *(DWORD*)(memData+i);
tein tuon juuri noin... mutta ei toimi. :/
Nytten koodi toimiikin!!!! Vika oli se, että hain ReadMemoryllä datan väärin..
//Hain näin (Väärä tapa) ReadProcessMemory(window.handle, (void*)window.curAddr, (void*)&memData, PSize, &window.bytes); //Toimii tällä ReadProcessMemory(window.handle, (void*)window.curAddr, (LPVOID)memData, PSize, &window.bytes);
Kirjoittelin tästä oman version. Ohjelmani löytää joitakin arvoja mutta esimerkiksi en löydä sillä Windowsin pinballin pisteitä. Mikähän on vialla?
#include <windows.h> #include <Tlhelp32.h> #include <iostream> using namespace std; DWORD GetPIDForProcess (char* process); int main() { MEMORY_BASIC_INFORMATION mbiBuffer; SYSTEM_INFO si; GetSystemInfo(&si); DWORD dwAlku = 0x000000, dwLoppu = 0xFFFFFFFF; DWORD dwNyt = dwAlku; DWORD loytyneita; HANDLE kahva = OpenProcess(PROCESS_ALL_ACCESS, false, GetPIDForProcess("PINBALL.exe")); while(dwNyt < dwLoppu) { if(!VirtualQueryEx(kahva, (LPVOID)dwNyt, &mbiBuffer, sizeof(MEMORY_BASIC_INFORMATION))) { cout << "Löytyi " << loytyneita << " arvoa" << endl; break; } if(mbiBuffer.State != MEM_FREE) // Löytyykö dataa? { DWORD luedata; DWORD luedatasize = sizeof(luedata); // Luetaan muistialueen arvo muuttujaan ja tulostetaan se ReadProcessMemory(kahva, (void*)dwNyt, (LPVOID)&luedata, luedatasize, NULL); cout << "#" << loytyneita << " Osoite: " << hex << dwNyt; cout << " Arvo: " << dec << luedata << endl; loytyneita += 1; // Tähän väliin ehtolause if(luedata == arvo) { cout << "arvo löytyi" << endl; ? dwNyt += mbiBuffer.RegionSize; // Kasvatetaan arvoa } else { dwNyt += si.dwPageSize; // Jos dataa ei löytynyt niin kasvatetaan arvoa yhden sivun verran. } } } DWORD GetPIDForProcess (char* process) { BOOL working=0; PROCESSENTRY32 lppe= {0}; DWORD targetPid=0; HANDLE hSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS ,0); if (hSnapshot) { lppe.dwSize=sizeof(lppe); working=Process32First(hSnapshot,&lppe); while (working) { if(_stricmp(lppe.szExeFile,process)==0) { targetPid=lppe.th32ProcessID; break; } working=Process32Next(hSnapshot,&lppe); } } CloseHandle( hSnapshot ); return targetPid; }
Kuuntelisin mielelläni myös parannuksia, selvennyksiä yms kirjoitustavan suhteen :)
Noh sullakin toi käsitys datan määrästä on väärä. Koodissas loytyneita-muuttujan arvo on regionien (ns. muistialueiden) määrä, eikä "arvojen" määrä, joka on myöskin melko hämärä käsite. Yhdessä regionissa on muistia yleensä >= 0x1000 tavua
Koodis ei löydä pinballin pisteitä, koska luet ja vertaat vain ensimmäistä löytyvää 4-tavun rykelmää jokaisesta muistialueesta, jolloin loput 0x0FFC tavua jää tarkastamatta. Kannattaa siis varata muistia koko mbiBuffer.RegionSizen verran, johon luet koko regionin. Luetun datan käyt sitten tarvittavalla tavalla läpi.
Aihe on jo aika vanha, joten et voi enää vastata siihen.