Montajdan makine koduna (kod oluşturma) nasıl geçilir


16

Kodun makine koduna montajı arasındaki adımı görselleştirmenin kolay bir yolu var mı?

Örneğin, not defterinde bir ikili dosyayı açarsanız, makine kodunun metin olarak biçimlendirilmiş bir gösterimini görürsünüz. Gördüğünüz her bayt (sembol) ikili değeri için karşılık gelen ascii karakter olduğunu varsayalım?

Ama montajdan ikiliye nasıl geçiyoruz, sahne arkasında neler oluyor ??

Yanıtlar:


28

Talimat kümesi belgelerine bakın ve her talimat için bir pic mikrodenetleyicisinden bunun gibi girişler bulacaksınız :

örnek addlw talimatı

"Kodlama" satırı, bu komutun ikili olarak nasıl göründüğünü anlatır. Bu durumda, her zaman 5 tane ile başlar, daha sonra (bir veya sıfır olabilir) umurumda değil, sonra "k" nin eklediğiniz değişmez anlamına gelir.

İlk birkaç bite "opcode" denir, her komut için benzersizdir. CPU temel olarak hangi talimatın olduğunu görmek için opcode'a bakar, daha sonra "k" ların eklenecek bir sayı olarak kodunu çözmeyi bilir.

Sıkıcı, ancak kodlamak ve kodunu çözmek o kadar zor değil. Sınavlarda elle yapmak zorunda kaldığımız bir lisans dersim vardı.

Aslında tam bir yürütülebilir dosya yapmak için, bellek ayırma, şube ofsetleri hesaplama ve işletim sisteminize bağlı olarak ELF gibi bir biçime koyma gibi şeyler yapmanız gerekir .


10

Montaj opcodları çoğunlukla altta yatan makine talimatları ile bire bir yazışmalara sahiptir. Tek yapmanız gereken montaj dilindeki her opcode'u tanımlamak, ilgili makine talimatıyla eşleştirmek ve makine talimatını karşılık gelen parametreleriyle (varsa) bir dosyaya yazmaktır. Daha sonra kaynak dosyadaki her ek opcode için işlemi tekrarlayın.

Tabii ki, bir işletim sistemini düzgün bir şekilde yükleyecek ve çalıştıracak yürütülebilir bir dosya oluşturmak için bundan daha fazlası gerekir ve çoğu iyi montajcı, opcodların makine talimatlarına basitçe eşleştirilmesinin ötesinde bazı ek yeteneklere sahiptir (örneğin makrolar gibi).


7

İhtiyacınız olan ilk şey bu dosya gibi bir şey . Bu NASM montajcı tarafından kullanılan x86 işlemciler için talimat veritabanı (ki aslında talimatları çeviren parçalar olmasa da, yazmaya yardımcı oldum). Veritabanından rastgele bir satır seçelim:

ADD   rm32,imm8    [mi:    hle o32 83 /0 ib,s]      386,LOCK

Bunun anlamı, talimatı tanımlamasıdır ADD. Bu komutun birden fazla varyantı vardır ve burada tarif edilen spesifik, 32 bitlik bir kayıt veya bellek adresi alan ve hemen 8 bitlik bir değer ekleyen varyanttır (yani talimatta doğrudan dahil edilen bir sabit). Bu sürümü kullanacak örnek bir montaj talimatı şudur:

add eax, 42

Şimdi, metin girişinizi almanız ve bireysel talimatlara ve işlenenlere ayrıştırmanız gerekir. Yukarıdaki talimat için, bu muhtemelen talimatı içeren bir yapıya ADDve bir işlenen dizisine (kayıt EAXve değere referans 42) neden olacaktır. Bu yapıya sahip olduğunuzda, talimat veritabanını çalıştırırsınız ve hem komut adı hem de işlenen türleriyle eşleşen satırı bulursunuz. Bir eşleşme bulamazsanız, bu, kullanıcıya sunulması gereken bir hatadır ("opcode ve işlenenlerin yasadışı kombinasyonu" veya benzeri, normal metindir).

