Soru:
Demontaj neden kesin bir bilim değil?
Einar
2013-08-04 18:44:00 UTC
view on stackexchange narkive permalink

Çaylak burada.

Wikipedia'dan

Parçalara ayırma kesin bir bilim değildir: değişken genişlikli talimatlara sahip CISC platformlarında veya kendi kendini değiştiren kodun varlığı, tek bir programın iki veya daha fazla makul sökme işlemine sahip olması mümkündür. Programın çalıştırılması sırasında gerçekte hangi talimatlarla karşılaşılacağının belirlenmesi, kanıtlanmış çözülemez durdurma sorununa indirgenir.

  1. Neden CISC'de parçalara ayırma kesin bir bilim değil? Parçalanmış kodun yeniden birleştirilmesinin farklı, ancak benzer işlem kodları üretebileceğini anlıyorum. aynı talimat, ancak benzer oldukları için ortaya çıkan programı etkilememelidir. Ayrıca, eğer bu kesin bir bilim değilse, yani toplayın (parçalayın (opcode))! = Opcode, CPU işlem kodu akışını hangi yolu yorumlayacağını nasıl belirleyebilir?

  2. Bir yan not olarak, bu, bir RISC platformunda sökmenin tam bir bilim olduğu anlamına mı geliyor?

Daha soyut bir neden, derlemenin 1'e 1 olmaması olabilir. Her yüksek seviyeli ifade, benzersiz bir işlem kodu kümesine derlenmez. Matematikte olduğu gibi, bir alanınız ve aralığınız var. Derleme, etki alanını aralıkla eşler. Etki alanı aralıktan çok daha büyükse, derleme 1'e 1 değildir. Yani genel (genel anlamıyla çözüm zorunluluktan gelmez, ancak bir seçim yaparsınız) ters veya yeniden derleme / parçalama işlevi yoktur.
Beş yanıtlar:
0xea
2013-08-04 18:52:54 UTC
view on stackexchange narkive permalink

İlk soruyu cevaplamak için. En büyük sorun, verileri koddan gerçekten ayıramamanızdır. Parçalara ayırmak için temel olarak iki yaklaşım vardır:

  1. Doğrusal süpürme
  2. Yinelemeli geçiş

Doğrusal süpürme kullanan ayırıcılar bazı adreslerde başlar ve parçalara ayrılır herhangi bir şekilde çözülen kod hakkında atlama veya mantık yürütmeden sonuna kadar tek tek talimatlar. Örneğin, SoftICE ve Windbg doğrusal süpürme kullanır. Bu bir sorun olabilir çünkü ne zaman duracağınızı bilmiyorsunuz. Talimatların nerede bittiğini ve verilerin nerede başladığını bilmiyorsunuz. Bunun için bölüm boyutları gibi çalıştırılabilir format meta verilerine güvenmeniz gerekir. Bunu sorunlu kılan da budur.

Öte yandan, yinelemeli geçiş algoritmaları atlamaları ve çağrıları dikkate alır ve disasembler atlamaları takip eder, yalnızca gerçekten yürütülecek kodu çözer. Örneğin IDA ve OllyDBG bu yaklaşımı kullanır. Temelde kodun ne olduğunu ve verilerin ne olduğunu bilmesinin yararı vardır. Ama belli ki dezavantajları var. Birincisi, saf özyinelemeli geçiş ile, tüm kod çözülmeyecektir. Örneğin, doğrudan referans verilmeyen, çalışma zamanında adresleri hesaplanarak çağrılan işlevler tespit edilmeyecektir. Yine, motorların bu sorunu aşmak için bazı buluşsal yöntemleri vardır.

Örneğin, birkaç talimatı geri alan bir programı ele alalım, ancak daha önce çözülmüş ve çalıştırılmış bir talimatın başlangıcına inmek yerine, tam ortasında atlar. Dağıtıcı hangisinin gösterileceğine nasıl karar vermelidir? Kodlayıcının niyeti buysa, her ikisi de geçerli olabilir. Yinelemeli geçiş çözücü muhtemelen ilk demonte edilmiş versiyonu gösterecek ve sadece bir fonksiyonun ortasına bir atlamayı işaretleyecektir. Ancak baytları ayrıntılı olarak incelemeden, atlamadan sonra gerçekte neyin yürütüleceğini söylemek zor.

Pek çok başka örnek var.

