Bagaimana Anda men-debug format biner?

11

Saya ingin dapat men-debug membangun biner builder. Saat ini saya pada dasarnya mencetak data input ke parser biner, dan kemudian masuk ke dalam kode dan mencetak pemetaan input ke output, kemudian mengambil pemetaan output (bilangan bulat) dan menggunakannya untuk mencari bilangan bulat yang sesuai dalam biner. Cukup kikuk, dan mengharuskan saya memodifikasi kode sumber secara mendalam untuk mendapatkan pemetaan antara input dan output.

Sepertinya Anda bisa melihat biner dalam varian yang berbeda (dalam kasus saya, saya ingin melihatnya dalam potongan 8-bit sebagai angka desimal, karena itu cukup dekat dengan input). Sebenarnya, beberapa angka adalah 16 bit, beberapa 8, beberapa 32, dll. Jadi mungkin akan ada cara untuk melihat biner dengan masing-masing angka yang berbeda ini disorot dalam memori dalam beberapa cara.

Satu-satunya cara saya bisa melihat bahwa menjadi mungkin adalah jika Anda benar-benar membangun visualizer khusus untuk format / tata letak biner yang sebenarnya. Jadi ia tahu di mana dalam urutan angka 32 bit seharusnya, dan di mana angka 8 bit seharusnya, dll. Ini adalah banyak pekerjaan dan agak sulit dalam beberapa situasi. Jadi bertanya-tanya apakah ada cara umum untuk melakukannya.

Saya juga bertanya-tanya apa cara umum men-debug jenis hal ini saat ini, jadi mungkin saya bisa mendapatkan beberapa ide tentang apa yang harus dicoba dari itu.

Lance Pollard
sumber
75
Anda mendapat satu jawaban yang mengatakan "gunakan hexdump secara langsung, dan lakukan ini dan itu tambahan" - dan jawaban itu mendapat banyak upvotes. Dan jawaban kedua, 5 jam kemudian (!), Hanya mengatakan "gunakan hexdump". Maka Anda menerima yang kedua demi yang pertama? Serius?
Doc Brown
4
Meskipun Anda mungkin memiliki alasan yang baik untuk menggunakan format biner, pertimbangkan apakah Anda bisa menggunakan format teks yang sudah ada seperti JSON saja. Keterbacaan terhadap manusia sangat penting, dan mesin serta jaringan biasanya cukup cepat sehingga menggunakan format khusus untuk mengurangi ukuran tidak diperlukan lagi saat ini.
jpmc26
4
@ jpmc26 masih banyak digunakan untuk format biner dan akan selalu ada. Keterbacaan manusia biasanya sekunder untuk kinerja, persyaratan penyimpanan, dan kinerja jaringan. Dan masih ada banyak area di mana kinerja jaringan khususnya buruk dan penyimpanan terbatas. Plus jangan lupa semua sistem harus berinteraksi dengan sistem lama (baik perangkat keras dan perangkat lunak) dan harus mendukung format data mereka.
jwenting
4
@ jwenting Tidak, sebenarnya, waktu pengembang biasanya merupakan bagian paling mahal dari suatu aplikasi. Tentu, itu mungkin tidak terjadi jika Anda bekerja di Google atau Facebook, tetapi sebagian besar aplikasi tidak beroperasi pada skala itu. Dan ketika pengembang Anda menghabiskan waktu untuk hal-hal adalah sumber daya paling mahal, keterbacaan manusia diperhitungkan lebih dari 100 milidetik tambahan untuk program untuk menguraikannya.
jpmc26
3
@ jpmc26 Saya tidak melihat apa pun dalam pertanyaan yang menunjukkan kepada saya bahwa OP adalah yang mendefinisikan format.
JimmyJames

Jawaban:

76

Untuk pemeriksaan ad-hoc, cukup gunakan hexdump standar dan pelajari cara melihatnya.

Jika Anda ingin alat untuk penyelidikan yang tepat, saya biasanya menulis dekoder terpisah dalam sesuatu seperti Python - idealnya ini akan didorong langsung dari dokumen spesifikasi pesan atau IDL, dan seotomatis mungkin (sehingga tidak ada kemungkinan memperkenalkan secara manual bug yang sama di kedua decoder).

Terakhir, jangan lupa Anda harus menulis unit test untuk decoder Anda, menggunakan input kalengan yang diketahui benar.

