Awalnya dikembangkan oleh Michal Zalewski [email protected].
Lihat QuickStartGuide.txt jika Anda tidak punya waktu untuk membaca file ini.
Fuzzing adalah salah satu strategi paling ampuh dan terbukti untuk mengidentifikasi masalah keamanan dalam perangkat lunak dunia nyata; ia bertanggung jawab atas sebagian besar eksekusi kode jarak jauh dan bug eskalasi hak istimewa yang ditemukan hingga saat ini dalam perangkat lunak yang kritis terhadap keamanan.
Sayangnya, fuzzing juga relatif dangkal; mutasi yang buta dan acak membuatnya sangat kecil kemungkinannya untuk mencapai jalur kode tertentu dalam kode yang diuji, sehingga meninggalkan beberapa kerentanan di luar jangkauan teknik ini.
Ada banyak upaya untuk memecahkan masalah ini. Salah satu pendekatan awal – dipelopori oleh Tavis Ormandy – adalah distilasi korpus. Metode ini mengandalkan sinyal cakupan untuk memilih subset benih yang menarik dari kumpulan file kandidat yang sangat besar dan berkualitas tinggi, lalu melakukan fuzzing dengan cara tradisional. Pendekatan ini bekerja dengan sangat baik, namun memerlukan korpus yang tersedia. Selain itu, pengukuran cakupan blok hanya memberikan pemahaman yang sangat sederhana mengenai status program, dan kurang berguna untuk memandu upaya fuzzing dalam jangka panjang.
Penelitian lain yang lebih canggih berfokus pada teknik seperti analisis aliran program ("eksekusi concolic"), eksekusi simbolik, atau analisis statis. Semua metode ini sangat menjanjikan dalam pengaturan eksperimental, namun cenderung mengalami masalah keandalan dan kinerja dalam penggunaan praktis - dan saat ini tidak menawarkan alternatif yang layak untuk teknik fuzzing yang "bodoh".
American Fuzzy Lop adalah fuzzer brute force yang digabungkan dengan algoritma genetika yang dipandu instrumentasi yang sangat sederhana namun kokoh. Ia menggunakan bentuk cakupan tepi yang dimodifikasi untuk dengan mudah mengambil perubahan kecil berskala lokal pada aliran kendali program.
Sedikit menyederhanakan, keseluruhan algoritma dapat diringkas sebagai:
Muat kasus uji awal yang disediakan pengguna ke dalam antrian,
Ambil file masukan berikutnya dari antrian,
Mencoba memangkas test case ke ukuran terkecil yang tidak mengubah perilaku program yang diukur,
Mutasi file berulang kali menggunakan variasi strategi fuzzing tradisional yang seimbang dan telah diteliti dengan baik,
Jika salah satu mutasi yang dihasilkan menghasilkan transisi keadaan baru yang dicatat oleh instrumentasi, tambahkan keluaran yang bermutasi sebagai entri baru dalam antrean.
Lanjut ke 2.
Kasus uji yang ditemukan juga secara berkala dimusnahkan untuk menghilangkan kasus uji yang sudah usang karena temuan baru dengan cakupan lebih tinggi; dan menjalani beberapa langkah minimalisasi upaya berbasis instrumentasi lainnya.
Sebagai hasil sampingan dari proses fuzzing, alat ini menciptakan kumpulan kecil kasus uji yang menarik dan mandiri. Ini sangat berguna untuk menyemai sistem pengujian lain yang membutuhkan banyak tenaga atau sumber daya - misalnya, untuk pengujian stres pada browser, aplikasi perkantoran, rangkaian grafis, atau alat sumber tertutup.
Fuzzer telah diuji secara menyeluruh untuk memberikan kinerja out-of-the-box yang jauh lebih unggul dibandingkan alat blind fuzzing atau alat cakupan saja.
Jika kode sumber tersedia, instrumentasi dapat dimasukkan oleh alat pendamping yang berfungsi sebagai pengganti gcc atau clang dalam proses pembuatan standar apa pun untuk kode pihak ketiga.
Instrumentasi ini memiliki dampak kinerja yang cukup sederhana; dalam hubungannya dengan optimasi lain yang diterapkan oleh afl-fuzz, sebagian besar program dapat di-fuzz secepat atau bahkan lebih cepat dibandingkan dengan alat tradisional.
Cara yang benar untuk mengkompilasi ulang program target dapat bervariasi tergantung pada proses pembangunan secara spesifik, namun pendekatan yang hampir universal adalah:
$ CC=/path/to/afl/afl-gcc ./configure
$ make clean all
Untuk program C++, Anda juga ingin menyetel CXX=/path/to/afl/afl-g++
.
Pembungkus dentang (afl-clang dan afl-clang++) dapat digunakan dengan cara yang sama; pengguna dentang juga dapat memilih untuk memanfaatkan mode instrumentasi berkinerja lebih tinggi, seperti yang dijelaskan dalam llvm_mode/README.llvm.
Saat menguji perpustakaan, Anda perlu menemukan atau menulis program sederhana yang membaca data dari stdin atau dari file dan meneruskannya ke perpustakaan yang diuji. Dalam kasus seperti ini, penting untuk menghubungkan executable ini dengan versi statis dari pustaka yang diinstrumentasi, atau untuk memastikan bahwa file .so yang benar dimuat pada waktu proses (biasanya dengan mengatur LD_LIBRARY_PATH
). Opsi paling sederhana adalah build statis, biasanya dapat dilakukan melalui:
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared
Menyetel AFL_HARDEN=1
saat memanggil 'make' akan menyebabkan CC wrapper secara otomatis mengaktifkan opsi pengerasan kode yang membuatnya lebih mudah untuk mendeteksi bug memori sederhana. Libdislocator, perpustakaan pembantu yang disertakan dengan AFL (lihat libdislocator/README.dislocator) juga dapat membantu mengungkap masalah korupsi heap.
PS. Pengguna ASAN disarankan untuk meninjau file note_for_asan.txt untuk mengetahui peringatan penting.
Ketika kode sumber TIDAK tersedia, fuzzer menawarkan dukungan eksperimental untuk instrumentasi binari kotak hitam yang cepat dan on-the-fly. Hal ini dicapai dengan versi QEMU yang berjalan dalam mode "emulasi ruang pengguna" yang kurang dikenal.
QEMU adalah proyek yang terpisah dari AFL, tetapi Anda dapat dengan mudah membangun fitur tersebut dengan melakukan:
$ cd qemu_mode
$ ./build_qemu_support.sh
Untuk petunjuk dan peringatan tambahan, lihat qemu_mode/README.qemu.
Mode ini kira-kira 2-5x lebih lambat dibandingkan instrumentasi waktu kompilasi, kurang kondusif untuk paralelisasi, dan mungkin memiliki beberapa keanehan lainnya.
Untuk beroperasi dengan benar, fuzzer memerlukan satu atau lebih file awal yang berisi contoh yang baik dari data input yang biasanya diharapkan oleh aplikasi target. Ada dua aturan dasar:
Jaga agar file tetap kecil. Di bawah 1 kB adalah ideal, meski tidak sepenuhnya diperlukan. Untuk diskusi tentang mengapa ukuran penting, lihat perf_tips.txt.
Gunakan beberapa kasus uji hanya jika fungsinya berbeda satu sama lain. Tidak ada gunanya menggunakan lima puluh foto liburan yang berbeda untuk mengaburkan perpustakaan gambar.
Anda dapat menemukan banyak contoh bagus untuk memulai file di subdirektori testcases/ yang disertakan dengan alat ini.
PS. Jika kumpulan data yang besar tersedia untuk penyaringan, Anda mungkin ingin menggunakan utilitas afl-cmin untuk mengidentifikasi subset file yang berbeda secara fungsional yang menjalankan jalur kode berbeda dalam biner target.
Proses fuzzing sendiri dilakukan oleh utilitas afl-fuzz. Program ini memerlukan direktori read-only dengan kasus pengujian awal, tempat terpisah untuk menyimpan temuannya, ditambah jalur ke biner untuk pengujian.
Untuk binari target yang menerima masukan langsung dari stdin, sintaks yang biasa digunakan adalah:
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]
Untuk program yang mengambil input dari sebuah file, gunakan '@@' untuk menandai lokasi di baris perintah target di mana nama file input harus ditempatkan. Fuzzer akan menggantikan ini untuk Anda:
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
Anda juga dapat menggunakan opsi -f agar data yang bermutasi ditulis ke file tertentu. Ini berguna jika program mengharapkan ekstensi file tertentu atau lebih.
Biner yang tidak diinstrumentasi dapat difuzz dalam mode QEMU (tambahkan -Q di baris perintah) atau dalam mode blind-fuzzer tradisional (tentukan -n).
Anda dapat menggunakan -t dan -m untuk mengganti batas waktu default dan batas memori untuk proses yang dijalankan; contoh langka dari target yang mungkin memerlukan pengaturan ini termasuk kompiler dan decoder video.
Tips untuk mengoptimalkan kinerja fuzzing dibahas di perf_tips.txt.
Perhatikan bahwa afl-fuzz dimulai dengan melakukan serangkaian langkah fuzzing deterministik, yang dapat memakan waktu beberapa hari, namun cenderung menghasilkan kasus pengujian yang rapi. Jika Anda ingin hasil yang cepat & kotor segera - mirip dengan zzuf dan fuzzer tradisional lainnya - tambahkan opsi -d ke baris perintah.
Lihat file status_screen.txt untuk informasi tentang cara menafsirkan statistik yang ditampilkan dan memantau kesehatan proses. Pastikan untuk membaca file ini terutama jika ada elemen UI yang disorot dengan warna merah.
Proses fuzzing akan berlanjut hingga Anda menekan Ctrl-C. Minimal, Anda ingin mengizinkan fuzzer menyelesaikan satu siklus antrean, yang mungkin memerlukan waktu beberapa jam hingga satu minggu atau lebih.
Ada tiga subdirektori yang dibuat dalam direktori keluaran dan diperbarui secara real time:
queue/ - uji kasus untuk setiap jalur eksekusi yang berbeda, ditambah semua file awal yang diberikan oleh pengguna. Ini adalah korpus yang disintesis yang disebutkan di bagian 2. Sebelum menggunakan korpus ini untuk tujuan lain, Anda dapat memperkecilnya ke ukuran yang lebih kecil menggunakan alat afl-cmin. Alat ini akan menemukan subkumpulan file lebih kecil yang menawarkan cakupan tepi setara.
crash/ - kasus uji unik yang menyebabkan program yang diuji menerima sinyal fatal (misalnya SIGSEGV, SIGILL, SIGABRT). Entri dikelompokkan berdasarkan sinyal yang diterima.
hang/ - kasus pengujian unik yang menyebabkan waktu program yang diuji habis. Batas waktu default sebelum sesuatu tergolong hang adalah yang lebih besar antara 1 detik dan nilai parameter -t. Nilainya dapat disesuaikan dengan menyetel AFL_HANG_TMOUT, namun hal ini jarang diperlukan.
Crash dan hang dianggap "unik" jika jalur eksekusi terkait melibatkan transisi keadaan apa pun yang tidak terlihat pada kesalahan yang tercatat sebelumnya. Jika satu bug dapat diatasi dengan berbagai cara, akan terjadi peningkatan jumlah di awal proses, namun hal ini akan segera berkurang.
Nama file untuk crash dan hang dikorelasikan dengan entri antrian induk yang tidak bermasalah. Ini akan membantu dalam proses debug.
Jika Anda tidak dapat mereproduksi kerusakan yang ditemukan oleh afl-fuzz, kemungkinan besar penyebabnya adalah Anda tidak menyetel batas memori yang sama seperti yang digunakan oleh alat tersebut. Mencoba:
$ LIMIT_MB=50
$ ( ulimit -Sv $[LIMIT_MB << 10] ; /path/to/tested_binary ... )
Ubah LIMIT_MB agar sesuai dengan parameter -m yang diteruskan ke afl-fuzz. Pada OpenBSD, ubah juga -Sv menjadi -Sd.
Direktori keluaran apa pun yang ada juga dapat digunakan untuk melanjutkan pekerjaan yang dibatalkan; mencoba:
$ ./afl-fuzz -i- -o existing_output_dir [...etc...]
Jika Anda telah menginstal gnuplot, Anda juga dapat membuat beberapa grafik cantik untuk tugas fuzzing aktif apa pun menggunakan afl-plot. Untuk contoh tampilannya, lihat http://lcamtuf.coredump.cx/afl/plot/.
Setiap contoh afl-fuzz membutuhkan sekitar satu inti. Ini berarti bahwa pada sistem multi-core, paralelisasi diperlukan untuk memanfaatkan perangkat keras secara penuh. Untuk tips tentang cara melakukan fuzz pada target umum pada beberapa inti atau beberapa mesin dalam jaringan, lihat parallel_fuzzing.txt.
Mode fuzzing paralel juga menawarkan cara sederhana untuk menghubungkan AFL ke fuzzer lain, ke mesin eksekusi simbolik atau concolic, dan seterusnya; sekali lagi, lihat bagian terakhir parallel_fuzzing.txt untuk mendapatkan tip.
Secara default, mesin mutasi afl-fuzz dioptimalkan untuk format data ringkas - misalnya, gambar, multimedia, data terkompresi, sintaksis ekspresi reguler, atau skrip shell. Ini agak kurang cocok untuk bahasa dengan kata-kata yang bertele-tele dan berlebihan - terutama termasuk HTML, SQL, atau JavaScript.
Untuk menghindari kerumitan dalam membangun alat yang sadar sintaksis, afl-fuzz menyediakan cara untuk menyemai proses fuzzing dengan kamus opsional kata kunci bahasa, header ajaib, atau token khusus lainnya yang terkait dengan tipe data yang ditargetkan -- dan menggunakannya untuk merekonstruksi tata bahasa yang mendasari saat bepergian:
http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html
Untuk menggunakan fitur ini, pertama-tama Anda perlu membuat kamus dalam salah satu dari dua format yang dibahas di kamus/README.dictionaries; lalu arahkan fuzzer ke sana melalui opsi -x di baris perintah.
(Beberapa kamus umum juga sudah disediakan di subdirektori itu.)
Tidak ada cara untuk memberikan deskripsi yang lebih terstruktur tentang sintaksis yang mendasarinya, namun fuzzer kemungkinan besar akan mengetahui beberapa hal ini berdasarkan umpan balik instrumentasi saja. Ini sebenarnya berhasil dalam praktiknya, katakanlah:
http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html
PS. Bahkan ketika tidak ada kamus eksplisit yang diberikan, afl-fuzz akan mencoba mengekstrak token sintaksis yang ada di korpus masukan dengan memperhatikan instrumentasi dengan cermat selama pembalikan byte deterministik. Ini berfungsi untuk beberapa jenis parser dan tata bahasa, tetapi tidak sebaik mode -x.
Jika kamus sangat sulit didapat, pilihan lain adalah membiarkan AFL berjalan untuk sementara waktu, dan kemudian menggunakan perpustakaan pengambilan token yang hadir sebagai utilitas pendamping dengan AFL. Untuk itu, lihat libtokencap/README.tokencap.
Pengelompokan kerusakan berdasarkan cakupan biasanya menghasilkan kumpulan data kecil yang dapat dengan cepat diprioritaskan secara manual atau dengan skrip GDB atau Valgrind yang sangat sederhana. Setiap kerusakan juga dapat ditelusuri ke kasus uji non-kerusakan induknya dalam antrean, sehingga lebih mudah untuk mendiagnosis kesalahan.
Oleh karena itu, penting untuk diketahui bahwa beberapa error fuzzing mungkin sulit dievaluasi dengan cepat untuk mengetahui kemampuan eksploitasinya tanpa banyak proses debug dan analisis kode. Untuk membantu tugas ini, afl-fuzz mendukung mode "eksplorasi tabrakan" yang sangat unik yang diaktifkan dengan flag -C.
Dalam mode ini, fuzzer mengambil satu atau lebih kasus uji yang mengalami error sebagai masukan, dan menggunakan strategi fuzzing yang digerakkan oleh umpan balik untuk dengan cepat menghitung semua jalur kode yang dapat dicapai dalam program sambil mempertahankannya dalam kondisi error.
Mutasi yang tidak mengakibatkan crash ditolak; begitu juga perubahan apa pun yang tidak memengaruhi jalur eksekusi.
Outputnya adalah sekumpulan kecil file yang dapat diperiksa dengan sangat cepat untuk melihat tingkat kendali yang dimiliki penyerang atas alamat yang salah, atau apakah mungkin untuk melewati pembacaan awal di luar batas - dan melihat apa yang ada di bawahnya. .
Oh, satu hal lagi: untuk meminimalkan kasus uji, cobalah afl-tmin. Alat ini dapat dioperasikan dengan cara yang sangat sederhana:
$ ./afl-tmin -i test_case -o minimized_result -- /path/to/program [...]
Alat ini berfungsi baik dengan kasus uji yang mogok maupun yang tidak mogok. Dalam mode mogok, ia akan dengan senang hati menerima biner berinstrumen dan non-instrumentasi. Dalam mode non-crashing, minimer mengandalkan instrumentasi AFL standar untuk membuat file lebih sederhana tanpa mengubah jalur eksekusi.
Minimizer menerima sintaks -m, -t, -f dan @@ dengan cara yang kompatibel dengan afl-fuzz.
Tambahan terbaru lainnya pada AFL adalah alat analisis afl. Dibutuhkan file masukan, mencoba membalik byte secara berurutan, dan mengamati perilaku program yang diuji. Kemudian memberi kode warna pada masukan berdasarkan bagian mana yang tampak penting dan mana yang tidak; Meskipun tidak antipeluru, sering kali ia menawarkan wawasan cepat tentang format file yang kompleks. Informasi lebih lanjut tentang pengoperasiannya dapat ditemukan di dekat bagian akhir technical_details.txt.
Fuzzing juga merupakan teknik yang bagus dan kurang dimanfaatkan untuk menemukan kesalahan desain dan implementasi yang tidak menyebabkan kerusakan. Cukup banyak bug menarik yang ditemukan dengan memodifikasi program target untuk memanggil abort() ketika, katakanlah:
Dua perpustakaan bignum menghasilkan keluaran berbeda ketika diberi masukan yang dihasilkan fuzzer yang sama,
Pustaka gambar menghasilkan keluaran berbeda ketika diminta untuk memecahkan kode gambar masukan yang sama beberapa kali berturut-turut,
Pustaka serialisasi/deserialisasi gagal menghasilkan output yang stabil ketika melakukan serialisasi dan deserialisasi data yang disuplai fuzzer secara berulang,
Pustaka kompresi menghasilkan output yang tidak konsisten dengan file input ketika diminta untuk mengompresi dan kemudian mendekompresi blob tertentu.
Menerapkan pemeriksaan kewarasan ini atau yang serupa biasanya hanya membutuhkan sedikit waktu; jika Anda adalah pengelola paket tertentu, Anda dapat membuat kode ini bersyarat dengan #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
(tanda yang juga dibagikan dengan libfuzzer) atau #ifdef __AFL_COMPILER
(yang ini hanya untuk AFL).
Harap diingat bahwa, mirip dengan banyak tugas komputasi intensif lainnya, fuzzing dapat membebani perangkat keras dan OS Anda. Secara khusus:
CPU Anda akan menjadi panas dan memerlukan pendinginan yang memadai. Dalam kebanyakan kasus, jika pendinginan tidak mencukupi atau berhenti bekerja dengan benar, kecepatan CPU akan dibatasi secara otomatis. Meskipun demikian, terutama ketika melakukan fuzzy pada perangkat keras yang kurang sesuai (laptop, ponsel cerdas, dll), bukan tidak mungkin sesuatu akan meledak.
Program yang ditargetkan mungkin akan memakan gigabyte memori secara tidak menentu atau mengisi ruang disk dengan file sampah. AFL mencoba menerapkan batas memori dasar, namun tidak dapat mencegah setiap kemungkinan kecelakaan. Intinya adalah Anda tidak boleh terlalu fokus pada sistem yang kemungkinan kehilangan datanya bukan merupakan risiko yang dapat diterima.
Fuzzing melibatkan miliaran pembacaan dan penulisan ke sistem file. Pada sistem modern, hal ini biasanya memiliki banyak cache, sehingga menghasilkan I/O "fisik" yang cukup sederhana - namun ada banyak faktor yang dapat mengubah persamaan ini. Merupakan tanggung jawab Anda untuk memantau potensi masalah; dengan I/O yang sangat berat, masa pakai banyak HDD dan SSD mungkin berkurang.
Cara yang baik untuk memantau I/O disk di Linux adalah dengan perintah 'iostat':
$ iostat -d 3 -x -k [...optional disk ID...]
Berikut beberapa peringatan terpenting untuk AFL:
AFL mendeteksi kesalahan dengan memeriksa proses pertama yang mati karena sinyal (SIGSEGV, SIGABRT, dll). Program yang menginstal penangan khusus untuk sinyal-sinyal ini mungkin memerlukan komentar kode yang relevan. Dengan cara yang sama, kesalahan pada proses anak yang dihasilkan oleh target fuzz mungkin tidak terdeteksi kecuali Anda menambahkan beberapa kode secara manual untuk menangkapnya.
Seperti alat brute force lainnya, fuzzer menawarkan cakupan terbatas jika enkripsi, checksum, tanda tangan kriptografi, atau kompresi digunakan untuk menggabungkan seluruh format data aktual yang akan diuji.
Untuk menyiasatinya, Anda dapat mengomentari pemeriksaan yang relevan (lihat eksperimental/libpng_no_checksum/ untuk mendapatkan inspirasi); jika ini tidak memungkinkan, Anda juga dapat menulis postprocessor, seperti yang dijelaskan di eksperimental/post_library/.
Ada beberapa trade-off yang disayangkan dengan ASAN dan biner 64-bit. Ini bukan karena kesalahan spesifik apa pun dari afl-fuzz; lihat note_for_asan.txt untuk tips.
Tidak ada dukungan langsung untuk layanan jaringan fuzzing, daemon latar belakang, atau aplikasi interaktif yang memerlukan interaksi UI agar dapat berfungsi. Anda mungkin perlu membuat perubahan kode sederhana agar berperilaku lebih tradisional. Preeny mungkin juga menawarkan opsi yang relatif sederhana - lihat: https://github.com/zardus/preeny
Beberapa tip berguna untuk memodifikasi layanan berbasis jaringan juga dapat ditemukan di: https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop
AFL tidak mengeluarkan data cakupan yang dapat dibaca manusia. Jika Anda ingin memantau jangkauan, gunakan afl-cov dari Michael Rash: https://github.com/mrash/afl-cov
Kadang-kadang, mesin yang hidup bangkit melawan penciptanya. Jika ini terjadi pada Anda, silakan berkonsultasi http://lcamtuf.coredump.cx/prep/.
Selain itu, lihat INSTALL untuk tips khusus platform.
Banyak perbaikan pada afl-fuzz tidak akan mungkin terjadi tanpa umpan balik, laporan bug, atau perbaikan dari:
Jann Horn Hanno Boeck
Felix Groebert Jakub Wilk
Richard W. M. Jones Alexander Cherepanov
Tom Ritter Hovik Manucharyan
Sebastian Roschke Eberhard Mattes
Padraig Brady Ben Laurie
@dronesec Luca Barbato
Tobias Ospelt Thomas Jarosch
Martin Carpenter Mudge Zatko
Joe Zbiciak Ryan Govostes
Michael Rash William Robinet
Jonathan Gray Filipe Cabecinhas
Nico Weber Jodie Cunningham
Andrew Griffiths Parker Thompson
Jonathan Neuschfer Tyler Nighswander
Ben Nagy Samir Aguiar
Aidan Thornton Aleksandar Nikolich
Sam Hakim Laszlo Szekeres
David A. Wheeler Turo Lamminen
Andreas Stieger Richard Godbee
Louis Dassy teor2345
Alex Moneger Dmitry Vyukov
Keegan McAllister Kostya Serebryany
Richo Healey Martijn Bogaard
rc0r Jonathan Foote
Christian Holler Dominique Pelle
Jacek Wielemborek Leo Barnes
Jeremy Barnes Jeff Trull
Guillaume Endignoux ilovezfs
Daniel Godas-Lopez Franjo Ivancic
Austin Seipp Daniel Komaromy
Daniel Binderman Jonathan Metzman
Vegard Nossum Jan Kneschke
Kurt Roeckx Marcel Bohme
Van-Thuan Pham Abhik Roychoudhury
Joshua J. Drake Toby Hutton
Rene Freingruber Sergey Davidoff
Sami Liedes Craig Young
Andrzej Jackowski Daniel Hodson
Terima kasih!
Pertanyaan? Kekhawatiran? Laporan bug? Silakan gunakan GitHub.
Ada juga milis untuk proyek tersebut; untuk bergabung, kirim email ke [email protected]. Atau, jika Anda lebih suka menelusuri arsip terlebih dahulu, coba: https://groups.google.com/group/afl-users.