Tujuan instruksi NOP dan menyelaraskan pernyataan dalam rakitan x86


15

Sudah setahun atau lebih sejak saya terakhir mengambil kelas perakitan. Di kelas itu, kami menggunakan MASM dengan perpustakaan Irvine untuk membuatnya lebih mudah diprogram.

Setelah kami mempelajari sebagian besar instruksi, ia mengatakan bahwa instruksi NOP pada dasarnya tidak melakukan apa-apa dan tidak perlu khawatir menggunakannya. Lagi pula, itu tentang ujian tengah semester dan dia memiliki beberapa contoh kode yang tidak akan berjalan dengan baik, jadi dia menyuruh kami untuk menambahkan instruksi NOP dan itu berfungsi dengan baik. Saya bertanya saya setelah kelas mengapa dan apa yang sebenarnya terjadi, dan dia bilang dia tidak tahu.

Adakah yang tahu?


NOP tidak melakukan apa-apa, tetapi ia mengkonsumsi siklus. Saya tidak berpikir pertanyaan Anda dapat dijawab, tanpa kode kami hanya bisa menebak. Nah, saya kira akan menjadi geser NOP ...
yannis

11
NOP sebenarnya melakukan sesuatu. Itu menambah Pointer Instruksi.
EricSchaefer

Jawaban:


37

Sering kali NOPdigunakan untuk menyelaraskan alamat instruksi. Ini biasanya ditemui misalnya ketika menulis Shellcode untuk mengeksploitasi buffer overflow atau kerentanan format string .

Katakanlah Anda memiliki lompatan relatif ke 100 byte ke depan, dan buat beberapa modifikasi pada kode. Kemungkinannya adalah modifikasi Anda mengacaukan alamat target lompatan dan karenanya Anda juga harus mengubah lompatan relatif tersebut. Di sini, Anda dapat menambahkan NOPs untuk mendorong alamat target ke depan. Jika Anda memiliki beberapa NOPs antara alamat target dan instruksi lompat, Anda dapat menghapus NOPs untuk menarik mundur alamat target.

Ini tidak akan menjadi masalah jika Anda bekerja dengan assembler yang mendukung label. Anda dapat melakukannya JXX someLabel(di mana JXX merupakan lompatan bersyarat) dan assembler akan mengganti someLabeldengan alamat label tersebut. Namun, jika Anda cukup memodifikasi kode mesin rakitan (opcodes aktual) dengan tangan (seperti yang kadang-kadang terjadi dengan menulis shellcode), Anda juga harus mengubah instruksi lompat secara manual. Anda dapat memodifikasinya, atau memindahkan alamat kode target dengan menggunakan NOPs.

Kasus penggunaan lain untuk NOPinstruksi adalah sesuatu yang disebut kereta luncur NOP . Intinya idenya adalah untuk membuat array instruksi yang cukup besar yang tidak menimbulkan efek samping (sepertiNOPatau menambah dan kemudian mengurangi register) tetapi menambah penunjuk instruksi. Ini berguna misalnya ketika seseorang ingin melompat ke bagian kode tertentu yang alamatnya tidak diketahui. Caranya adalah dengan menempatkan kereta luncur NOP di depan kode target dan kemudian melompat ke suatu tempat ke kereta luncur tersebut. Apa yang terjadi adalah bahwa eksekusi berlanjut dengan harapan dari array yang tidak memiliki efek samping dan ia akan meneruskan instruksi-per-instruksi hingga menyentuh bagian kode yang diinginkan. Teknik ini biasanya digunakan dalam eksploitasi buffer overflow yang disebutkan di atas dan terutama untuk melawan langkah-langkah keamanan seperti ASLR .

Namun penggunaan khusus lain untuk NOPinstruksi adalah ketika seseorang memodifikasi kode dari beberapa program. Misalnya, Anda dapat mengganti bagian dari lompatan bersyarat dengan NOPs dan dengan demikian menghindari kondisi tersebut. Ini adalah metode yang sering digunakan ketika " memecahkan " perlindungan salinan perangkat lunak. Paling sederhana itu hanya tentang menghapus konstruksi kode perakitan untuk if(genuineCopy) ...baris kode dan mengganti instruksi dengan NOPs dan .. Voa! Tidak ada pemeriksaan yang dilakukan dan salinan yang tidak asli berfungsi!

Perhatikan bahwa pada dasarnya kedua contoh shellcode dan cracking melakukan hal yang sama; memodifikasi kode yang ada tanpa memperbarui alamat relatif operasi yang bergantung pada pengalamatan relatif.


2
Ini adalah jawaban yang luar biasa, terima kasih telah meluangkan waktu untuk menjelaskan ini! Saya akhirnya mengerti!
alvonellos

