- PROJEKT: OVERFLOW, RISC-V assembly ve buffer overflow kavramlarını kutu oyunu kurallarına dönüştürerek bellek, stack ve dönüş adresi manipülasyonunu doğrudan takip ettiren eğitsel bir oyun
- Oyuncular aynı belleği ve programı paylaşır; sanal bellek olmadan, tur başına yalnızca 10 komut çalıştırılan preemptive scheduling modeliyle yarışır
- Sonuç, mevcut komutları kopyalayıp shellcode oluşturma ve rakibin return address’ini ezerek onu
game_over()fonksiyonuna gönderme akışında belirlenir - Hatalı bellek erişimi, hizasız okuma/yazma ve illegal komutlar crash’e ve exception handler’ın çalışmasına yol açar; trap adresini değiştirme ve
nopmonkeypatch stratejinin temel değişkenleridir - Web’de oynama, yazdırılabilir board, ESP32 ve mobil oyun yardımcısı sunuluyor; ancak kuralların bir kısmı hâlâ ayarlandığı için deneysel bir hacking bulmacasına daha yakın
Oyunun amacı ve yürütme modeli
- PROJEKT: OVERFLOW, RISC-V assembly ve buffer overflow konularını masaüstü kutu oyunu olarak ele alan bir proje
- Temel amaç, mevcut komutları kopyalayarak bellek içinde küçük bir shellcode oluşturmak, buffer overflow ile bu koda jump etmek ve ardından rakibin return address’ini ezerek
game_over()fonksiyonunu çağırmasını sağlamak - Strateji, basit kod yürütmenin ötesinde exception handler ayarlamayı ve monkeypatch yapmayı da içerir
- Tüm oyuncular aynı belleği ve aynı programı paylaşır, aynı işlemciyi zaman paylaşımlı olarak kullanır
- Bir turda 10 komut çalıştırılır
- Her oyuncunun stack pointer’ı farklı bir konumdan başlar
- Sanal bellek yoktur
Build ve board oluşturma akışı
- Kod, RV32 hedefi için
riscv64-unknown-elf-gccile derlenir- Başlıca seçenekler arasında
-march=rv32g,-mabi=ilp32,-ffreestanding,-nostdlib,-nostartfiles,-O0gibi seçenekler bulunur -O0sayesinde makine kodu uzun ama takip etmesi kolay bir biçimde kalır
- Başlıca seçenekler arasında
- Board materyalleri,
riscv64-unknown-elf-objdump -S -l -fd gameçıktısı parse edilerek oluşturulur▲ve✎komutları değiştirilir- Jump offset’leri onaltılıktan onluğa çevrilir
- Assembly temizlenir ve kaynak kodla eşleştirilir
- SVG oluşturulduktan sonra Inkscape ile PDF’ye dönüştürülür
Baskı ve gerekenler
- Board, sol ve sağ olarak ayrılmış PDF’ler yazdırılarak kullanılır
- Baskı için A3 tercih edilir; A4 de mümkündür ama küçük kalır
- Gerekenler:
nopkomutu için 1 taş, trap adresi için 1 taş, oyuncu başına program counter ve stack pointer için 2 taş, kalem ve silgi - Web sürümü tek başına oynamayı ve arkadaşlarla oynamayı destekler; ESP32 ve mobil için oyun yardımcısı da sunulur
Temel kurallar ve tur akışı
- Başlangıç durumu şöyledir
- Tüm register’lar 0’dan başlar; ancak return address register’ı
ra1000’den başlar - Player 1’in
spdeğeri 2244, Player 2’ninspdeğeri 3844 olarak ilklendirilir - İki oyuncunun
pcdeğeri,mainfonksiyonunun başlangıç adresi olan 1000’den başlar - Trap taşı adres 1000’e yerleştirilir
- Önceden yüklenmiş program hariç tüm bellek adresleri 0’dır
nopkomutu taşı başlangıçta board’a konmaz
- Tüm register’lar 0’dan başlar; ancak return address register’ı
- Bir turda 10 komut çalıştırılmalıdır;
jal,beqgibi jump’lar da aynen takip edilmelidir - Oyuncu en az 1 komut çalıştırdıktan sonra turu durdurup kalan komut sayısını bir sonraki tura aktarabilir
- Biriktirilebilecek komut sayısının üst sınırı 20’dir
Monkeypatch ve kazanma koşulu
- Her turun başında tam olarak 1 komut çalıştırdıktan sonra,
nopkomutu taşı, mevcut oyuncuların çalıştırmakta olmadığı bir fonksiyonun herhangi bir adresine taşınabilir pco adrese ulaştığında ilgili komut no-operation olarak davranırnoptaşını hareket ettirmek mevcut turu ve bir sonraki turu kaybettirir; rakip bir sonraki turda en fazla 20 komut çalıştırabilir- Monkeypatch kuralı henüz dengeli olmadığı için birkaç günde bir küçük değişiklikler yapılıyor
- Hard mode’da, rakibi hackleyip
game_over()fonksiyonunu çağırmasını sağlarsanız oyun biter - Taraflardan hiçbirinin rakibi
game_over()’a göndermesi mümkün değilse oyun berabere biter - Easy mode’da,
mainiçinderetçalıştırarak ana döngüden çıkan ilk oyuncu kazanır
Özel semboller ve exception handling
✎,likomutunun immediate değeri olarak 0’dan 4095’e kadar herhangi bir 12 bitlik sayı seçmeyi sağlar▲, load komutunda kendi stack pointer’ı temel alınarak ±128 bayt aralığındaki bir değeri seçmeyi sağlar- Örneğin
sp2180 ise 2052’den 2308’e kadar seçim yapılabilir
- Örneğin
- Yasak eylemler program crash’ine yol açar
- 1192’den düşük bellek adreslerinin üzerine yazmak
- 4’ün katı olmayan adreslerde hizasız okuma veya yazma yapmak
- Illegal komut çalıştırmak
- Crash olursa exception handler çalışır ve trap adresine jump edilir
- Trap adresi başlangıçta 1000’dir, ancak
set_trap()fonksiyonunda üzerine yazılabilir - Exception oluştuğunda program counter belirli bir değere ayarlanır ve yürütme devam eder
- Trap adresi başlangıçta 1000’dir, ancak
- Hile veya hata tespit edilirse ilgili oyuncunun program durumu, belleği ve register’ları sıfırlanır
3–4 oyunculu genişleme kuralları
- Player 3’ün
spdeğeri 2116 olarak ayarlanır - Player 4’ün
spdeğeri 3716 olarak ayarlanır - 3 veya daha fazla oyuncu olduğunda
▲sembolü yalnızca stack pointer’dan -128 bayt uzaklıktaki aralıkta kullanılabilir - 2’den fazla oyuncuyla oynandığında oyun oldukça kararsızlaşır ve hızla bozulur
- Kazanma koşuluna ulaşmak zorlaşır, ancak oynanış daha eğlenceli ve kaotik hâle gelir
Hacking stratejisi örnekleri
- Crash, rakibin ilerlemesini engelleyen bir saldırı stratejisi olarak kullanılabilir
- Trap handler’ı
game_overfonksiyonuna çevirirseniz ilk crash olan oyuncu kaybeder- Bu durumda
noptaşı çok güçlüdür - Rakip, o anda yürüttüğünüz fonksiyonun
retkomutunun üzerinenopkoyarsa kaybedebilirsiniz
- Bu durumda
bug()fonksiyonunda index’i 400 veya -400 yaparak overflow oluşturursanız rakibin stack’ine erişip return address’i ezebilirsiniz- Örneğin adres 3784’ten 2184’e gitmek için
(3784 - 2184) / 4 = 400olduğundan index-400gerekir
- Örneğin adres 3784’ten 2184’e gitmek için
copy()fonksiyonuyla belirli komutlar kopyalanarak bellekte kısa bir shellcode oluşturulabilir- Örnek shellcode,
li a4, ✎,li a5, ✎,sw a4, 0(a5),retkombinasyonuyla arbitrary write gerçekleştirir retkomutu kopyalanırsa return address shellcode’un başlangıç noktasına ayarlanır ve sonsuz döngü oluşur
- Örnek shellcode,
bug()fonksiyonunda index 6 olarak ayarlanırsavaluedeğişkeni stack’te saklanan return address olan28(sp)üzerine yazılabilirbug()dönerken28(sp)değeri return address register’ına kopyalanır- Bu değere oluşturduğunuz shellcode adresini koyarsanız belleğe jump edebilirsiniz
Komut yorumlama ve değişiklikler
- Tüm jump’lar, disassembler’da mutlak adres gibi görünse bile mevcut program counter’a göre relative jump’tır
- Örneğin
jal a4, 0makine kodu 1903, çalıştırıldığında sonsuz döngü olur
- Örneğin
- Geçerli oyun komutlarının listesi, 0’dan 4095’e kadar olan makine kodları içindeki RV32 JRI komutlarından
a0,a4,a5,sp,ragibi register’ları kullanan biçimler olarak düzenlenmiştir - Değişiklik günlüğü 0.0.6’da
while(run)yerinewhile(*prun)kullanımı yer alır- Rakibin hizasız dereference tetikleyerek zorla crash oluşturabilmesi mümkün hâle gelir
- NOP kuralı, yalnızca o anda yürütülmeyen fonksiyonlara yerleştirilebilecek şekilde değiştirilmiştir
Tasarım ve öğrenme materyalleri
- Board’un solundaki ve sağındaki kareler, ASCII olarak kodlanmış ikili mesajlardır
- Beyaz kare 1, siyah kare 0’dır
- Renkler, düşük maliyetli baskı ve siyah-beyaz yazıcı okunabilirliği için yalnızca kırmızı, mavi, siyah ve beyaz olarak seçilmiştir
- Syntax highlighting kullanılmaz
- Temaya bağlı olarak kodun bazı bölümlerinin daha önemli görünmesi etkisinden kaçınmak ve doğrudan karar vererek odaklanmak için yapılan bir tercihtir
- RISC-V assembly öğrenme materyalleri arasında riscv-programming.org, cs3410 risc-v interpreter, luplab’in rvcodecjs’i gibi kaynaklar bulunur
- C öğrenme materyali olarak Beej's Guide to C Programming rehberinin ilk bölümleri kullanılır
- Değişkenler, fonksiyon çağrıları, pointer’lar, string’ler, struct’lar, array’ler, recursion gibi konuları ele alan yazdırılabilir assembly alıştırma PDF’leri ve boşluk doldurmalı “assembly hangman” sürümü sunulur
1 yorum
Hacker News yorumları
Gerçekten etkileyici. Özellikle 12 yaşındaki kızının bunu onunla birlikte oynamasını sağlamış olması en etkileyici tarafı gibi görünüyor
CHERI sürümünü ne zaman bekleyebiliriz? :-D
Core War, basit bir simüle edilmiş assembly dilini destekleyen sanal makinenin bellek arenasında oynanan bir oyun. İlk kez 1984’te Scientific American’da görmüştüm; o sırada zaten yaklaşık 15 yıldır programlama yaptığım için bunun Bell Labs’in daha eski oyunu Darwin’den esinlendiğini fark etmiştim
Darwin 1961’de yapılmıştı ve IBM 7090 üzerinde çalışıyordu. Programlar kaynaklar için rekabet ediyor, kendisine ayrılan alanın tamamını kopyalayıp ele geçiren program kazanıyordu. Robert Morris Sr. yenilmez bir program yaptıktan sonra çok uzun ömürlü olmadı. Bkz. [2]
1970’lerin ortasında Software Practice and Experience en sevdiğim bilgisayar bilimi dergilerinden biriydi ve Aleph-Null takma adıyla yazılan Computer Recreations köşesi sık sık yayımlanırdı. Lisansüstü yıllarımda o köşede çıkan oyunların birkaçını uygulayıp keyif almıştım. Dergi pahalı, ama üniversite öğrencisiyseniz benim eskiden yaptığım gibi üniversite kütüphanesinde bulma ihtimaliniz yüksek. 1970’ler sayılarında Pascal derleyicisi, Algol 68, eşzamanlı programlama gibi konular vardı; okunması kolay ve eğlenceliydi. N. Wirth’in yazıları sayesinde Module[3,4] ve daha sonra Oberon[5] ile tanışmıştım
[1] https://en.wikipedia.org/wiki/Core_War
[2] https://en.wikipedia.org/wiki/Darwin_(programming_game)
[3] https://onlinelibrary.wiley.com/doi/abs/10.1002/spe.43800701...
[4] https://onlinelibrary.wiley.com/doi/abs/10.1002/spe.43800701...
[5] https://onlinelibrary.wiley.com/doi/abs/10.1002/spe.43801909...
Oyunları sevdiğini ama kodlama kafasının olmadığını söyleyen bir arkadaşım vardı; Human Resource Machine sayesinde fiilen kod yazmaya başladı ve bazı çözümleri, yılların deneyimine sahip benimkilerden daha iyiydi
12 yaşındaki çocuğum matematikten nefret ediyor ama Human Resource Machine ve SpaceChem’de şaşırtıcı derecede iyi. Lise matematiğiyle programlamanın matematiği temelde farklı mı diye merak ediyorum
Çok ilginç. Günümüz bilgisayarlarının bellek boyutları düşünüldüğünde, kısa mnemonic’lerin mühendislik açısından iyi bir tercih olmadığını hep düşünmüşümdür
Burada da yapılması gereken ilk şey komutların ne yaptığını öğrenip hatırlamak. Adları daha açık yazılmış biçimlere dönüştürmek; öğrenmeyi, hatırlamayı ve kod okumayı çok daha kolaylaştırır. İnsanların bunu sık yapmaması şüphe uyandırıyor
Bu tür bir açığın mümkün olması da bence tüm sistem tasarımının başarısızlığına işaret ediyor. Bunun eğlenceli bir oyun olmadığı ya da öğrenmek için iyi bir yöntem olmadığı anlamına gelmez; ama mühendislikte yapısal sorunlar fazla kolay kabulleniliyor. Çoğu kişi o yapısal kusuru göremeyecek durumda
Çocukların küçümsenmediklerinde gerçekten iyi karşılık verdiğini düşünüyorum. En azından benim çocuğumda böyle oldu
Rastgele okuma ve yazmayı yapısal kusur olarak görmeyen biri olduğunu mu düşünüyorsun? Binlerce kişi bu sorun üzerinde çalışıyor ve epey ilerleme de kaydediyor. Aynı zamanda, peek ve poke hâlâ eğlenceli bence
Bu gerçekten harika. Şirkette denemek isterim
Oldukça eğlenceli görünüyor. Hangi yaş aralığına uygun olduğunu düşünüyorsunuz?
bug()içinde hızlı bir buffer overflow ile ana döngüden çıkmayı 10-15 yaşındakilerin de yapabileceğini düşünüyorumKızım 12 yaşında ve birlikte keyifle oynuyoruz. Zor kazanma koşulu, yani rakibi
game_over()fonksiyonuna jump ettirmek daha zor; ama 5-6 ay içinde oraya ulaşabileceğini düşünüyorumYetişkinlerden emin değilim. Bazı insanlar assembly’den şeytan icadıymış gibi korkuyor; onları oynatmak çocuklardan daha zor olabilir
İlginç olan, dünyayı kendi aynammış gibi görme eğilimimiz
Ben buffer overflow ve programlamaya ilgi duyuyorum diye kızımın da doğal olarak büyük ilgi duyacağını varsaymak ne kadar olası? Üstelik ilk çocuk ve ikinci çocuğun kız olması gibi etkenler de varsa olasılık daha düşük görünüyor; yine de birçok babanın bunu zorladığını görüyorum
Böyle bir proje yaparken, bunun en azından bir ölçüde kibir projesi olduğunun farkında mıydınız merak ediyorum. Her hâlükârda ben böyle şeylerle ilgileniyorum, bu yüzden yayımladığınız için sevindim
Projenin yaratıcısının kendi kibrinden dolayı kızına bunu dayattığını ima ediyorsun; bunun dayanağı nerede? Sitede birkaç sayfaya baktım ama bunu ima eden hiçbir şey görmedim; tam tersine kızının bundan keyif aldığına ve çok ilgili olduğuna dair yumuşak ifadeler birkaç kez vardı
Kızının önce babasının bilgisayarda ne yaptığını sürekli merak ederek başlamış olma ihtimalini neden dışlıyorsun? Küçük başlamış, sonra ilgi alanını paylaşan biriyle küçük bir ortak kâşif arasında çift yönlü bir sürece dönüşmüş olabilir
Gerçekte nasıl olduğunu ben de bilmiyorum, ama sen de bilmiyorsun. Eğitimle birkaç yıl ilgilenmiş biri olarak çocukların genel olarak sanılandan çok daha iyi öğrenenler olduğunu düşünüyorum. Okul yapısı da bunun bir nedeni olabilir, ama özünde bu tür sınırlayıcı inançlar da olabilir. Kızıyla ve dünyayla kendi ilgi ve tutkusunu paylaşmaya çalışan bu babayı alkışlamak istiyorum
Bunlardan bazılarının değeri olacak, bazılarının olmayacak. Olasılıklar her zaman aleyhimize. Hayat böyle
64-bit RISC-V kod yolu kararlı hâle gelip yeterince iyi çalıştığında ve “buffer overflow” bile ortadan kalktığında, C/C++ sürekli sözdizimini değiştirmediğine göre planlı eskitmeyi nasıl yapacaklar? Zavallı ruhlar…
Bir dakika.
Assembly kodlama içeren bir tabletop boardgame mi? Bunu neden daha önce düşünmedim? :D
PL/I, string/array sınır denetimi, aşağı değil yukarı büyüyen stack gibi kısımları doğru yapmıştı
https://www.acsac.org/2002/papers/classic-multics.pdf