Pada 2007, saya menulis
beberapa alat modding untuk simulator ruang
Freelancer . Sumber daya game disimpan dalam format "binary INI" atau "BINI". Mungkin, format biner dipilih demi kinerja: file seperti itu lebih cepat untuk memuat dan membaca daripada teks sewenang-wenang dalam format INI.
Sebagian besar konten game dapat diedit langsung dari file-file ini, mengubah nama, harga produk, statistik pesawat ruang angkasa, atau bahkan menambahkan kapal baru. File biner sulit untuk dimodifikasi secara langsung, jadi pendekatan alami adalah mengonversinya menjadi teks INI, membuat perubahan dalam editor teks, kemudian mengonversi kembali ke format BINI dan mengganti file dalam direktori game.
Saya tidak menganalisis format BINI, dan saya bukan orang pertama yang belajar mengeditnya. Tetapi saya tidak menyukai alat-alat yang ada, dan saya memiliki visi saya sendiri tentang bagaimana mereka harus bekerja. Saya lebih suka antarmuka bergaya Unix, meskipun gim itu sendiri berjalan di Windows.
Pada waktu itu, saya baru saja berkenalan dengan alat
yacc (sebenarnya
Bison ) dan
lex (sebenarnya
flex ), serta Autoconf, jadi saya menggunakannya dengan tepat. Sangat menarik untuk mencoba utilitas ini dalam praktiknya, walaupun saya dengan kasar meniru proyek open source lainnya, tidak memahami mengapa semuanya dilakukan dengan cara ini, tidak dengan cara lain. Karena penggunaan yacc / lex dan pembuatan skrip konfigurasi, diperlukan sistem mirip Unix. Ini semua terlihat dalam
versi asli program .
Proyek ini ternyata cukup sukses: Saya sendiri berhasil menggunakan alat-alat ini, dan mereka muncul di koleksi berbeda untuk modding Freelancer.
Refactoring
Pada pertengahan 2018, saya kembali ke proyek ini. Pernahkah Anda melihat kode lama Anda dengan pemikiran: apa yang Anda pikirkan? Format INI saya ternyata jauh lebih kaku dan ketat dari yang diperlukan, biner direkam dengan cara yang meragukan, dan perakitan bahkan tidak bekerja secara normal.
Berkat sepuluh tahun pengalaman ekstra, saya tahu pasti bahwa saya akan menulis alat ini jauh lebih baik sekarang. Dan saya melakukannya dalam beberapa hari, menulis ulang dari awal. Kode baru ini sekarang berada di utas utama di Github.
Saya suka membuat semuanya sesederhana mungkin , jadi saya menyingkirkan autoconf yang mendukung
Makefile yang lebih sederhana dan lebih portabel . Tidak ada lagi yacc atau lex, tetapi parser ditulis dengan tangan. Hanya yang sesuai, portable C yang digunakan. Hasilnya sangat sederhana sehingga saya merakit proyek dengan satu perintah pendek
dari Visual Studio , sehingga Makefile tidak benar-benar diperlukan. Jika Anda mengganti
stdint.h
dengan
typedef
, Anda bahkan dapat
membangun dan menjalankan binitools di bawah DOS .
Versi baru ini lebih cepat, lebih kompak, lebih bersih dan lebih mudah. Ini jauh lebih fleksibel sehubungan dengan input INI, sehingga lebih mudah digunakan. Tetapi apakah itu benar?
Fuzzing
Saya telah tertarik pada
fuzzing selama bertahun-tahun, terutama
afl (amerika fuzzy lop). Tapi dia tidak pernah menguasainya, meskipun dia menguji beberapa alat yang saya gunakan secara teratur. Tetapi fuzzing tidak menemukan sesuatu yang luar biasa, setidaknya sebelum saya menyerah. Saya menguji perpustakaan JSON saya dan untuk beberapa alasan juga tidak menemukan apa pun. Jelas bahwa parser JSON saya tidak dapat diandalkan, bukan? Tetapi fuzzing tidak menunjukkan apa-apa. (Ternyata, perpustakaan JSON saya cukup dapat diandalkan, sebagian besar berkat upaya masyarakat!)
Tapi sekarang saya memiliki parser INI yang relatif baru. Meskipun berhasil menganalisis dan merakit dengan benar set asli file BINI dalam game, fungsinya belum
benar -
benar diuji. Tentunya di sini fuzzing akan menemukan sesuatu. Selain itu, Anda tidak perlu menulis satu baris untuk menjalankan afl pada kode ini. Alat default berfungsi dengan input standar, yang ideal.
Dengan asumsi Anda memiliki alat yang diperlukan diinstal (make, gcc, afl), di sini adalah bagaimana fuzzing binitools dimulai dengan mudah:
$ make CC=afl-gcc $ mkdir in out $ echo '[x]' > in/empty $ afl-fuzz -i in -o out -- ./bini
Utilitas
bini
menerima INI pada input dan mengeluarkan BINI, jadi jauh lebih menarik untuk memeriksanya daripada prosedur
unbini
terbalik. Karena
unbini
menganalisis data biner yang relatif sederhana, fuzzer (mungkin) tidak perlu dicari. Namun, untuk berjaga-jaga, saya tetap memeriksanya.

