Pertanyaan x86 cara untuk memberi tahu instruksi dari data


Apakah ada cara yang lebih atau kurang dapat diandalkan untuk mengetahui apakah data di beberapa lokasi dalam memori adalah awal dari instruksi prosesor atau beberapa data lainnya?

Sebagai contoh, E8 3F BD 6A 00 mungkin call petunjuk (E8) dengan offset relatif 0x6ABD3F, atau mungkin tiga byte data milik beberapa instruksi lainnya, diikuti oleh push 0 (6A 00).

Saya tahu pertanyaannya terdengar konyol dan mungkin tidak ada cara yang sederhana, tetapi mungkin set instruksi dirancang dengan masalah ini dalam pikiran dan mungkin beberapa kode sederhana memeriksa + -100 byte di sekitar lokasi dapat memberikan jawaban yang sangat mungkin benar.

Saya ingin tahu ini karena saya memindai kode program dan mengganti semua panggilan ke beberapa fungsi dengan panggilan ke penggantiku. Ini bekerja sejauh ini tapi itu tidak mustahil bahwa pada titik tertentu, ketika saya meningkatkan jumlah fungsi yang saya ganti, beberapa data akan terlihat persis seperti pemanggilan fungsi ke alamat yang tepat itu, dan akan diganti, dan ini akan menyebabkan sebuah program untuk istirahat dengan cara yang paling tidak terduga. Saya ingin mengurangi kemungkinan itu.


4
2017-11-19 12:07


asal


Jawaban:


Jika kode Anda (atau yang lain yang mempertahankan menghubungkan dan info debug), cara terbaik adalah dengan memindai tabel simbol / relokasi dalam file objek. Jika tidak, tidak ada cara yang dapat diandalkan untuk menentukan apakah beberapa byte adalah pengantar atau data.

Mungkin metode yang paling efisien untuk mengkualifikasi data adalah pembongkaran rekursif. I. e. kode pembongkaran dari titik enty dan dari semua tujuan lompat ditemukan. Tapi ini tidak sepenuhnya dapat diandalkan, karena tidak melintasi tabel lompatan (Anda dapat mencoba menggunakan beberapa heuristik untuk ini, tetapi ini juga tidak sepenuhnya dapat diandalkan).

Solusi untuk masalah Anda adalah fungsi patch diganti sendiri: menimpa permulaannya dengan lompatan lompat ke fungsi Anda.


5
2017-11-19 12:40



Sayangnya, tidak ada cara yang 100% andal untuk membedakan kode dari data. Dari sudut pandang CPU, kode adalah kode hanya ketika beberapa lompat opcode menginduksi prosesor untuk mencoba mengeksekusi byte seolah-olah mereka kode. Anda dapat mencoba membuat analisis aliran kontrol dengan memulai dengan titik masuk program, dan mengikuti semua jalur eksekusi yang mungkin, tetapi ini mungkin gagal dengan adanya fungsi pointer.

Untuk masalah khusus Anda: Saya mengumpulkan bahwa Anda ingin mengganti fungsi yang ada dengan penggantian milik Anda sendiri. Saya menyarankan agar Anda menambal fungsi yang diganti itu sendiri. Yaitu, daripada mencari semua panggilan ke foo() berfungsi dan menggantinya dengan panggilan ke bar(), ganti saja byte pertama foo() dengan melompat ke bar() (Sebuah jmp, tidak a call: Anda tidak ingin mengacaukan tumpukan). Ini kurang memuaskan karena lompat ganda, tetapi bisa diandalkan.


2
2017-11-19 13:11



Tidak mungkin membedakan data dari instruksi secara umum dan ini karena arsitektur von Neumann . Menganalisis kode di sekitar adalah alat bantu dan pembongkaran yang melakukan ini. (Ini semoga bermanfaat. Jika Anda tidak dapat menggunakan IDA Pro / itu komersial /, gunakan alat pembongkaran lain.)


1
2017-11-19 12:29



Kode polos memiliki entropi yang sangat spesifik, sehingga cukup mudah untuk membedakannya dari sebagian besar data. Namun, ini adalah pendekatan probabilistik, tetapi buffer yang cukup besar dari kode biasa dapat dikenali (terutama keluaran kompilator, ketika Anda juga dapat mengenali pola, seperti awal dari suatu fungsi).

Juga, beberapa opcode dicadangkan untuk masa depan, yang lain hanya tersedia dari mode kernel. Dalam hal ini dengan mengetahui mereka dan mengetahui cara menghitung panjang instruksi (Anda dapat mencoba rutin yang ditulis oleh Z0mbie untuk itu), Anda dapat melakukannya.


1
2017-11-19 12:38



Thomas menyarankan ide yang tepat. Untuk menerapkannya dengan benar, Anda perlu membongkar beberapa instruksi pertama (bagian yang akan Anda timpa dengan JMP) dan menghasilkan fungsi trampolin sederhana yang mengeksekusinya kemudian melompat ke sisa fungsi aslinya.

Ada perpustakaan yang melakukan ini untuk Anda. Yang terkenal adalah Jalan memutar tetapi memiliki ketentuan lisensi yang agak canggung. Implementasi yang bagus dari ide yang sama dengan lisensi yang lebih permisif Mhook.


0
2017-11-20 10:05