ORGA Hub’daki trainerleri geliştirirken her oyuna aynı şekilde yaklaşmadığımı önceki konularda anlatmıştım.
Unity oyunları bunun en güzel örneklerinden biri.
Eğer oyun Mono kullanıyorsa her şeyi klasik AOB, pointer ve sabit offsetlerle çözmeye çalışmak yerine Unity’nin çalışma zamanındaki sınıf ve field bilgilerine ulaşabiliyoruz.
Bu da bazı özellikleri hem geliştirmeyi kolaylaştırıyor hem de daha anlamlı bir yapı üzerinden çalışmamızı sağlıyor.
ORGA Hub’da bunun için ayrı bir Mono altyapısı bulunuyor.
Şu anda Rain World, Raft, Stranded Deep ve Streamer Life Simulator gibi trainerlerde bu sistemden yararlanıyorum.
🎮 Önce Unity Nedir?Unity bir oyun motoru.
Özellikle bağımsız oyunlarda çok sık kullanılıyor ama oldukça büyük projelerde de karşımıza çıkabiliyor.
Unity ile geliştirilen oyunların önemli bir kısmında oyun mantığının büyük bölümü C# tarafında yazılıyor.
Örneğin geliştirici şöyle sınıflar oluşturabilir:
Player
PlayerStats
Inventory
Weapon
Enemy
ve bunların içerisinde:
health
stamina
money
speed
gibi field’lar bulunabilir.
Trainer geliştirirken bunlara ulaşabilmek bizim için oldukça değerli.
🧠 Mono Nedir?Basitleştirirsek Mono, C# kodlarının oyun içerisinde çalışmasını sağlayan runtime sistemlerinden biri.
Unity oyununda Mono kullanılıyorsa oyun çalışırken sınıflar ve methodlarla ilgili bazı bilgiler runtime içerisinde bulunabiliyor.
Bu sayede trainer tarafından sadece:
Game.exe + 0x123456
gibi ham adreslerle uğraşmak yerine daha anlamlı şeyler arayabiliyoruz.
Örneğin teorik olarak:
PlayerStats
sınıfını bul.
Sonra:
health
field’ını bul.
Sonra o field’ın offset’ini öğren.
Bu yaklaşım klasik memory scanning’den oldukça farklı.
🔍 Mono Kullanıp Kullanmadığını Nasıl Anlıyoruz?Bir Unity oyununun dosyalarına baktığınızda bazı ipuçları bulunabilir.
Örneğin:
MonoBleedingEdge
veya Mono ile ilişkili dosyalar görüyorsanız oyun büyük ihtimalle Mono runtime kullanıyordur.
Aynı şekilde oyunun assembly yapısında:
Assembly-CSharp.dll
gibi dosyalar da oldukça tanıdık bir görüntüdür.
Ama her Unity oyunu Mono kullanmaz.
Özellikle birçok yeni oyun IL2CPP kullanıyor.
Bu ikisi trainer geliştirme açısından aynı şey değil.
⚠️ Mono ile IL2CPP Aynı DeğilBu ayrım önemli.
Mono kullanan bir Unity oyununda C# metadata yapısına runtime üzerinden ulaşmak görece daha kolay olabilir.
IL2CPP tarafında ise C# kodları native koda dönüştürülür.
Bu nedenle Mono için kullandığımız yöntemleri IL2CPP oyuna doğrudan uygulayamayız.
Yani:
Unity gördüm → Mono trainer yaparım
şeklinde düşünmüyorum.
Önce oyunun hangi backend’i kullandığını kontrol etmek gerekiyor.
🏗️ ORGA Hub’ın Mono Katmanı Ne Yapıyor?ORGA Hub içerisinde Mono oyunları için ayrı bir runtime katmanı geliştirdim.
Bu sistem genel olarak oyun içerisindeki:
Assembly
Image
Class
Field
Method
yapılarına ulaşabilmemizi sağlıyor.
Bunun sonucunda trainer tanımında bazı özellikleri doğrudan Mono üzerinden çözebiliyorum.
Örneğin trainer:
Şu sınıfı bul.
Şu field’ı bul.
Gerçek offset’ini runtime’dan çöz.
İlgili instance üzerinden değeri değiştir.
gibi işlemler gerçekleştirebiliyor.
Bu sayede her şeyi önceden elle hesaplanmış sabit adreslerle tanımlamak zorunda kalmıyorum.
🧩 Class Nedir?Class’ı oyun içerisindeki bir nesnenin şablonu gibi düşünebiliriz.
Mesela geliştirici:
PlayerStats
adında bir sınıf oluşturmuş olsun.
Bu sınıfın içerisinde:
health
energy
hunger
thirst
bulunabilir.
Oyuncu oluşturulduğunda bu sınıftan bir instance meydana gelir.
Trainer açısından bizim istediğimiz şey çoğu zaman bu gerçek oyuncu instance’ına ulaşmak.
Çünkü sınıfın yapısını bilmek tek başına yetmez.
Hangi oyuncunun verisini değiştireceğimizi de bilmemiz gerekiyor.
📦 Field Nedir?Field bir sınıf içerisinde tutulan değerlerden biri.
Örneğin:
PlayerStats
├── health
├── stamina
├── hunger
└── thirst
gibi.
Klasik yöntemle health değerinin:
Player + 0x128
olduğunu elle bulabiliriz.
Mono tarafında ise mümkün olduğu durumda:
PlayerStatssınıfındakihealthfield’ının offset’i nedir?
diye runtime’dan öğrenebiliriz.
Bu bence çok daha temiz.
Çünkü 0x128 sadece bir sayıdır.
Ama:
PlayerStats.health
neyi değiştirdiğimizi açıkça anlatır.
🧊 Mono Freeze Nasıl Çalışıyor?ORGA Hub’daki bazı Unity trainerlerinde en çok kullandığım işlemlerden biri Mono Freeze.
Mantığı oldukça basit.
Önce Mono üzerinden ilgili field çözülüyor.
Sonra gerçek instance’a ulaşılıyor.
Trainer belirli aralıklarla değeri istediğimiz seviyede tutuyor.
Örneğin:
PlayerStats.energy = 100
Trainer enerji değerini sürekli 100’de tutarsa oyun düşürmeye çalışsa bile tekrar yükseltiliyor.
Kullanıcı açısından:
Sınırsız enerji
ortaya çıkıyor.
Rain World trainerindeki özelliklerin büyük kısmında buna benzer Mono Freeze yaklaşımı bulunuyor.
🔧 Mono Patch Nedir?Her şeyi field yazarak yapmak mümkün değil.
Bazen bir methodun çalışma şeklini değiştirmek daha doğru.
Örneğin oyun:
ConsumeStamina()
gibi bir method kullanıyor olabilir.
Bu durumda method runtime içerisinde bulunup JIT edilmiş native koduna ulaşılabilir.
Sonrasında uygun durumlarda ilgili kod üzerinde patch uygulanabilir.
Yani burada:
Mono metadata → methodu bul → native kodunu bul → gerekli patch’i uygula
gibi bir süreç oluşuyor.
Raft ve Stranded Deep gibi trainerlerde field işlemleri ile patch yöntemleri birlikte kullanılabiliyor.
🕳️ Mono CaveBazı durumlarda Mono üzerinden methodu bulmak yeterli oluyor ancak method içerisinde daha gelişmiş bir davranış gerekiyor.
Burada daha önce anlattığım Code Cave mantığını Mono ile birleştirebiliyoruz.
Önce:
Class → Method
çözülüyor.
Ardından methodun JIT edilmiş koduna ulaşılıyor.
Sonra uygun instruction üzerinde hook veya cave kurulabiliyor.
Yani AOB ile fonksiyon aramak yerine Mono bize doğrudan:
Aradığın method burada.
diyebiliyor.
Sonrasında native trainer teknikleri yine kullanılabiliyor.
Bence farklı sistemlerin birbirini tamamlamasının güzel örneklerinden biri bu.
▶️ Mono Method ÇağırmakBazı özelliklerde değer değiştirmek yerine doğrudan oyunun kendi methodunu çalıştırmak daha mantıklı.
Diyelim oyunda:
AddMoney()
veya:
RefillHealth()
gibi kullanılabilecek bir method var.
Uygun şartlarda trainer bu methodu çağırabilir.
Bu durumda oyunun kendi iş mantığını tekrar kullanmış oluruz.
Bu her zaman mümkün veya güvenli değil tabii.
Methodun:
hangi instance üzerinde çalıştığını,
hangi parametreleri istediğini,
ne zaman çağrılabileceğini
bilmek gerekiyor.
Ama uygun olduğunda oldukça kullanışlı.
🖱️ Mono Button MantığıORGA Hub’daki trainerlerde bazı özellikler checkbox şeklinde sürekli açık kalmak yerine buton olarak çalışıyor.
Örneğin:
Parayı Maksimum Yap
gibi.
Kullanıcı butona bastığında ilgili Mono field bulunuyor ve değer bir kez yazılıyor.
Sonra trainer sürekli o değeri freeze etmiyor.
Bu tarz işlemler için Mono button yapısı kullanılabiliyor.
Yani:
Freeze
→ değeri sürekli koru.
Button
→ işlemi bir kez gerçekleştir.
gibi düşünebiliriz.
🎮 Rain WorldRain World traineri Mono sisteminin daha sade kullanıldığı örneklerden biri.
Trainerde bulunan 9 özelliğin tamamı ağırlıklı olarak Mono Freeze yöntemi üzerinden çalışıyor.
Burada oyunun ilgili C# yapılarına ulaşıp değerleri doğrudan kontrol etmek yeterli olduğu için gereksiz yere daha karmaşık native hook sistemleri kullanmıyorum.
Bu da daha önce söylediğim şeye güzel bir örnek:
En karmaşık yöntemi değil, işe en uygun yöntemi kullanmak.
🛶 RaftRaft tarafı biraz daha karmaşık.
Burada:
Mono Freeze
Mono Patch
Mono Cave
Mono Button
Mono Method çağırma
gibi farklı yöntemler birlikte bulunuyor.
Çünkü her trainer özelliği aynı şekilde çözülemiyor.
Bir özellik sadece field değiştirmekle yapılabilirken başka bir özellik oyunun method davranışına müdahale etmeyi gerektirebiliyor.
Bu nedenle Raft traineri Mono altyapısının daha geniş kullanıldığı örneklerden biri.
🏝️ Stranded DeepStranded Deep tarafında da Mono oldukça yoğun kullanılıyor.
Özellikle karakter değerleri için Mono Freeze yaklaşımı işimizi kolaylaştırıyor.
Oyunun çalışma zamanındaki field bilgilerine ulaşabildiğimiz için klasik pointer zincirleri hazırlamak zorunda kalmadığımız yerler bulunuyor.
📹 Streamer Life SimulatorStreamer Life Simulator da Unity tabanlı ancak burada ilginç başka bir durum var.
Her değer normal class field’ında tutulmuyor.
Bazı veriler Unity’nin:
PlayerPrefs
sistemi üzerinden yönetiliyor.
Bu nedenle ORGA Hub’daki trainerde Mono işlemlerinin yanında PlayerPrefs tabanlı işlemler de bulunuyor.
Bence trainer geliştirmede en önemli derslerden biri bu.
Bir değeri bellekte bulmanız, onun gerçek kaynağını bulduğunuz anlamına gelmiyor.
Örneğin ekranda gördüğünüz para değeri bellekte bulunabilir.
Ama oyunun asıl kayıt sistemi PlayerPrefs olabilir.
Sadece geçici kopyayı değiştirirseniz oyun biraz sonra gerçek değeri tekrar yükleyebilir.
Bu durumda direkt kaynağa gitmek daha mantıklı.
💾 PlayerPrefs Nedir?PlayerPrefs Unity’nin basit veri saklama sistemlerinden biri.
Oyun geliştiricileri burada örneğin:
Ayarlar
Bazı ilerleme bilgileri
Basit oyuncu değerleri
tutabiliyor.
Her oyun bunu farklı amaçla kullanabilir.
Streamer Life Simulator tarafında bazı trainer özelliklerinde gerçek değere ulaşmak için PlayerPrefs sisteminden yararlanıyorum.
Yani yine:
RAM’de bir sayı gördüm, değiştireyim.
yerine:
Oyun bu sayıyı nereden alıyor?
sorusunu soruyoruz.
🚀 Mono Kullanmanın Avantajı Ne?Bence en büyük avantajı yapının daha anlamlı olması.
Klasik yöntemde:
0x1F38A12C
ve:
+0x148
gibi rakamlarla çalışıyoruz.
Mono tarafında ise teorik olarak:
Player
PlayerStats
health
gibi gerçek oyun yapılarını kullanabiliyoruz.
Bu hem trainer geliştirirken ne yaptığımızı anlamayı kolaylaştırıyor hem de bazı durumlarda sabit offset bağımlılığını azaltabiliyor.
🔄 Oyun Güncellemesinde Mono Trainer Bozulmaz mı?Bozulabilir.
Mono kullanmak traineri güncellemelere karşı ölümsüz yapmıyor.
Geliştirici:
PlayerStats
sınıfını:
CharacterStats
olarak değiştirebilir.
health
field’ını kaldırabilir.
Method adını değiştirebilir.
Kodun çalışma mantığını tamamen yeniden yazabilir.
Bu durumda trainerin ilgili tanımı da güncellenmek zorunda kalır.
Ama örneğin sadece sınıfın içerisindeki field sırası değişmişse ve offset’i runtime üzerinden çözüyoruz, bazı durumlarda hiçbir şey yapmamız gerekmeyebilir.
Bu da Mono metadata kullanmanın avantajlarından biri.
🆚 Mono mu AOB mi?Aslında ikisini karşılaştırmayı çok doğru bulmuyorum.
İkisi farklı araçlar.
Mono bize:
Oyunun managed yapısını
anlatıyor.
AOB ise:
Bellekte belirli bir native kod desenini
buluyor.
Bazen Mono tek başına yeterli.
Bazen Mono ile methodu bulup içerisinde AOB veya code cave benzeri native teknik kullanmak gerekiyor.
Yani:
Mono + native memory teknikleri
birlikte de çalışabiliyor.
🧠 Neden Her Unity Oyunda Aynı Trainer Kodunu Kullanmıyoruz?Çünkü Unity kullanmak oyunların tamamen aynı olduğu anlamına gelmiyor.
Bir oyun:
Mono kullanabilir.
Başka biri IL2CPP.
Biri health değerini normal field’da tutabilir.
Diğeri property kullanır.
Biri PlayerPrefs kullanır.
Başka biri özel save sistemi kullanır.
Biri her şeyi singleton üzerinden yönetir.
Diğeri sahne değiştiğinde bütün instance’ları yeniden oluşturur.
Bu yüzden motoru bilmek bize başlangıç noktası sağlıyor ama oyunun kendi yapısını yine incelemek gerekiyor.
❤️ Kısaca ÖzetlersekUnity Mono trainer geliştirmede bize oyunun C# tarafına daha anlamlı şekilde ulaşma imkanı verebiliyor.
Kullanabildiğimiz yapılar arasında:
Class çözme
Field çözme
Method bulma
Runtime offset alma
Value freeze
Method patch
Code cave
Method çağırma
gibi farklı yöntemler bulunuyor.
ORGA Hub’da da Mono oyunlarında mümkün olduğunca bu altyapıdan yararlanıyorum.
Ama yine temel yaklaşım değişmiyor:
Önce oyunun yapısını anlamaya çalışıyorum, sonra en uygun yöntemi seçiyorum.
Bir oyun Mono kullanıyorsa bunu değerlendirmek, her şeyi baştan AOB ve pointer ile çözmeye çalışmaktan çoğu zaman daha mantıklı oluyor.
Bir sonraki konuda da bunun diğer tarafına geçip Unreal Engine Reflection nedir ve ORGA Hub’da Subnautica 2 trainerinde nasıl kullanıyorum? kısmını anlatacağım.
Gringos