1. Çözüm: C (Mac OS X x86_64), 109 bayt
Golf_sol1.c kaynağı
main[]={142510920,2336753547,3505849471,284148040,2370322315,2314740852,1351437506,1208291319,914962059,195};
Yukarıdaki programın __DATA segmentinde yürütme erişimi ile derlenmesi gerekir.
clang golf_sol1.c -o golf_sol1 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
Ardından programı çalıştırmak için aşağıdakileri çalıştırın:
./golf_sol1 $(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
Sonuçlar:
Ne yazık ki Valgrind, sistem çağrılarından ayrılan belleği izlemiyor, bu yüzden iyi bir tespit sızıntısı gösteremiyorum.
Ancak ayrılan belleğin (MALLOC meta verileri) büyük bölümünü görmek için vmmap'a bakabiliriz.
VIRTUAL REGION
REGION TYPE SIZE COUNT (non-coalesced)
=========== ======= =======
Kernel Alloc Once 4K 2
MALLOC guard page 16K 4
MALLOC metadata 16.2M 7
MALLOC_SMALL 8192K 2 see MALLOC ZONE table below
MALLOC_TINY 1024K 2 see MALLOC ZONE table below
STACK GUARD 56.0M 2
Stack 8192K 3
VM_ALLOCATE (reserved) 520K 3 reserved VM address space (unallocated)
__DATA 684K 42
__LINKEDIT 70.8M 4
__TEXT 5960K 44
shared memory 8K 3
=========== ======= =======
TOTAL 167.0M 106
TOTAL, minus reserved VM space 166.5M 106
açıklama
Bu nedenle, gelişmiş çözüme geçmeden önce burada neler olup bittiğini açıklamam gerektiğini düşünüyorum.
Bu ana işlev, C'nin eksik tip bildirimini kötüye kullanıyor (bu yüzden, karakterleri yazarken karakterleri boşa harcamamız gerekmeden int olarak varsayılan değer). Bağlayıcı yalnızca, çağrılması main
gereken bir simgeyi bulamama konusunda umurunda değil . Yani burada ana idam edilecek kabuk kodumuzla başlattığımız bir int dizisi yapıyoruz. Bu nedenle, ana __TEXT segmentine eklenmeyecek, bunun yerine __DATA segmentine eklenecektir, bu nedenle programı çalıştırılabilir bir __DATA segmentiyle derlememiz gerekir.
Anada bulunan kabuk kodu şudur:
movq 8(%rsi), %rdi
movl (%rdi), %eax
movq 4(%rdi), %rdi
notl %eax
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
Bunun yaptığı şey, bir sayfa hafıza ayırmak için syscall işlevini çağırmaktır (syscall mach_vm_allocate dahili olarak kullanır). RAX, 0x100000a'ya eşit olmalıdır (sisteme hangi işlevi istediğimizi söyler), RDI ayırma hedefini tutarken (bizim durumumuzda bunun mach_task_self () olmasını istiyoruz), RSI işaretçiyi yeni oluşturulan belleğe yazmak için adresi tutmalıdır (bu yüzden sadece yığındaki bir kısma işaret ediyoruz), RDX tahsisin büyüklüğünü tutar (sadece bayttan tasarruf etmek için RAX veya 0x100000a'yı geçiyoruz), R10 bayraklarını tutar herhangi bir yere tahsis edilecek).
Şimdi RAX ve RDI'nin değerlerini nereden aldıkları açıkça belli değil. RAX'in 0x100000a olması gerektiğini ve RDI'nin mach_task_self () döndürdüğü değer olması gerektiğini biliyoruz. Neyse ki mach_task_self () aslında her seferinde aynı hafıza adresinde olan bir değişken (mach_task_self_) için bir makrodur (bununla birlikte yeniden başlatılması gerekir). Özel örneğimde mach_task_self_, 0x00007fff7d578244 konumunda bulunuyor. Böylece talimatları kısmak için, bu verileri argv'den iletiyoruz. Bu yüzden programı bu ifadeyle çalıştırıyoruz.$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
ilk argüman için. Dize, RAX değerinin (0x100000a) yalnızca 32 bittiği ve kendisine uygulanmış birinin tamamlayıcısına sahip olduğu iki değerdir (bu nedenle boş bayt yoktur; orijinali elde etmek için DEĞİLDİR), sonraki değer sonuna kadar 2 ekstra önemsiz bayt eklenerek sola kaydırılan RDI (0x00007fff7d578244) (yine de boş baytları dışlamak için tekrar orijinal haline getirmek için sağa kaydırıyoruz).
Sistemden sonra yeni ayrılan hafızamıza yazıyoruz. Bunun nedeni, mach_vm_allocate (veya bu sistem çağrısı) kullanılarak ayrılan hafızanın aslında VM sayfaları olması ve otomatik olarak hafızaya kaydedilmemesidir. Aksine, veriler kendilerine yazılana kadar saklanır ve daha sonra bu sayfalar belleğe eşlenir. Sadece ayrılsaydı, şartları yerine getirip getirmediğinden emin değildi.
Bir sonraki çözüm için, kabuk kodumuzda boş bayt bulunmadığı gerçeğinden faydalanacağız ve böylece boyutu küçültmek için program kodumuzun dışına taşıyabiliriz.
2. Çözüm: C (Mac OS X x86_64), 44 bayt
Golf_sol2.c kaynağı
main[]={141986632,10937,1032669184,2,42227};
Yukarıdaki programın __DATA segmentinde yürütme erişimi ile derlenmesi gerekir.
clang golf_sol2.c -o golf_sol2 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
Ardından programı çalıştırmak için aşağıdakileri çalıştırın:
./golf_sol2 $(ruby -e 'puts "\xb8\xf5\xff\xff\xfe\xf7\xd0\x48\xbf\xff\xff\x44\x82\x57\x7d\xff\x7f\x48\xc1\xef\x10\x8b\x3f\x48\x8d\x74\x24\xf8\x89\xc2\x4c\x8d\x50\xf7\x0f\x05\x48\x8b\x36\x89\x36\xc3"')
Sonuç, aynı boyutta bir tahsisat yaptığımızdan önceki gibi olmalıdır.
açıklama
Sızıntı kodumuzun yığınını programın dışına çıkarmamız haricinde, çözüm 1 ile aynı kavramı takip eder.
Anada bulunan kabuk kodu şimdi şöyledir:
movq 8(%rsi), %rsi
movl $42, %ecx
leaq 2(%rip), %rdi
rep movsb (%rsi), (%rdi)
Bu temelde argv içinde geçirdiğimiz kabuk kodunu bu koddan sonra kopyalar (böylece kopyaladıktan sonra, eklenen kabuk kodunu çalıştırır). Bizim lehimize olan şey, __DATA segmentinin en azından bir sayfa boyutunda olacağı, bu yüzden kodumuz o kadar büyük olmasa da, yine de "güvenle" daha fazla yazabiliriz. Dezavantajı, buradaki ideal çözümdür, kopyaya bile ihtiyaç duymaz, bunun yerine doğrudan kodu kullanarak argv kodunu çağırıp çalıştırır. Ancak ne yazık ki, bu hafızanın icra hakkı yoktur. Bu hafızanın haklarını değiştirebiliriz, ancak kopyalamaktan daha fazla kod gerektirebilir. Alternatif bir strateji, harici bir programdan hakları değiştirmek olacaktır (ancak daha fazlası için).
Argv'ye ilettiğimiz kabuk kodu şöyledir:
movl $0xfefffff5, %eax
notl %eax
movq $0x7fff7d578244ffff, %rdi
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
Bu, önceki kodumuzla aynıdır, sadece fark, doğrudan EAX ve RDI için değerleri dahil ettiğimizdir.
Olası Çözüm 1: C (Mac OS X x86_64), 11 bayt
Programın harici olarak değiştirilmesi fikri bize sızıntının harici bir programa taşınması için olası bir çözüm sunar. Asıl programımızın (başvuruların) sadece sahte bir program olduğu ve sızıntı programının hedef programımıza bir miktar bellek ayıracağı yer. Şimdi bunun, bu meydan okuma için kurallara uyup uymayacağından emin değildim, ancak yine de paylaşıyordum.
Bu nedenle, mach_vm_allocate'i hedef programımıza göre hedefle birlikte harici bir programda kullanacak olsaydık, bu meydan okuma programımızın sadece aşağıdakiler boyunca bir şey olması gerektiği anlamına gelebilirdi:
main=65259;
Bu kabuk kodunun kendisine kısa bir atlama (sonsuz atlama / döngü) olduğu için, program açık kalır ve harici bir programdan başvuru yapabiliriz.
Olası Çözüm 2: C (Mac OS X x86_64), 8 bayt
Valgrind çıkışına bakarken yeterince komikti, en azından valgrind'e göre, dyld'in hafızayı sızdırdığını gördüm. Bu yüzden etkili bir şekilde her program bir miktar bellek sızdırıyor. Durum böyle olunca, aslında sadece hiçbir şey yapmayan bir program yapabiliriz (basitçe çıkar) ve bu aslında hafızadan sızıyor olur.
Kaynak:
main(){}
==55263== LEAK SUMMARY:
==55263== definitely lost: 696 bytes in 17 blocks
==55263== indirectly lost: 17,722 bytes in 128 blocks
==55263== possibly lost: 0 bytes in 0 blocks
==55263== still reachable: 0 bytes in 0 blocks
==55263== suppressed: 16,316 bytes in 272 blocks