Veritabanından satır aldıktan sonra, bu talimat için üçüncü sütuna bakıyoruz:

[mi:    hle o32 83 /0 ib,s] 

Bu, gerekli makine kodu talimatının nasıl oluşturulacağını açıklayan bir dizi talimattır:

  • Bu mi, işlenenlerin bir tanımlamasıdır: bir a modr/m(kayıt veya bellek) işleneni (yani modr/m, talimatın sonuna bir bayt eklememiz gerekeceği anlamına gelir, daha sonra geleceğiz) ve bir acil talimat ( talimatın açıklamasında kullanılmalıdır).
  • Sıradaki hle. Bu, "kilit" önekini nasıl ele aldığımızı tanımlar. "Kilit" kullanmadık, görmezden geldik.
  • Sıradaki o32. Bu bize 16 bit çıkış biçimi için kod monte edersek, talimatın işlenen boyut geçersiz kılma önekine ihtiyacı olduğunu söyler. 16-bit çıktı üretiyor olsaydık, şimdi öneki ( 0x66) üretirdik , ama öyle olmadığımızı ve devam edeceğimizi varsayacağım.
  • Sıradaki 83. Bu, onaltılı olarak gerçek bir bayttır. Biz çıkardık.
  • Sıradaki /0. Bu, modr / m bytem içinde ihtiyaç duyacağımız bazı ekstra bitleri belirtir ve üretmemize neden olur. modr/mBayt kodlamak kayıtları veya dolaylı bellek başvuruları için kullanılır. Tek bir işlenen var, bir sicil. Kayıt başka bir veri dosyasında belirtilen bir numaraya sahiptir :

    eax     REG_EAX         reg32           0
  • reg32Orijinal veritabanından gerekli talimat boyutunu kabul edip etmediğini kontrol ediyoruz (öyle). 0Kasan sayıdır. Bir modr/mbayt işlemcisi tarafından belirlenen bir veri yapısı, böyle bu görünüm olup:

     (most significant bit)
     2 bits       mod    - 00 => indirect, e.g. [eax]
                           01 => indirect plus byte offset
                           10 => indirect plus word offset
                           11 => register
     3 bits       reg    - identifies register
     3 bits       rm     - identifies second register or additional data
     (least significant bit)
  • Bir sicille çalıştığımız için, modalan öyle 0b11.

  • regTarla, Kullandığımız kayıt sayısıdır0b000
  • Bu talimatta yalnızca bir kayıt olduğundan, rmalanı bir şeyle doldurmamız gerekiyor . Belirtilen ekstra veriler bunun /0içindi, bu yüzden bunu rmalana koyduk 0b000.
  • Bu modr/mnedenle bayt 0b11000000veya 0xC0. Bunun çıktısını alıyoruz.
  • Sıradaki ib,s. Bu, imzalı bir anlık baytı belirtir. İşlenenlere bakarız ve hemen bir değere sahip olduğumuzu not ederiz. İşaretli bir bayta dönüştürüyoruz ve çıktısını veriyoruz ( 42=> 0x2A).

Tamamen montajlı talimat nedenle: 0x83 0xC0 0x2A. Baytların hiçbirinin bellek referansı oluşturmadığını (çıkış modülünün çalışıp çalışmadığını bilmeleri gerekebilir) bir notla birlikte çıkış modülünüze gönderin.

Her talimat için tekrarlayın. Referans alındığında ne ekleyeceğinizi bilmeniz için etiketleri takip edin. Nesne dosyası çıkış modüllerinize aktarılan makrolar ve yönergeler için olanaklar ekleyin. Temel olarak bir montajcı bu şekilde çalışır.


1
Teşekkür ederim. Harika bir açıklama ama "0x83 0xB0 0x2A" yerine "0x83 0xC0 0x2A" olmamalı, çünkü 0b11000000 = 0xC0
Kamran

@Kamran - $ cat > test.asm bits 32 add eax,42 $ nasm -f bin test.asm -o test.bin $ od -t x1 test.bin 0000000 83 c0 2a 0000003... evet, oldukça haklısın. :)
Jules

2