Tak berguna
sumber
2
"Cukup gunakan hexdump standar dan pelajari cara melihatnya." Ya. Dalam pengalaman saya, beberapa bagian dari apa saja hingga 200 bit dapat dituliskan di papan tulis untuk perbandingan yang dikelompokkan, yang kadang-kadang membantu dengan hal semacam ini untuk memulai.
Mast
1
Saya menemukan decoder terpisah sepadan dengan usaha jika data biner memainkan bagian penting dalam aplikasi (atau sistem, secara umum). Ini terutama benar jika format data variabel: data dalam tata letak tetap dapat dilihat dalam hexdump dengan sedikit latihan tetapi mengenai dinding kepraktisan dengan cepat. Kami memperdebatkan lalu lintas USB dan CAN dengan decoder paket komersial, dan saya telah menulis dekoder PROFIBus (di mana variabel tersebar di byte, benar-benar tidak dapat dibaca dalam hex dump), dan menemukan ketiganya sangat membantu.
Peter - Reinstate Monica
10

Langkah pertama untuk melakukan ini adalah bahwa Anda memerlukan cara untuk menemukan atau mendefinisikan tata bahasa yang menggambarkan struktur data yaitu skema.

Contohnya adalah fitur bahasa COBOL yang secara informal dikenal sebagai copybook. Dalam program COBOL Anda akan menentukan struktur data dalam memori. Struktur ini dipetakan langsung ke cara byte disimpan. Ini umum untuk bahasa pada zaman itu yang bertentangan dengan bahasa kontemporer umum di mana tata letak fisik memori merupakan masalah implementasi yang disarikan dari pengembang.

Pencarian google untuk bahasa skema data biner menghasilkan sejumlah alat. Contohnya adalah Apache DFDL . Mungkin sudah ada UI untuk ini juga.

JimmyJames
sumber
2
Fitur ini tidak diperuntukkan bagi bahasa era 'kuno'. Struktur dan serikat C dan C ++ dapat diselaraskan dengan memori. C # memiliki StructLayoutAttribute, yang saya gunakan untuk mengirimkan data biner.
Kasper van den Berg
1
@KaspervandenBerg Kecuali Anda mengatakan bahwa C dan C ++ menambahkan ini baru-baru ini, saya menganggap itu era yang sama. Intinya adalah bahwa format ini bukan hanya untuk transmisi data, meskipun mereka digunakan untuk itu, mereka memetakan langsung ke bagaimana kode bekerja dengan data dalam memori dan pada disk. Secara umum, itu bukan cara bahasa baru cenderung berfungsi meskipun mereka mungkin memiliki fitur seperti itu.
JimmyJames
@KaspervandenBerg C ++ tidak melakukan sebanyak yang Anda pikirkan. Mungkin saja menggunakan perkakas khusus implementasi untuk meluruskan dan menghilangkan bantalan (dan, diakui, semakin standar menambahkan fitur untuk hal semacam ini) dan pesanan anggota bersifat deterministik (tetapi tidak harus sama dengan yang ada di memori!).
Lightness Races dalam Orbit
6

ASN.1 , Abstract Syntax Notation One, menyediakan cara menentukan format biner.

  • DDT - Kembangkan menggunakan data sampel dan tes unit.
  • Tumpukan teks dapat membantu. Jika dalam XML Anda dapat menutup / memperluas subhierarki.
  • ASN.1 tidak benar-benar dibutuhkan tetapi berdasarkan tata bahasa, spesifikasi file yang lebih deklaratif lebih mudah.
Joop Eggen
sumber
6
Jika parade kerentanan keamanan yang tak berkesudahan di ASN.1 parser adalah indikasi, mengadopsinya tentu akan memberikan latihan yang baik dalam debugging format biner.
Tandai
1
@ Mark banyak array byte kecil (dan dalam berbagai hierarki pohon) sering tidak ditangani dengan benar (aman) di C (misalnya tidak menggunakan pengecualian). Jangan pernah meremehkan tingkat rendah, ketidakamanan yang melekat dari C. ASN.1 di - misalnya - java tidak memaparkan masalah ini. Karena parsing terarah tata bahasa ASN.1 dapat dilakukan dengan aman, bahkan C dapat dilakukan dengan basis kode kecil dan aman. Dan bagian dari kerentanan melekat pada format biner itu sendiri: seseorang dapat mengeksploitasi konstruksi "legal" dari tata bahasa format, yang memiliki semantik jahat.
Joop Eggen
3

