Herkese selamlar,
Bugün, tersine mühendisliğe ilgi duyan, bu alanda kendini geliştirmek isteyen topluluk üyeleri için kapsamlı bir eğitim içeriği hazırladım.
Tersine mühendisliğin temel mantığı kavramak adına adeta bir simülatör görevi gören Assault Cube oyunu üzerinde, geliştirdiğim External Projeyi ve çalışma mantığını sizlerle paylaşıyorum. Projenin asıl amacı, popüler hile mekaniklerinin arka planında dönen yazılımsal ve matematiksel süreçlerin mantığını göstermektir.
NOT: Eğitimin faydalı olması için paylaşacağım kodu copy paste yapmak yerine mantığını anlayıp kendiniz sıfırdan yapmaya çalışın.
İlk olarak Cheat engine üzerinden offsetleri bulmalısınız.
Konunun teknik kalitesini ve eğitici değerini zirveye çıkaracak kısım tam olarak burası. Cheat Engine kullanarak bu offsetlerin nasıl bulunacağını adım adım ve foruma yakışır bir kılavuz (rehber) formatında metne ekledim.
Metnin "İlk olarak Cheat Engine üzerinden offsetleri bulmalısınız." dediğin cümlesinden sonrasını şu şekilde devam ettirebilirsin:
Cheat Engine üzerinden bu verileri bulabilmek için temel arama mantıklarını bilmemiz gerekiyor. Adım adım gidelim:
1. Proses Bağlantısı ve Bellek Yönetimi (Memory Management)
Projemiz External (Dışarıdan) çalıştığı için öncelikle FindWindow veya CreateToolhelp32Snapshot ile Assault Cube prosesini (ac_client.exe) buluyoruz. Ardından OpenProcess fonksiyonuna PROCESS_ALL_ACCESS yetkisi vererek oyunun hafıza odalarına giriş biletimizi alıyoruz. Bellekten veri okurken ReadProcessMemory (RPM), veri yazarken ise WriteProcessMemory (WPM) fonksiyonlarını kullanıyoruz.
2. Sınırsız Can (God Mode) & Sınırsız Mermi (Infinite Ammo)
Bu iki özelliğin mantığı tamamen "yazma" (Write) işlemine dayanır. Local Player adresimizin üzerine can ve mermi offsetlerini ekleyerek ulaştığımız bellek adreslerine, bir sonsuz döngü (veya thread) içerisinde sürekli olarak maksimum değerleri (örneğin can için 100) yazıyoruz. Oyun bu değerleri düşürmeye çalışsa da bizim programımız saliseler içinde değeri tekrar yükselttiği için ölümsüzlük ve sınırsız mermi sağlanmış oluyor.
3. WorldToScreen (W2S) ve ESP Mantığı
Düşmanların etrafına kutu çizebilmek için oyunun 3 boyutlu dünyasındaki koordinatları (Vector3), kendi monitörümüzdeki 2 boyutlu piksel koordinatlarına (Vector2) çevirmemiz gerekir. İşte bu noktada devreye View Matrix giriyor. Oyundan çektiğimiz matrix verisi ile düşmanın X, Y, Z koordinatlarını belirli matris çarpımlarına sokarak oyuncunun ekranımızda hangi pikselde durduğunu hesaplıyoruz. Eğer oyuncu görüş açımızın içerisindeyse, GDI (Graphics Device Interface) kullanarak o piksellere şeffaf bir kare (ESP Box) çizdiriyoruz.
4. Vektörel Aimbot Matematiği
Aimbot ise ESP'nin tam tersi bir mantıkla çalışır. Kendi koordinatlarımız ile hedef seçtiğimiz düşmanın koordinatları arasındaki farkı alarak 3 boyutlu bir mesafe vektörü elde ederiz. Ardından temel Trigonometri (Ark tanjant - atan2) fonksiyonlarını kullanarak, aramızdaki bu mesafeyi kapatacak olan yatay (Yaw) ve dikey (Pitch) bakış açılarını hesaplarız. Bulduğumuz bu yeni açıları Local Player + View Angles adresine WPM ile yazdığımızda, faremiz anında düşmanın kafasına kilitlenir.
Aşağıda paylaştığım kodları dikkatlice inceleyin. Projeyi derlemeden önce Cheat Engine ile doğruladığınız güncel offsetleri kodun ilgili kısımlara girmeyi unutmayın.
Her türlü sorunuzu ve merak ettiklerinizi konunun altından veya discord üzerinden sorabilirsiniz elimden geldiğince tamamını yanıtlamaya çalışacağım.
Discord: lyr1ca.x4n
Bugün, tersine mühendisliğe ilgi duyan, bu alanda kendini geliştirmek isteyen topluluk üyeleri için kapsamlı bir eğitim içeriği hazırladım.
Tersine mühendisliğin temel mantığı kavramak adına adeta bir simülatör görevi gören Assault Cube oyunu üzerinde, geliştirdiğim External Projeyi ve çalışma mantığını sizlerle paylaşıyorum. Projenin asıl amacı, popüler hile mekaniklerinin arka planında dönen yazılımsal ve matematiksel süreçlerin mantığını göstermektir.
NOT: Eğitimin faydalı olması için paylaşacağım kodu copy paste yapmak yerine mantığını anlayıp kendiniz sıfırdan yapmaya çalışın.
İlk olarak Cheat engine üzerinden offsetleri bulmalısınız.
Konunun teknik kalitesini ve eğitici değerini zirveye çıkaracak kısım tam olarak burası. Cheat Engine kullanarak bu offsetlerin nasıl bulunacağını adım adım ve foruma yakışır bir kılavuz (rehber) formatında metne ekledim.
Metnin "İlk olarak Cheat Engine üzerinden offsetleri bulmalısınız." dediğin cümlesinden sonrasını şu şekilde devam ettirebilirsin:
Cheat Engine üzerinden bu verileri bulabilmek için temel arama mantıklarını bilmemiz gerekiyor. Adım adım gidelim:
1. Can (Health) ve Mermi (Ammo) Offsetlerini Bulmak
Bu değerler ekranda direkt göründüğü için bulması en kolay olanlardır:- Cheat Engine'i açın ve sol üstteki bilgisayar simgesine tıklayarak oyun prosesini (ac_client.exe) seçin.
- Oyundaki mevcut can değerinizi (örneğin 100) Cheat Engine'de Value kısmına yazıp First Scan yapın.
- Oyuna dönün, canınızın azalmasını sağlayın (örneğin bir robota vurdurun veya bombayla kendinizi hafifçe hasarlayın).
- Yeni can değerinizi (örneğin 82) yazıp Next Scan deyin. Sol tarafta tek veya birkaç adres kalana kadar bu işlemi tekrarlayın.
- Kalan adresi aşağıya indirin ve sağ tıklayıp "Find out what writes to this address" seçeneğini seçin. Oyunda tekrar hasar aldığınızda pencereye bir kod satırı düşecektir (Örn: mov [eax+0xF8], ecx). Burada köşeli parantez içindeki 0xF8 sizin Health Offset değerinizdir. eax ise o anki oyuncu adresinizdir.
2. Yerel Oyuncu (Local Player Base) Adresini Bulmak
Yukarıdaki adımda can adresinizi bulduktan sonra, bu adresin kalıcı (yeşil) olan ana pointer'ını bulmamız gerekir:- Bulduğunuz can adresine sağ tıklayıp "Find out what accesses this address" deyin.
- Oyunda hareket edin veya ateş edin. Listelenen kod satırlarından birine çift tıklayarak detayını açın.
- Detay penceresinde "The registers contains the pointer to the pointer you are looking for" yazar. Oradaki adresi (Örn: 0050F4F4 gibi bir hex değeri) kopyalayın.
- Cheat Engine ana ekranına dönüp Hex kutucuğunu işaretleyin ve kopyaladığınız adresi yapıştırıp New Scan -> First Scan yapın.
- Sol tarafta yeşil renkli bir adres göreceksiniz (Örn: ac_client.exe+0x10F4F4). İşte bu yeşil adres sizin Local Player Base Pointer'ınızdır. Bunu koda 0x10F4F4 olarak ekleyeceğiz.
3. Düşman Listesi (Entity List) ve Koordinatları Bulmak
Burada mantık, oyuncu sayısı değiştikçe bellekte neyin değiştiğini izlemektir:- Oyunda tek başınıza boş bir harita açın. Oyuncu koordinatları sürekli değiştiği için X, Y, Z aramasını "Unknown initial value" ve Value Type: Float olarak başlatın.
- İleri yürüdükçe "Increased value" (artan değer), geri yürüdükçe "Decreased value" aramaları yaparak kendi koordinat offsetlerinizi bulun.
- Kendi koordinat adresinizi bulduğunuzda, bu adresin "neleri etkilediğini" (Find out what accesses...) inceleyin.
- Assault Cube'da düşmanlar bir dizi (array) halinde tutulur. Kendi oyuncu yapınız ile botların yapıları aynıdır. Bellek haritasını (Memory View -> Tools -> Dissect Data/Structures) açıp yerel oyuncu adresinizi ve bir botun adresini yan yana koyduğunuzda, aralarındaki mesafenin (genellikle 0x4 veya 0x10 katları) size Entity List yapısını ve bot sayısını tutan pointer'ı verecektir.
Projenin Çalışma Mimarisi ve Mantığı
1. Proses Bağlantısı ve Bellek Yönetimi (Memory Management)Projemiz External (Dışarıdan) çalıştığı için öncelikle FindWindow veya CreateToolhelp32Snapshot ile Assault Cube prosesini (ac_client.exe) buluyoruz. Ardından OpenProcess fonksiyonuna PROCESS_ALL_ACCESS yetkisi vererek oyunun hafıza odalarına giriş biletimizi alıyoruz. Bellekten veri okurken ReadProcessMemory (RPM), veri yazarken ise WriteProcessMemory (WPM) fonksiyonlarını kullanıyoruz.
2. Sınırsız Can (God Mode) & Sınırsız Mermi (Infinite Ammo)
Bu iki özelliğin mantığı tamamen "yazma" (Write) işlemine dayanır. Local Player adresimizin üzerine can ve mermi offsetlerini ekleyerek ulaştığımız bellek adreslerine, bir sonsuz döngü (veya thread) içerisinde sürekli olarak maksimum değerleri (örneğin can için 100) yazıyoruz. Oyun bu değerleri düşürmeye çalışsa da bizim programımız saliseler içinde değeri tekrar yükselttiği için ölümsüzlük ve sınırsız mermi sağlanmış oluyor.
3. WorldToScreen (W2S) ve ESP Mantığı
Düşmanların etrafına kutu çizebilmek için oyunun 3 boyutlu dünyasındaki koordinatları (Vector3), kendi monitörümüzdeki 2 boyutlu piksel koordinatlarına (Vector2) çevirmemiz gerekir. İşte bu noktada devreye View Matrix giriyor. Oyundan çektiğimiz matrix verisi ile düşmanın X, Y, Z koordinatlarını belirli matris çarpımlarına sokarak oyuncunun ekranımızda hangi pikselde durduğunu hesaplıyoruz. Eğer oyuncu görüş açımızın içerisindeyse, GDI (Graphics Device Interface) kullanarak o piksellere şeffaf bir kare (ESP Box) çizdiriyoruz.
4. Vektörel Aimbot Matematiği
Aimbot ise ESP'nin tam tersi bir mantıkla çalışır. Kendi koordinatlarımız ile hedef seçtiğimiz düşmanın koordinatları arasındaki farkı alarak 3 boyutlu bir mesafe vektörü elde ederiz. Ardından temel Trigonometri (Ark tanjant - atan2) fonksiyonlarını kullanarak, aramızdaki bu mesafeyi kapatacak olan yatay (Yaw) ve dikey (Pitch) bakış açılarını hesaplarız. Bulduğumuz bu yeni açıları Local Player + View Angles adresine WPM ile yazdığımızda, faremiz anında düşmanın kafasına kilitlenir.
Kaynak Kodları (Source Code)
Aşağıda paylaştığım kodları dikkatlice inceleyin. Projeyi derlemeden önce Cheat Engine ile doğruladığınız güncel offsetleri kodun ilgili kısımlara girmeyi unutmayın.
C++:
#include <Windows.h>
#include <iostream>
#include <tlhelp32.h>
#include <cmath>
#include <cfloat>
using namespace std;
// Yapılar
struct Vector3 { float x, y, z; };
struct Vector2 { float x, y; };
// Genel Değişkenler
HDC hdc;
HPEN hBoxPen, hLinePen, hHealthPen;
float fov = 150.0f;
// Yardımcı Fonksiyonlar
void renkli_yaz(std::string metin, std::string renk_kodu) {
std::cout << "\033[" << renk_kodu << "m" << metin << "\033[0m" << std::endl;
}
uintptr_t GetModuleBaseAddress(DWORD procId, const char* modName) {
uintptr_t modBaseAddr = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
if (hSnap != INVALID_HANDLE_VALUE) {
MODULEENTRY32 modEntry;
modEntry.dwSize = sizeof(modEntry);
if (Module32First(hSnap, &modEntry)) {
do {
if (lstrcmpiA(modEntry.szModule, modName) == 0) {
modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
break;
}
} while (Module32Next(hSnap, &modEntry));
}
}
CloseHandle(hSnap);
return modBaseAddr;
}
bool WorldToScreen(Vector3 pos, Vector2& screen, float matrix[], int width, int height) {
float w = pos.x * matrix[3] + pos.y * matrix[7] + pos.z * matrix[11] + matrix[15];
if (w < 0.1f) return false;
float x = (pos.x * matrix[0] + pos.y * matrix[4] + pos.z * matrix[8] + matrix[12]) / w;
float y = (pos.x * matrix[1] + pos.y * matrix[5] + pos.z * matrix[9] + matrix[13]) / w;
screen.x = (width / 2.0f * x) + (x + width / 2.0f);
screen.y = -(height / 2.0f * y) + (y + height / 2.0f);
return true;
}
void AimAt(HANDLE hProcess, uintptr_t localPlayer, Vector3 localPos, Vector3 enemyPos) {
float dX = enemyPos.x - localPos.x;
float dY = enemyPos.y - localPos.y;
float dZ = enemyPos.z - localPos.z;
float dist = sqrt(dX * dX + dY * dY + dZ * dZ);
float yaw = atan2(dY, dX) * (180.0f / 3.14159265f) + 90.0f;
float pitch = asin(dZ / dist) * (180.0f / 3.14159265f);
WriteProcessMemory(hProcess, (LPVOID)(localPlayer + 0x34), &yaw, sizeof(float), NULL);
WriteProcessMemory(hProcess, (LPVOID)(localPlayer + 0x38), &pitch, sizeof(float), NULL);
}
void DrawLine(int x1, int y1, int x2, int y2, HPEN pen) {
SelectObject(hdc, pen);
MoveToEx(hdc, x1, y1, NULL);
LineTo(hdc, x2, y2);
}
int main() {
HWND hwnd = FindWindowA(NULL, "AssaultCube");
if (hwnd == NULL) {
renkli_yaz("GAME NOT FOUND! PLEASE OPEN THE GAME...", "31");
Sleep(2500);
return 0;
}
DWORD procId;
GetWindowThreadProcessId(hwnd, &procId);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId);
if (hProcess == NULL) {
renkli_yaz("Run the Cheat as administrator.", "31");
Sleep(2500);
return 0;
}
uintptr_t baseAddress = GetModuleBaseAddress(procId, "ac_client.exe");
if (baseAddress == 0) {
CloseHandle(hProcess);
return 0;
}
// Ofsetler
uintptr_t localPlayerPtr = baseAddress + 0x17E0A8;
uintptr_t entityListPtr = baseAddress + 0x18AC04;
uintptr_t viewMatrixPtr = baseAddress + 0x17DFD0;
uintptr_t playerCountPtr = baseAddress + 0x18AC0C;
// GDI Kurulum
hdc = GetDC(hwnd);
hBoxPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
hLinePen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
hHealthPen = CreatePen(PS_SOLID, 3, RGB(0, 255, 0));
// Trainer Hazırlığı
DWORD oldProtect;
uintptr_t ammoDecreaseAddress = baseAddress + 0xC73EF;
BYTE ammoNop[2] = { 0x90, 0x90 };
VirtualProtectEx(hProcess, (LPVOID)ammoDecreaseAddress, sizeof(ammoNop), PAGE_EXECUTE_READWRITE, &oldProtect);
WriteProcessMemory(hProcess, (LPVOID)ammoDecreaseAddress, &ammoNop, sizeof(ammoNop), nullptr);
VirtualProtectEx(hProcess, (LPVOID)ammoDecreaseAddress, sizeof(ammoNop), oldProtect, &oldProtect);
renkli_yaz("[SUCCESESFULLY] CHEAT ENABLE!", "32");
std::cout << "Press [END] to close" << std::endl;
while (true) {
if (GetAsyncKeyState(VK_END) & 0x8000) break;
if (GetAsyncKeyState(0x46)) { // 'F' tuşu FOV değiştirme
std::cout << "New FOV: ";
std::cin >> fov;
}
RECT rect;
GetClientRect(hwnd, &rect);
int centerX = rect.right / 2;
int centerY = rect.bottom / 2;
// Çizimler
Arc(hdc, centerX - (int)fov, centerY - (int)fov, centerX + (int)fov, centerY + (int)fov, 0, 0, 0, 0);
DrawLine(centerX - 8, centerY, centerX + 8, centerY, hLinePen);
DrawLine(centerX, centerY - 8, centerX, centerY + 8, hLinePen);
uintptr_t lp = 0;
ReadProcessMemory(hProcess, (LPCVOID)localPlayerPtr, &lp, sizeof(lp), NULL);
if (lp != 0) {
// TRAINER: Can Güncelleme
int yeniCan = 1000;
WriteProcessMemory(hProcess, (LPVOID)(lp + 0xEC), ¥iCan, sizeof(yeniCan), NULL);
int myTeam = 0, count = 0;
ReadProcessMemory(hProcess, (LPCVOID)(lp + 0x30C), &myTeam, sizeof(int), NULL);
ReadProcessMemory(hProcess, (LPCVOID)playerCountPtr, &count, sizeof(count), NULL);
uintptr_t entList = 0;
ReadProcessMemory(hProcess, (LPCVOID)entityListPtr, &entList, sizeof(entList), NULL);
float matrix[16];
ReadProcessMemory(hProcess, (LPCVOID)viewMatrixPtr, &matrix, sizeof(matrix), NULL);
float closestDist = FLT_MAX;
Vector3 bestTarget{};
bool targetFound = false;
for (int i = 1; i < count; i++) {
uintptr_t ent = 0;
ReadProcessMemory(hProcess, (LPCVOID)(entList + (i * 4)), &ent, sizeof(ent), NULL);
if (!ent) continue;
int eTeam = 0, health = 0;
ReadProcessMemory(hProcess, (LPCVOID)(ent + 0x30C), &eTeam, sizeof(int), NULL);
ReadProcessMemory(hProcess, (LPCVOID)(ent + 0xEC), &health, sizeof(int), NULL);
if (eTeam == myTeam || health <= 0) continue;
Vector3 head, foot;
ReadProcessMemory(hProcess, (LPCVOID)(ent + 0x4), &head, sizeof(Vector3), NULL);
ReadProcessMemory(hProcess, (LPCVOID)(ent + 0x28), &foot, sizeof(Vector3), NULL);
Vector2 sH, sF;
if (WorldToScreen(head, sH, matrix, rect.right, rect.bottom) && WorldToScreen(foot, sF, matrix, rect.right, rect.bottom)) {
// Aimbot Logic
if (GetAsyncKeyState(VK_SHIFT)) {
float distToCrosshair = sqrt(pow(sH.x - centerX, 2) + pow(sH.y - centerY, 2));
if (distToCrosshair < fov && distToCrosshair < closestDist) {
closestDist = distToCrosshair;
bestTarget = head;
targetFound = true;
}
}
// ESP Çizimleri
int h = abs((int)(sF.y - sH.y));
int w = (int)(h / 1.3f);
int x = (int)sH.x - (w / 2);
int y = (int)sH.y;
DrawLine(x, y, x + w, y, hBoxPen);
DrawLine(x + w, y, x + w, y + h, hBoxPen);
DrawLine(x + w, y + h, x, y + h, hBoxPen);
DrawLine(x, y + h, x, y, hBoxPen);
DrawLine(centerX, rect.bottom, (int)sF.x, (int)sF.y, hLinePen);
int healthPixels = (h * min(health, 100)) / 100;
DrawLine(x - 6, y + h, x - 6, y + h - healthPixels, hHealthPen);
}
}
if (GetAsyncKeyState(VK_SHIFT) && targetFound) {
Vector3 lPos;
ReadProcessMemory(hProcess, (LPCVOID)(lp + 0x4), &lPos, sizeof(Vector3), NULL);
AimAt(hProcess, lp, lPos, bestTarget);
}
}
GdiFlush();
Sleep(0);
}
// Temizlik
BYTE originalAmmo[2] = { 0xFF, 0x08 };
VirtualProtectEx(hProcess, (LPVOID)ammoDecreaseAddress, sizeof(originalAmmo), PAGE_EXECUTE_READWRITE, &oldProtect);
WriteProcessMemory(hProcess, (LPVOID)ammoDecreaseAddress, &originalAmmo, sizeof(originalAmmo), nullptr);
VirtualProtectEx(hProcess, (LPVOID)ammoDecreaseAddress, sizeof(originalAmmo), oldProtect, &oldProtect);
DeleteObject(hBoxPen);
DeleteObject(hLinePen);
DeleteObject(hHealthPen);
ReleaseDC(hwnd, hdc);
CloseHandle(hProcess);
return 0;
}