Ne zamandır basit manada nasıl bir game trainer yazilabileceği hakkında küçükte olsa bir yazı yazmak istiyordum. Basit manada dedim çünkü şimdilerde her oyun bir hile engelleme (örn. PunkBuster) yazılımıyla çalışmayı şart tutuyor.Biz burada böyle bir durumun olmadığını varsayacağız. Kimbilir belki bu tür yazılımların çalışma mantığını araştırdıktan sonra onlar içinde bir çalışma yapabilirim.
Trainerların temel olarak yaptıkları şey önceden belirlenmiş bellek adreslerindeki degerleri değiştirmektir.Bu adreslerde çoğunlukla para,zaman,taş,ekmek miktarı gibi değerler bulunur.Peki bu bellek adreslerini nerden öğreneceğiz?İşte burası yazımızın birinci bölümünü oluşturacak.İkinci bölümde ise bu adreslerin içeriğini dll injection yöntemiyle nasıl değiştireceğimizi göreceğiz.
BÖLÜM 1:
Herşeyden önce eğer çalıştırdığımız process ile başka bir process üzerinde at koşturmak istiyorsak SE_DEBUG_NAME ruhsatını(privilege) almamız gerekiyor.Tabii bu ruhsatı alabilmek için de Administrator haklarına sahip olmamız gerekiyor(eğer işletim sisteminin yerel güvenlik ilkelerini değiştimediysek, default da sadece Administrator grubu bu hakka sahip).
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////
BOOL AdjustPrivileges()
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
TOKEN_PRIVILEGES oldtp;
DWORD dwSize = sizeof(TOKEN_PRIVILEGES);
LUID luid;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
return TRUE;
return FALSE;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
{
CloseHandle(hToken);
return FALSE;
}
ZeroMemory(&tp, sizeof(tp));
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges (hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &oldtp, &dwSize))
{
CloseHandle(hToken);
return FALSE;
}
CloseHandle(hToken);
return TRUE;
}
////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Yukarıdaki fonksiyon öncelikle “OpenProcessToken” ile üzerinde bulunduğumuz processin “TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY” ruhsatlarına sahip olduğunu gösterir bir token açıyor. “AdjustTokenPrivileges” fonksiyonu ile de bu token ı kullanarak istediğimiz ruhsatı alabiliyoruz.Peki “LookupPrivilegeValue” ne iş görür derseniz ,o da bize ruhsatımızın id sini döndürüyor. Dip not olarak: eğer sistem üzerinde sürekli çalışan bir program yazıyorsak ruhsatları işimiz bittikten sonra eski haline getirmek olası bir güvenlik açığı oluşmasını önler ama bizim programımızın böyle bir durumu olmadığından o bölümü atlıyoruz.
Artık ruhsatımızı da aldığımıza göre artık oyuna ait processi gönlümüzce kullanabiliriz. Aşağıda da görüleceği üzere “OpenProcess” i kullanabilmek için ilgili processin id sine ihtiyacımız olacak. Bu değeri görev yöneticisinden kolayca öğrenebileceğimiz gibi programatik olarak “GetWindowThreadProcessId” ve “FindWindow” fonksiyonlarıylada öğrenmek mümkün.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////
hReadp = OpenProcess( PROCESS_ALL_ACCESS, FALSE, hPID );
////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Aşağıdaki memory işlemlerinin üzerinde pek durmak istemiyorum.Buradaki önemli soru bir processin belli bir adresindeki memory bölgesinin özelliklerine nasıl ulaştığımızdır ki,cevabı “VirtualQueryEx” fonksiyonudur.Bu fonksiyon bize “MEMORY_BASIC_INFORMATION” yapısını döndürür.Bu yapı sayesinde o bellek bölgesinin başlangıç adresini,büyüklüğünü, ayrılma şeklini(reserve,commit,free),korunma şeklini öğrenebiliriz.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////
//Allocate required mem for basic mem infos
allocMem=VirtualAlloc(NULL,MEMRESERVED,MEM_RESERVE,PAGE_NOACCESS);
uncommitedMem=allocMem;
readedBasicInfos=(PMEMORY_BASIC_INFORMATION)allocMem;
////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
“GetSystemInfo” fonksiyonu adındanda anlaşılacağı üzere sistem bilgilerini veriyor bize.Biz bu fonksiyonu processe ait sanal belleğin max boyutunu öğrenmek için kullanıyoruz.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////
GetSystemInfo(&si);
pMemory=0;
while(pMemory
//if(readedBasicInfos) TODO: Check out of memory
if((DWORD)readedBasicInfos+sizeof(MEMORY_BASIC_INFORMATION)>=(DWORD)uncommitedMem){
if(VirtualAlloc(uncommitedMem,4200/*150 More*/,MEM_COMMIT,PAGE_READWRITE)==NULL){
MessageBox(NULL,"Failed to commit reserved memory space.","FAILED",MB_OK);
return 0;
}
uncommitedMem=(DWORD)uncommitedMem+ 4200;
}
VirtualQueryEx(hReadp,pMemory,readedBasicInfos,sizeof(MEMORY_BASIC_INFORMATION));
pMemory=((DWORD)readedBasicInfos->BaseAddress) +((DWORD)readedBasicInfos->RegionSize);
readedBasicInfos++;
}
dwSize=(DWORD)readedBasicInfos -(DWORD)allocMem;
count=dwSize/sizeof(MEMORY_BASIC_INFORMATION);
////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Bu noktada ilgili processin tüm bellek bölgelerinin niteliklerini biliyoruz.Şimdi bu özellikler yardımıyla işimize yarayan bölgeleri seçip bunları okuyoruz.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////
memBuffer=VirtualAlloc(NULL,maxRegionSize,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
memInfo=(PMEMORY_BASIC_INFORMATION)allocMem;
while(memInfo
if(memInfo->State==MEM_FREE | memInfo->State==MEM_RESERVE){
memInfo++;
continue;
}
dontRead=0;
dontRead |= (memInfo->Protect & PAGE_WRITECOPY);
dontRead |= (memInfo->Protect & PAGE_EXECUTE);
dontRead |= (memInfo->Protect & PAGE_GUARD);
dontRead |= (memInfo->Protect & PAGE_NOACCESS);
if(dontRead){
memInfo++;
continue;
}
memset(memBuffer,0,memInfo->RegionSize);
readed=0;
ReadProcessMemory(hReadp,memInfo->BaseAddress,memBuffer,memInfo->RegionSize,&readed);
////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Artık bellek bölgesini okuduğumuza göre aramak istediğimiz değeri ,tipinide belirterek bu bölge içerisinde aratıyoruz.Nede olsa tip olmadan bellek, 0-1 lerden oluşan sayı dizisidir.Buradaki “SEARCH_TYPE” ve “SEARCH_VALUE” bizim belirlediğimiz macrodan başka bir şey değildir.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////
pValue=(SEARCH_TYPE*)memBuffer;
while((DWORD)pValue+sizeof(SEARCH_TYPE)< (DWORD)memBuffer+readed){
i=(*pValue);
if((*pValue)==SEARCH_VALUE){
valueOffset=(LPVOID)((DWORD)memInfo ->BaseAddress+(DWORD)pValue-(DWORD)memBuffer);
printf("FOUND : 0x%p\n",valueOffset);
}
pValue++;
}
memInfo++;//next memory info
}
CloseHandle(hReadp);
}
////////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Tabii eğer oyundaki para miktarınız 5 ise bu değerin sadece bir adreste olmayacağını fark etmişsinizdir herhalde. Bunun için bulduğunuz adresleri bir yerde saklayıp ,para miktarınız değiştiğinde yeni miktar için bu adresler içinde arama yapabilirsiniz.
Kodun tamamına buradan ulaşabilirsiniz.
Hiç yorum yok:
Yorum Gönder