Sistem waktu nyata tertentu (PLC datang ke pikiran) memungkinkan Anda untuk "menambal" logika baru ke dalam program yang sudah ada saat sedang berjalan. Sistem ini meninggalkan NOP sebelum setiap bagian kecil dari logika sehingga Anda dapat menimpa NOP dengan melompat ke logika baru yang Anda masukkan. Pada akhir logika baru, ia akan melompat ke akhir logika asli yang Anda gantikan. Logika baru juga akan memiliki NOP di depan sehingga Anda dapat mengganti logika baru juga.
Scott Whitlock

10

Nop dapat digunakan dalam slot tunda ketika tidak ada instruksi lain yang dapat dipesan ulang untuk ditempatkan di sana.

lw   v0,4(v1)
jr   v0

Dalam MIPS, ini akan menjadi bug karena pada saat jr sedang membaca register v0 register v0 belum dimuat dengan nilai dari instruksi sebelumnya.

Cara untuk memperbaikinya adalah:

lw   v0,4(v1)
nop
jr   v0
nop

Ini mengisi slot dealy setelah kata load dan instruksi register lompat dengan nop sehingga instruksi kata load selesai sebelum perintah register register dijalankan.

Bacaan lebih lanjut - sedikit tentang pengisian slot keterlambatan SPARC . Dari dokumen itu:

Apa yang bisa dimasukkan ke dalam slot penundaan?

  • Beberapa instruksi berguna yang harus dijalankan apakah Anda bercabang atau tidak.
  • Beberapa instruksi yang berguna hanya bekerja saat Anda bercabang (atau saat Anda tidak bercabang), tetapi tidak ada salahnya jika dieksekusi dalam kasus lain.
  • Ketika semuanya gagal, instruksi NOP

Apa yang TIDAK HARUS dimasukkan ke dalam slot penundaan?

  • Apa pun yang menetapkan CC yang bergantung pada keputusan cabang. Instruksi cabang membuat keputusan apakah akan cabang atau tidak segera tetapi sebenarnya tidak melakukan cabang sampai setelah instruksi penundaan. (Hanya cabang yang ditunda, bukan keputusan.)
  • Instruksi cabang lainnya. (Apa yang terjadi jika Anda melakukan ini bahkan tidak ditentukan! Hasilnya tidak dapat diprediksi!)
  • Instruksi "set". Ini benar-benar dua instruksi, bukan satu, dan hanya setengahnya yang ada di slot penundaan. (Assembler akan memperingatkan Anda tentang ini.)

Perhatikan opsi ketiga dalam apa yang dimasukkan ke dalam slot tunda. Bug yang Anda lihat kemungkinan seseorang mengisi salah satu hal yang tidak boleh dimasukkan ke dalam slot penundaan. Menempatkan nop di lokasi itu akan memperbaiki bug.

Catatan: setelah membaca kembali pertanyaan, ini untuk x86, yang tidak memiliki slot tunda (percabangan malah hanya menghentikan pipa). Sehingga tidak akan menjadi penyebab / solusi untuk bug tersebut. Pada sistem RISC, itu bisa menjadi jawabannya.


4
Perhatikan bahwa pertanyaan tersebut ditandai x86, dan x86 tidak memiliki slot tunda. Tidak akan, juga, karena itu adalah perubahan yang melanggar.
MSalters

6

setidaknya satu alasan untuk menggunakan NOP adalah perataan. Prosesor x86 membaca data dari memori utama dalam blok yang cukup besar, dan mulai dari blok untuk membaca selalu selaras, jadi jika seseorang memiliki blok kode, yang akan banyak dibaca, blok ini harus disejajarkan. Ini akan menghasilkan sedikit percepatan.


Ini tidak tepat bahwa blok perlu disejajarkan, itu bahwa Anda tidak ingin harus mengambil beberapa byte terakhir dari blok sebelumnya. Jadi tidak apa-apa untuk melompat 0x1002, karena masih ada 14 byte instruksi di blok 16B yang berisi alamat target, tetapi tidak baik untuk dilompati 0x099D.
Peter Cordes

3

Satu tujuan untuk NOP (dalam perakitan umum, tidak hanya x86) itu untuk memperkenalkan penundaan waktu. Misalnya Anda ingin memprogram mikrokontroler yang harus menghasilkan beberapa LED dengan penundaan 1 detik. Penundaan ini dapat diimplementasikan dengan NOP (dan cabang). Tentu saja Anda dapat menggunakan beberapa ADD atau sesuatu yang lain, tetapi itu akan membuat kode lebih terbaca; atau mungkin Anda membutuhkan semua register.


1
Biasanya untuk jangka waktu yang lama, seperti 1 detik, timer digunakan. NOPS digunakan untuk zaman dalam urutan besarnya jam - nano dan mikro detik.
mattnz