Jawaban lain telah menjelaskan melihat hex dump, atau menuliskan struktur objek dalam JSON. Saya pikir menggabungkan keduanya sangat membantu.

Menggunakan alat yang dapat membuat JSON di atas hex dump sangat berguna; Saya menulis alat open source yang mengurai biner .NET yang disebut dotNetBytes , berikut ini adalah contoh contoh DLL .

Contoh dotNetBytes

Carl Walsh
sumber
1

Saya tidak yakin saya mengerti sepenuhnya, tetapi sepertinya Anda memiliki parser untuk format biner ini, dan Anda mengontrol kode untuk itu. Jadi jawaban ini dibangun di atas asumsi itu.

Pengurai dalam beberapa cara akan mengisi struct, kelas, atau struktur data apa pun yang dimiliki bahasa Anda. Jika Anda menerapkan a ToStringuntuk semua yang diurai, maka Anda berakhir dengan metode yang sangat mudah digunakan dan mudah dipelihara menampilkan data biner dalam format yang dapat dibaca manusia.

Anda pada dasarnya akan memiliki:

byte[] arrayOfBytes; // initialized somehow
Object obj = Parser.parse(arrayOfBytes);
Logger.log(obj.ToString());

Dan itu saja, dari sudut pandang menggunakannya. Tentu saja ini mengharuskan Anda mengimplementasikan / menimpa ToStringfungsi untuk Objectkelas / struct / apa pun Anda, dan Anda juga harus melakukannya untuk setiap kelas / struct / nate yang bersarang.

Anda juga dapat menggunakan pernyataan bersyarat untuk mencegah ToStringfungsi dipanggil dalam kode rilis sehingga Anda tidak membuang waktu untuk sesuatu yang tidak akan dicatat di luar mode debug.

Anda ToStringmungkin terlihat seperti ini:

return String.Format("%d,%d,%d,%d", int32var, int16var, int8var, int32var2);

// OR

return String.Format("%s:%d,%s:%d,%s:%d,%s:%d", varName1, int32var, varName2, int16var, varName3, int8var, varName4, int32var2);

Pertanyaan awal Anda membuatnya terdengar seolah-olah Anda agak berusaha melakukan ini, dan Anda merasa metode ini memberatkan, tetapi Anda juga pada beberapa titik telah mengimplementasikan parsing format biner dan membuat variabel untuk menyimpan data itu. Jadi yang harus Anda lakukan adalah mencetak variabel-variabel yang ada pada tingkat abstraksi yang sesuai (kelas / struct variabel berada).

Ini adalah sesuatu yang harus Anda lakukan sekali saja, dan Anda bisa melakukannya sambil membangun parser. Dan itu hanya akan berubah ketika format biner berubah (yang sudah akan meminta perubahan ke parser Anda).

Dalam nada yang sama: beberapa bahasa memiliki fitur yang kuat untuk mengubah kelas menjadi XML atau JSON. C # sangat pandai dalam hal ini. Anda tidak harus menyerahkan format biner Anda, cukup lakukan XML atau JSON dalam pernyataan debug logging dan biarkan kode rilis Anda sendiri.

Saya pribadi merekomendasikan untuk tidak menggunakan hex dump route, karena rawan kesalahan (apakah Anda memulai pada byte yang tepat, apakah Anda yakin ketika Anda membaca dari kiri ke kanan bahwa Anda "melihat" endianness yang benar, dll.) .

Contoh: Katakan ToStringsvariabel meludah Anda a,b,c,d,e,f,g,h. Anda menjalankan program dan melihat adanya bug g, tetapi masalahnya benar-benar dimulai c(tetapi Anda sedang melakukan debug, jadi Anda belum mengetahuinya). Jika Anda tahu nilai input (dan Anda harus) Anda akan langsung melihat di csitulah masalah dimulai.

Dibandingkan dengan hex dump yang hanya memberitahu Anda 338E 8455 0000 FF76 0000 E444 ....; jika bidang Anda memiliki ukuran yang bervariasi, di mana cdimulai dan apa nilainya - editor hex akan memberi tahu Anda, tetapi poin saya adalah ini rawan kesalahan dan memakan waktu. Tidak hanya itu, tetapi Anda tidak dapat dengan mudah / cepat mengotomatiskan pengujian melalui penampil hex. Mencetak string setelah mem-parsing data akan memberi tahu Anda dengan tepat apa yang 'dipikirkan' program Anda, dan akan menjadi satu langkah di sepanjang jalur pengujian otomatis.

Shaz
sumber