Bu yaklaşımların her ikisinin de bir başka büyük sorunu, kendi kendini değiştiren koddur. Sadece statik olarak yapılamaz.

CPU, kodu yürüttüğü için kendisinde bu sorunlardan herhangi birine sahip değildir, başka bir deyişle dinamiktir.

İkinci soruyu yanıtlamak için bunun olduğunu sanmıyorum yalnızca CISC mimarileriyle ilgili bir sorun, bunlardan bazıları RISC'lere de uygulanabilir.

Aslında CISC değil von Neumann mimarisi (kendi kendini değiştirmeye izin verir) demeliler.
@Ruslan: Kendi kendini değiştirmeyle ilgisi yoktur. Mesele şu ki, değişken genişlikli komutlar belirsiz yorumlara izin veriyor. Örneğin. ayrı, geçerli bir talimat dizisinin okunmasına neden olarak bir talimatın ortasına `JMP 'ekleyebilirsiniz. Bu, RISC * (neredeyse?) * Her zaman olan sabit genişlikli komut setlerinde mümkün değildir.
evet, kendi kendini değiştiren kod belirttiğim gibi tek sorun değil ve 0xC0000022L daha fazla açıklıyor
* "Kendi kendini değiştiren kod tek sorun değil" * - Bu ** bir ** sorun bile değil. Sabit genişlikli bir mimaride, kendi kendini değiştiren kodu sökme sorunu olmamalıdır. Tabii ki, sökücü, derlemenin * çalışma zamanı değişikliklerinden * sonra (ki bu karar verilemeyen bir sorundur) * neye benzeyeceğini bilemez, ancak bu, ikiliyi sökmenin kapsamı dışındadır.
OllyDBG doğrusal süpürme kullanıyor mu?
Öyle olduğundan oldukça emindim, ancak şu anda bu iddianın referansını bulamıyorum. SoftICE ile karıştırmış olabilirim. Cevapta düzeltildi. Teşekkürler!
debray
2013-08-04 20:26:15 UTC
view on stackexchange narkive permalink

Önceki yanıtlarda açıklanan sorunlara ek olarak, burada bir programın birden fazla sökme yapabileceği başka bir yol var. Aşağıdaki mantığa sahip bir kodu düşünün (sözdizimi-kasaplık için özür dilerim):

buf = ... bir dizi ...
val = okuyun ()
eğer (val == 7) {
mprotect (buf, ..., PROT_EXEC); / * buf'u çalıştırılabilir hale getir * /
goto buf; / * buf çalıştır * /
}
başka {
yazdır (buf);
}

Bu durumda, buf 'un kod (ve bu nedenle demonte edilmesi) veya veri (ve dolayısıyla demonte edilmemesi) olup olmadığı girişe bağlıdır. Bu nedenle, programın birden fazla olası demontajı vardır. Bunun nedeni, kodun temelde verilerden ayırt edilemez olması ve RISC ile CISC ile hiçbir ilgisinin olmamasıdır.

Bu belgede kodun nasıl mantıklı görünen veriler olarak gizlenebileceğine dair keyifli bir tartışma var:

J. Mason, S. Small, F. Monrose, G. MacManus. İngilizce Shellcode. Bilgisayar ve İletişim Güvenliği 16. ACM Konferansı Bildirileri (CCS), Chicago, IL. Kasım 2009. [PDF]

0xC0000022L
2013-08-04 19:02:27 UTC
view on stackexchange narkive permalink

Bence yorumun açıklamak istediği şey, x86'yı burada örnek olarak ele alan CISC'de sökme işleminin birkaç olası makul temsilinin olması gerçeğidir. Ancak bunun kısmen tipik RISC uygulamaları için de söylenebileceğini düşünüyorum, ancak montajcı, farklı bir temel (RISC) işlem kodlarının kombinasyonuyla temsil edilen CISC benzeri bir anımsatıcı gibi bir şey sunabilir.

Örneğin, rep öneki birçok işlem kodunda anlamsızdır ve yine de geçmişte tersine mühendisler için işi zorlaştırmak için kullanılmıştır. Bununla birlikte, nihayetinde sökmeye bakan bir insan için daha iyi temsil hangisidir? rep önekine sahip olan veya olmayan olan (bu, CPU'nun ne yaptığını daha fazla yansıtır).

Çözücü bulduğu şeye sadık kalırsa, rep öneki. Öte yandan, makine kodunu bir insan için okunabilir hale getirmek de demonte eden kişinin görevidir. Bu nedenle, fazladan yol kat etmek ve gereksiz öneklerin kısalık için kaldırıldığından emin olmak mükemmel bir anlam ifade ediyor. Benzer şekilde, nop 'u (işlem yok) temsil eden çok baytlı işlem kodları tamamen çıkarılabilir veya diğerlerinden farklı olacak şekilde (örneğin IDA'nın hizalama baytlarıyla yaptığı gibi) veya her biri nop olarak gösterilebilir sırasıyla (1 ve 5 baytlık sürümler için).

Benim bakış açıma göre makine kodunu sökmek tam bir bilimdir çünkü oradaki her işlem kodu için tam bir anımsatıcıdır (burada jne / jnz ikiliğini göz ardı edelim). Aksi takdirde, sizin de belirttiğiniz gibi, CPU ne yapacağını nasıl çözerdi? Bununla birlikte, tersine mühendise bir temsil vermek için, bazen okunabilirlik ve anlaşılma için (sık sık söylemeye cesaret edebilir miyim?) Sonradan işlenir. Sökücü ve sökme işlemlerinin değiştiği yer burasıdır.


Diğer bir sorun da, olası tüm kod yollarını izlemek için buluşsal yöntemlerin kullanılması gerektiğidir (ve bu iyi bir sökücünün ayırt edici özelliklerinden biridir). Aksi takdirde verileri koddan ayırmak zordur. Ancak bildiğim kadarıyla "CISC" bu özellik için seçilemez, bu nedenle yorumun ana nedeninin bu olmadığını düşünüyorum.

Codoka
2015-04-10 13:23:05 UTC
view on stackexchange narkive permalink

Parçalara ayırmadaki kritik bir sorun, kodu ikili yürütülebilir dosyalardaki verilerden kesin olarak ayırmaktır. Bu amaçla, dolaylı sıçramaların (çalışma zamanında hesaplanan adreslere atlamalar) hedeflerini tam olarak belirleyebilen statik bir analize ihtiyacınız olacaktır. Anahtar hedef hesaplaması ve C ++ 'da vtables dahil olmak üzere birkaç dolaylı atlama örneği vardır.

Bu tür bir statik analiz, eğer varsa, karar verilemediği bilinen Durdurma Problemini çözebilir. Bu nedenle böyle bir statik analiz var olamaz. Buna dayanarak, sökme araçlarının dolaylı atlama hedeflerini belirlemek için buluşsal yöntemlere ve / veya tahminlere güvenmesi gerekir. Bununla ilgili daha fazla ayrıntı ARM demontajının sağlamlığı buradaki benzer bir soruya verilen yanıtlarda bulunabilir. Kod / veri ayırma görevi, ikililerin gizlendiği ve / veya kendi kendini değiştiren kodlara sahip olduğu durumlarda daha da zorlaşır.

İkililerden yaklaşık kod ve veri çıkardıktan sonra. Öyleyse, onlara anlambilim ekleme sorunu ile uğraşmak gerekiyor ki bu da zor. Örneğin bir struct {int i; int j;} , talimatlarla erişildiğinde int arr [2] 'ye benzer görünür. Bu nedenle, çalıştırılan koda, hata ayıklama sembolleri gibi harici bilgilere dayanmadan benzersiz anlambilim eklemek zordur.

Daha büyük bir dizi, elemanlarına nasıl erişildiği ve hatta belki de sizin "arr [2]" kadar küçük olan bir yapıdan ayırt edilebilir.
@Jongware katılıyor. Demontajda birden fazla geçerli anlama sahip olma fikrini göstermek için sadece basit bir örnek.
perror
2017-06-20 22:03:54 UTC
view on stackexchange narkive permalink

Ön Açıklamalar

Her şeyden önce, bir ikili programın doğru bir şekilde sökülmesinin ne olduğu konusunda anlaşmamız gerekir. Şu tanımı öneririm:

Bir ikili programın doğru bir şekilde sökülmesi , girdi ne olursa olsun program tarafından yürütülebilecek tüm olası komutlar kümesini verecektir

Bunu belirtmenin bir başka yolu, alabileceği her girdide programın tüm olası çalıştırmalarının talimatlarını ifşa ettiğimizi söylemektir.

Durma Problem

Burada, bir Turing makinesindeki durma problemiyle şu şekilde tanımlanabilecek bir paralellik kurabiliriz ( Wikipedia):

Durma sorunu , gelişigüzel bir bilgisayar programının açıklamasından ve bir girdiden, programın çalışmasını bitirip bitirmeyeceğini veya sonsuza kadar çalışmaya devam edip etmeyeceğini belirleme sorunudur.

Bu (görünüşe göre) çok basit problem Turing tarafından kararlaştırılamaz olarak gösterilmiştir, yani bir programla belirli sayıda vakayı otomatik olarak halledebilsek bile, bazı patolojik vakalar her zaman makinenin / programın verilen girdide evet mi hayır mı olduğunu söyleyemeyecek olan programımız.

Ve tabii ki bu tür patolojik vakalar sonsuz sayıda (bu nedenle, bunları saymak için bir umut yok) özel durumlar olarak birer birer).

Demontaj sorunumuza geri dönelim!

Bir programın tüm olası yollarını keşfetmek, gerçekten de durdurma sorunu nedeniyle !

Gerçekten de, durma problemini formüle etmenin ikili bir yolu, erişilebilirlik problemidir , burada belirli bir noktaya ulaşmanıza izin veren bir girdi olup olmadığını bilmek istediğiniz programı. Ve bellekteki belirli bir yere program tarafından erişilip bir talimat olarak yorumlanıp yorumlanamayacağını bilmek ( yani , talimat işaretçisi bir noktada bu adresin değerini alır) şudur bir erişilebilirlik sorunu.

Yani, parçalarına ayırma karar verilemez .

Ama Gerçek Dünyada?

Biliyorum, biliyorum, bu sadece Matematik ... Gerçek değil ... Gizemlerin çoğu (gönüllü olsun ya da olmasın) çözülebilir ve ikili koddan otomatik olarak kaldırılabilir ...

Bunun nedeni esasen bu gizlemeleri yapan kişilerin karar verilemez hale problemler ...

Programınıza, karar verilemeyen bir problemin hesaplamasını, hatta ona uygulanan herhangi bir otomatik muhakemeyi bozacak kadar zor ve karmaşık bir şeyi eklediğinizi hayal edin.

Bir örnek vermek gerekirse, Collatz dizisini ( Wikipedia) alalım, bu dizinin bir süre sonra her zaman 1'de biteceği varsayılır. Ancak, arkasındaki aritmetik problem o kadar karmaşıktır ki, bu varsayım yaklaşık bir yüzyıldan beri geçerlidir ... Bu, kullanılacak mükemmel bir opak yüklemdir! Elbette, böyle bir varsayımın kanıtı mevcut olabilir, ancak bu sorun, üzerine inşa etmeye başlayacak ve bilgisayarın bir programın durum uzayını keşfetmesini karıştıracak kadar karmaşıktır.

Aslında, şu anki durum budur. günümüzde güçlü gizlemede araştırma yönü ... Daha önce kullanılan küçük numaralarla neredeyse bitirdik ve insanlar daha iyi temelli problemler üzerine bir şeyler inşa etmeye başladılar. Kriptoloji ile karşılaştırılacak yazılım gizleme konusunda bir Shannon 'un (bilgi teorisinin babası) bir eşdeğerini hala kaçırsak bile.

Son Kelimeler

Böylece, sökme sorununun durma sorunuyla güçlü bir şekilde bağlantılı olduğunu gördük. Ayrıca, oldukça karmaşık problemlerin kullanılması, modern yazılım gizlemesinde bir sonraki adım olabilir.

Sökme tekniklerinde en son teknolojiye bağlı kalmak zorunda olsaydık, mevcut sökme araçlarının muhtemelen yapabileceğimizin çok gerisinde olduğu gerçeğiyle ilgili son bir söz söylemek istiyorum. Tarih öncesi araçların ne kadar tarih öncesi olduğunu gördüğümde hep acı içinde ağlıyorum ... ama tüm modern teknikleri uygulamaya koymak o kadar çok geliştirme ve bakım çabası gerektirecek ki hiç kimse bunu yapmaya hazır görünmüyor (ama bu sadece benim mütevazı görüş).



Bu Soru-Cevap, otomatik olarak İngilizce dilinden çevrilmiştir.Orijinal içerik, dağıtıldığı cc by-sa 3.0 lisansı için teşekkür ettiğimiz stackexchange'ta mevcuttur.
Loading...