Ini hanya masuk akal pada mikrokontroler, bukan x86 modern. Kebanyakan kode x86 tidak memenuhi lebar pipa CPU superscalar modern out-of-order, jadi menambahkan NOP antara setiap instruksi dalam kebanyakan kode hanya akan berdampak kecil (saya kira angka untuk kode "rata-rata" mungkin adalah 5 sampai 20% untuk menggandakan jumlah instruksi, dengan beberapa kode yang menunjukkan tidak ada perlambatan tapi loop ketat beberapa menunjukkan hampir perlambatan 2x.) Anyway, berkerak kode x86 tua tradisional digunakan dalam loopinstruksi untuk menunda loop , tidak NOP.
Peter Cordes

3

Secara umum pada 80x86, instruksi NOP tidak diperlukan untuk kebenaran program, meskipun kadang-kadang pada beberapa mesin, NOP yang ditempatkan secara strategis dapat menyebabkan kode berjalan lebih cepat. Pada 8086, misalnya, kode akan diambil dalam potongan dua-byte, dan prosesor memiliki buffer "prefetch" internal yang dapat menampung tiga potongan tersebut. Beberapa instruksi akan dieksekusi lebih cepat daripada yang bisa diambil, sementara instruksi lain akan memakan waktu untuk dieksekusi. Selama instruksi lambat, prosesor akan mencoba untuk mengisi buffer prefetch, sehingga jika beberapa instruksi berikutnya cepat mereka dapat dieksekusi dengan cepat. Jika instruksi mengikuti instruksi lambat dimulai pada batas kata genap, instruksi enam byte berikutnya akan di-prefetch; jika dimulai pada batas byte ganjil, hanya lima byte yang akan diambil sebelumnya.

Masalah penyelarasan memori semacam itu dapat memengaruhi kecepatan program, tetapi umumnya tidak memengaruhi kebenaran. Di sisi lain, ada beberapa masalah terkait prefetch pada prosesor lama di mana NOP dapat memengaruhi kebenaran. Jika suatu instruksi mengubah byte kode yang sudah lebih dulu diambil, 8086 (dan saya pikir 80286 dan 80386) akan menjalankan instruksi yang sudah ditentukan meskipun tidak lagi cocok dengan apa yang ada dalam memori. Menambahkan satu atau dua NOP di antara instruksi yang mengubah memori dan byte kode yang diubah dapat mencegah byte kode tidak diambil hingga setelah ditulis. Perhatikan, omong-omong, banyak skema perlindungan salinan mengeksploitasi perilaku semacam ini; Perhatikan juga, bahwa perilaku ini tidak dijamin. Variasi prosesor yang berbeda dapat menangani pengambilan awal yang berbeda, beberapa mungkin membatalkan byte prefetched jika memori dari mana mereka dibaca dimodifikasi, dan interupsi umumnya akan membatalkan buffer prefetch; kode akan diambil ulang ketika interupsi kembali.


3

Ada kasus spesifik x86 yang masih belum dijelaskan dalam jawaban lain: penanganan interupsi. Untuk beberapa gaya, mungkin ada bagian kode ketika interupsi dinonaktifkan karena kode utama berfungsi dengan beberapa data yang dibagi dengan penangan interupsi, tetapi masuk akal untuk memungkinkan interupsi antara bagian tersebut. Jika seseorang secara naif menulis


    STI
    CLI

ini tidak akan memproses interupsi tertunda karena, mengutip Intel:

Setelah flag IF diatur, prosesor mulai merespons interupsi eksternal yang dapat disamarkan setelah instruksi berikutnya dijalankan.

jadi ini harus ditulis ulang setidaknya sebagai:


    STI
    NOP
    CLI

Pada varian kedua, semua interupsi yang tertunda akan diproses hanya antara NOP dan CLI. (Tentu saja, bisa ada banyak varian alternatif, seperti menggandakan instruksi IMS. Tetapi NOP eksplisit lebih jelas, setidaknya untuk saya.)


-2

NOP berarti Tidak Ada Operasi

Biasanya digunakan untuk memasukkan atau menghapus kode mesin atau untuk menunda eksekusi kode tertentu.

Juga digunakan oleh cracker dan debugger untuk mengatur breakpoints.

Jadi mungkin melakukan sesuatu seperti: XCHG BX, BX juga akan menghasilkan hal yang sama.

Bagi saya terdengar seperti ada beberapa operasi yang masih dalam proses dan karenanya menyebabkan kesalahan.

Jika Anda terbiasa dengan VB, saya bisa memberi Anda contoh:

Jika Anda membuat sistem login di vb, dan memuat 3 halaman bersama - facebook, youtube dan twitter di 3 tab berbeda.

Dan gunakan 1 tombol login untuk semua. Mungkin memberikan kesalahan jika koneksi internet Anda lambat. Yang berarti salah satu halaman belum dimuat. Jadi kami memasukkan Application.DoEvents untuk mengatasi ini. Cara yang sama dalam perakitan NOP dapat digunakan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.