Dalam contoh ini, saya mengubah kompiler default ke shell GCC untuk afl (
CC=afl-gcc
). Di sini afl memanggil GCC di latar belakang, tetapi menambahkan toolkit sendiri ke biner. Saat fuzzing,
afl-fuzz
menggunakan toolkit ini untuk memantau jalur eksekusi suatu program.
Dokumentasi afl menjelaskan rincian teknis.
Saya juga membuat direktori input dan output dengan menempatkan dalam direktori input contoh kerja minimal yang memberikan titik awal. Ketika mulai, itu memutasi antrian data input dan mengawasi perubahan selama eksekusi program. Direktori output berisi hasil dan, yang lebih penting, badan input data yang menyebabkan jalur eksekusi yang unik. Dengan kata lain, banyak input diproses pada keluaran fuzzer, memeriksa banyak skenario perbatasan yang berbeda.
Hasil paling menarik dan menakutkan adalah crash program yang lengkap. Ketika saya pertama kali memulai fuzzer untuk binitools,
bini
menunjukkan
banyak crash seperti itu. Dalam beberapa menit, afl menemukan sejumlah kesalahan halus dan menarik dalam program saya, yang sangat berguna. Fazzer bahkan menemukan
bug yang tidak biasa
dari pointer usang , memeriksa urutan berbeda alokasi memori yang berbeda. Bug khusus ini adalah titik balik yang membuat saya menyadari nilai fuzzing.
Tidak semua kesalahan ditemukan menyebabkan kegagalan. Saya juga mempelajari output dan melihat input mana yang memberikan hasil yang sukses dan mana yang tidak, dan menyaksikan bagaimana program menangani berbagai kasus ekstrem. Dia menolak beberapa masukan yang saya pikir akan dia proses. Dan sebaliknya, dia memproses beberapa data yang saya anggap tidak benar, dan menafsirkan beberapa data dengan cara yang tidak terduga bagi saya. Jadi, bahkan setelah memperbaiki bug dengan crash program, saya masih mengubah pengaturan parser untuk memperbaiki masing-masing kasus yang tidak menyenangkan ini.
Buat suite uji
Segera setelah saya memperbaiki semua kesalahan yang terdeteksi oleh fuzzer dan menyesuaikan parser di semua situasi perbatasan, saya membuat serangkaian tes dari paket data fuzzer - meskipun tidak secara langsung.
Pertama, saya menjalankan fuzzer secara paralel - proses ini dijelaskan dalam dokumentasi afl - jadi saya mendapat banyak input berlebihan. Dengan redundansi, maksud saya bahwa input berbeda tetapi memiliki jalur eksekusi yang sama. Untungnya, afl memiliki alat untuk mengatasi hal ini:
afl-cmin
, alat untuk meminimalkan shell. Ini menghilangkan input yang tidak perlu.
Kedua, banyak dari input ini lebih lama dari yang dibutuhkan untuk menjalankan jalur eksekusi unik mereka.
afl-tmin
,
afl-tmin
test case yang mengurangi test case, membantu di
afl-tmin
.
Saya memisahkan input yang valid dan tidak valid - dan memeriksanya di repositori. Lihatlah semua pintu masuk bodoh yang
ditemukan oleh fuzzer ini berdasarkan pada input minimal:
Bahkan, di sini pengurai dibekukan dalam satu keadaan, dan serangkaian tes memastikan bahwa bangunan tertentu berperilaku dengan cara yang
sangat spesifik. Ini sangat berguna untuk memastikan bahwa rakitan yang dibuat oleh kompiler lain pada platform lain berperilaku sama sehubungan dengan output mereka. Suite tes saya bahkan mendeteksi kesalahan di perpustakaan dietlibc karena binitools tidak lulus tes setelah menautkannya. Jika Anda harus membuat perubahan non-sepele untuk parser, maka pada dasarnya Anda harus meninggalkan set tes saat ini dan mulai dari awal lagi sehingga afl akan menghasilkan seluruh tubuh baru untuk parser baru.
Tentu saja, fuzzing telah memantapkan dirinya sebagai teknik yang kuat. Dia menemukan sejumlah kesalahan yang tidak mungkin saya temukan sendiri. Sejak itu, saya mulai menggunakannya secara lebih kompeten untuk menguji program lain - bukan hanya milik saya - dan menemukan banyak bug baru. Sekarang fuzzer telah mengambil tempat permanen di antara alat-alat dalam kit pengembangan saya.