Uygulamada, bir montajcı genellikle doğrudan bir ikili çalıştırılabilir dosya üretmez , ancak bazı nesne dosyaları (daha sonra bağlayıcıya beslenir ). Ancak, istisnalar vardır (doğrudan bazı ikili yürütülebilir dosyalar üretmek için bazı montajcıları kullanabilirsiniz; bunlar nadirdir).

İlk olarak, birçok montajcının bugün ücretsiz yazılım programları olduğuna dikkat edin . Yani bilgisayarınıza GNU kaynak kodunu ( binutils'in bir parçası ) ve nasm olarak indirin ve derleyin . Sonra kaynak kodlarını inceleyin. BTW, Linux'u bu amaçla kullanmanızı öneririm (çok geliştirici dostu ve ücretsiz yazılım dostu bir işletim sistemidir).

Bir montajcı tarafından üretilen nesne dosyası özellikle bir kod segmenti ve yer değiştirme talimatları içerir. İşletim sistemine bağlı olarak iyi belgelenmiş bir dosya biçiminde düzenlenmiştir. Linux'ta bu biçim (nesne dosyaları, paylaşılan kütüphaneler, çekirdek dökümleri ve yürütülebilir dosyalar için kullanılır) ELF'dir . Bu nesne dosyası daha sonra bağlayıcıya girilir (sonunda yürütülebilir bir dosya oluşturur). Yer değiştirme ABI tarafından belirtilir (örn. X86-64 ABI ). Daha fazla bilgi için Levine'nin Bağlayıcılar ve Yükleyiciler kitabını okuyun .

Böyle bir nesne dosyasındaki kod segmenti delikli makine kodunu içerir (yer değiştirme bilgisi yardımıyla, bağlayıcı tarafından doldurulacaktır). Bir montajcı tarafından üretilen (yeniden yerleştirilebilir) makine kodu, açıkça bir komut kümesi mimarisine özeldir . X86 veya x86-64 (çoğu dizüstü veya masaüstü işlemcileri kullanılan) ISA'lar kendi ayrıntılarında korkunç karmaşıktır. Ancak, öğretim amacıyla y86 veya y86-64 adı verilen basitleştirilmiş bir alt küme icat edilmiştir. Slaytları okuyun . Bu sorunun diğer cevapları da biraz açıklıyor. Bilgisayar Mimarisi ile ilgili güzel bir kitap okumak isteyebilirsiniz .

Çoğu montajcı iki geçişte çalışır , ikincisi taşınma ya da ilk geçişin bazı çıktılarını düzeltir. Şimdi her zamanki ayrıştırma tekniklerini kullanıyorlar (bu yüzden belki Ejderha Kitabı'nı okuyun ).

İşletim sistemi çekirdeği tarafından yürütülebilir dosyanın nasıl başlatıldığı (örn. execveSistem çağrısı Linux'ta nasıl çalışır) farklı (ve karmaşık) bir sorudur. Genellikle bazı sanal adres alanı kurar ( bunu yapan işlemde (2) ...) ve işlem iç durumunu yeniden başlatır ( kullanıcı modu kayıtları dahil ). Bir dinamik bağlayıcı olarak -bunlar ld-linux.so (8) Linux- üzerinde zamanında rol oynayabilir. İşletim Sistemi: Üç Kolay Parça gibi iyi bir kitap okuyun . OSDEV wiki de yararlı bilgi veriyor.

PS. Sorunuz o kadar geniştir ki, bununla ilgili birkaç kitap okumalısınız. Bazı (çok eksik) referanslar verdim. Onlardan daha fazlasını bulmalısın.


1
Nesne dosya biçimleriyle ilgili olarak, yeni başlayanlar için NASM tarafından üretilen RDOFF biçimine bakmanızı tavsiye ederim. Bu, kasıtlı olarak gerçekçi bir şekilde mümkün olduğu kadar basit olacak ve hala çeşitli durumlarda çalışacak şekilde tasarlanmıştır. NASM kaynağı, format için bir bağlayıcı ve bir yükleyici içerir. (Tüm açıklamalar - Bunların hepsini tasarladım ve yazdım)
Jules
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.