Cara menggunakan Slither untuk menemukan bug kontrak pintar
Cara menggunakan Slither
Tujuan dari tutorial ini adalah untuk menunjukkan cara menggunakan Slither untuk secara otomatis menemukan bug dalam kontrak pintar.
- Instalasi
- Penggunaan baris perintah
- Pengantar analisis statis: Pengantar singkat tentang analisis statis
- API: Deskripsi API Python
Instalasi
Slither membutuhkan Python >= 3.6. Ini dapat diinstal melalui pip atau menggunakan docker.
Slither melalui pip:
pip3 install --user slither-analyzerSlither melalui docker:
docker pull trailofbits/eth-security-toolboxdocker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolboxPerintah terakhir menjalankan eth-security-toolbox dalam docker yang memiliki akses ke direktori Anda saat ini. Anda dapat mengubah file dari host Anda, dan menjalankan alat pada file dari docker
Di dalam docker, jalankan:
solc-select 0.5.11cd /home/trufflecon/Menjalankan skrip
Untuk menjalankan skrip python dengan python 3:
python3 script.pyBaris perintah
Baris perintah versus skrip yang ditentukan pengguna. Slither dilengkapi dengan serangkaian detektor yang telah ditentukan sebelumnya yang menemukan banyak bug umum. Memanggil Slither dari baris perintah akan menjalankan semua detektor, tidak diperlukan pengetahuan terperinci tentang analisis statis:
slither project_pathsSelain detektor, Slither memiliki kemampuan tinjauan kode melalui printer (opens in a new tab) dan alat (opens in a new tab) miliknya.
Gunakan crytic.io (opens in a new tab) untuk mendapatkan akses ke detektor privat dan integrasi GitHub.
Analisis statis
Kemampuan dan desain kerangka kerja analisis statis Slither telah dijelaskan dalam postingan blog (1 (opens in a new tab), 2 (opens in a new tab)) dan sebuah makalah akademis (opens in a new tab).
Analisis statis ada dalam berbagai bentuk. Anda kemungkinan besar menyadari bahwa kompiler seperti clang (opens in a new tab) dan gcc (opens in a new tab) bergantung pada teknik penelitian ini, tetapi ini juga mendasari (Infer (opens in a new tab), CodeClimate (opens in a new tab), FindBugs (opens in a new tab) dan alat-alat yang didasarkan pada metode formal seperti Frama-C (opens in a new tab) dan Polyspace (opens in a new tab).
Kita tidak akan mengulas teknik analisis statis dan penelitinya secara mendalam di sini. Sebaliknya, kita akan berfokus pada apa yang diperlukan untuk memahami cara kerja Slither sehingga Anda dapat menggunakannya dengan lebih efektif untuk menemukan bug dan memahami kode.
Representasi kode
Berbeda dengan analisis dinamis, yang menalar tentang satu jalur eksekusi, analisis statis menalar tentang semua jalur sekaligus. Untuk melakukannya, ini bergantung pada representasi kode yang berbeda. Dua yang paling umum adalah pohon sintaksis abstrak (AST) dan grafik aliran kontrol (CFG).
Pohon Sintaksis Abstrak (AST)
AST digunakan setiap kali kompiler mengurai kode. Ini mungkin merupakan struktur paling dasar di mana analisis statis dapat dilakukan.
Singkatnya, AST adalah pohon terstruktur di mana, biasanya, setiap daun berisi variabel atau konstanta dan node internal adalah operan atau operasi aliran kontrol. Pertimbangkan kode berikut:
1function safeAdd(uint a, uint b) pure internal returns(uint){2 if(a + b <= a){3 revert();4 }5 return a + b;6}AST yang sesuai ditunjukkan pada:
Slither menggunakan AST yang diekspor oleh solc.
Meskipun mudah dibangun, AST adalah struktur bersarang. Terkadang, ini bukan yang paling mudah untuk dianalisis. Misalnya, untuk mengidentifikasi operasi yang digunakan oleh ekspresi a + b <= a, Anda harus terlebih dahulu menganalisis <= dan kemudian +. Pendekatan umum adalah menggunakan apa yang disebut pola pengunjung (visitor pattern), yang menavigasi melalui pohon secara rekursif. Slither berisi pengunjung generik di ExpressionVisitor (opens in a new tab).
Kode berikut menggunakan ExpressionVisitor untuk mendeteksi apakah ekspresi tersebut berisi penambahan:
1from slither.visitors.expression.expression import ExpressionVisitor2from slither.core.expressions.binary_operation import BinaryOperationType34class HasAddition(ExpressionVisitor):56 def result(self):7 return self._result89 def _post_binary_operation(self, expression):10 if expression.type == BinaryOperationType.ADDITION:11 self._result = True1213visitor = HasAddition(expression) # expression is the expression to be tested # expression adalah ekspresi yang akan diuji14print(f'The expression {expression} has a addition: {visitor.result()}')Tampilkan semuaGrafik Aliran Kontrol (CFG)
Representasi kode paling umum kedua adalah grafik aliran kontrol (CFG). Seperti namanya, ini adalah representasi berbasis grafik yang mengekspos semua jalur eksekusi. Setiap node berisi satu atau beberapa instruksi. Tepi (edges) dalam grafik mewakili operasi aliran kontrol (if/then/else, loop, dll). CFG dari contoh kita sebelumnya adalah:
CFG adalah representasi di atas mana sebagian besar analisis dibangun.
Banyak representasi kode lainnya yang ada. Setiap representasi memiliki kelebihan dan kekurangan sesuai dengan analisis yang ingin Anda lakukan.
Analisis
Jenis analisis paling sederhana yang dapat Anda lakukan dengan Slither adalah analisis sintaksis.
Analisis sintaksis
Slither dapat menavigasi melalui berbagai komponen kode dan representasinya untuk menemukan ketidakkonsistenan dan kelemahan menggunakan pendekatan yang mirip dengan pencocokan pola.
Misalnya, detektor berikut mencari masalah terkait sintaksis:
-
State variable shadowing (opens in a new tab): mengulangi semua variabel status dan memeriksa apakah ada yang membayangi variabel dari kontrak yang diwariskan (state.py#L51-L62 (opens in a new tab))
-
Incorrect ERC20 interface (opens in a new tab): mencari tanda tangan fungsi ERC20 yang salah (incorrect_erc20_interface.py#L34-L55 (opens in a new tab))
Analisis semantik
Berbeda dengan analisis sintaksis, analisis semantik akan masuk lebih dalam dan menganalisis "makna" dari kode tersebut. Keluarga ini mencakup beberapa jenis analisis yang luas. Mereka mengarah pada hasil yang lebih kuat dan berguna, tetapi juga lebih kompleks untuk ditulis.
Analisis semantik digunakan untuk deteksi kerentanan yang paling canggih.
Analisis ketergantungan data
Sebuah variabel variable_a dikatakan bergantung pada data variable_b jika ada jalur di mana nilai variable_a dipengaruhi oleh variable_b.
Dalam kode berikut, variable_a bergantung pada variable_b:
1// ... // ...2variable_a = variable_b + 1;Slither dilengkapi dengan kemampuan ketergantungan data (opens in a new tab) bawaan, berkat representasi perantaranya (dibahas di bagian selanjutnya).
Contoh penggunaan ketergantungan data dapat ditemukan di detektor kesetaraan ketat yang berbahaya (opens in a new tab). Di sini Slither akan mencari perbandingan kesetaraan yang ketat dengan nilai yang berbahaya (incorrect_strict_equality.py#L86-L87 (opens in a new tab)), dan akan memberi tahu pengguna bahwa mereka harus menggunakan >= atau <= daripada ==, untuk mencegah penyerang menjebak kontrak. Antara lain, detektor akan menganggap berbahaya nilai kembalian dari panggilan ke balanceOf(address) (incorrect_strict_equality.py#L63-L64 (opens in a new tab)), dan akan menggunakan mesin ketergantungan data untuk melacak penggunaannya.
Komputasi titik tetap (Fixed-point computation)
Jika analisis Anda menavigasi melalui CFG dan mengikuti tepinya, Anda kemungkinan akan melihat node yang sudah dikunjungi. Misalnya, jika sebuah loop disajikan seperti yang ditunjukkan di bawah ini:
1for(uint i; i < range; ++){2 variable_a += 13}Analisis Anda perlu mengetahui kapan harus berhenti. Ada dua strategi utama di sini: (1) mengulangi pada setiap node dalam jumlah yang terbatas, (2) menghitung apa yang disebut fixpoint. Fixpoint pada dasarnya berarti bahwa menganalisis node ini tidak memberikan informasi yang berarti.
Contoh penggunaan fixpoint dapat ditemukan di detektor reentrancy: Slither mengeksplorasi node, dan mencari panggilan eksternal, menulis dan membaca ke penyimpanan. Setelah mencapai fixpoint (reentrancy.py#L125-L131 (opens in a new tab)), ia menghentikan eksplorasi, dan menganalisis hasil untuk melihat apakah ada reentrancy, melalui pola reentrancy yang berbeda (reentrancy_benign.py (opens in a new tab), reentrancy_read_before_write.py (opens in a new tab), reentrancy_eth.py (opens in a new tab)).
Menulis analisis menggunakan komputasi titik tetap yang efisien membutuhkan pemahaman yang baik tentang bagaimana analisis menyebarkan informasinya.
Representasi perantara
Representasi perantara (IR) adalah bahasa yang dimaksudkan agar lebih mudah menerima analisis statis daripada bahasa aslinya. Slither menerjemahkan Solidity ke IR-nya sendiri: SlithIR (opens in a new tab).
Memahami SlithIR tidak diperlukan jika Anda hanya ingin menulis pemeriksaan dasar. Namun, ini akan berguna jika Anda berencana untuk menulis analisis semantik tingkat lanjut. Printer SlithIR (opens in a new tab) dan SSA (opens in a new tab) akan membantu Anda memahami bagaimana kode diterjemahkan.
Dasar-dasar API
Slither memiliki API yang memungkinkan Anda menjelajahi atribut dasar kontrak dan fungsinya.
Untuk memuat basis kode:
1from slither import Slither2slither = Slither('/path/to/project')3Menjelajahi kontrak dan fungsi
Objek Slither memiliki:
contracts (list(Contract): daftar kontrakcontracts_derived (list(Contract): daftar kontrak yang tidak diwarisi oleh kontrak lain (subset dari kontrak)get_contract_from_name (str): Mengembalikan kontrak dari namanya
Objek Contract memiliki:
name (str): Nama kontrakfunctions (list(Function)): Daftar fungsimodifiers (list(Modifier)): Daftar fungsiall_functions_called (list(Function/Modifier)): Daftar semua fungsi internal yang dapat dijangkau oleh kontrakinheritance (list(Contract)): Daftar kontrak yang diwariskanget_function_from_signature (str): Mengembalikan Fungsi dari tanda tangannyaget_modifier_from_signature (str): Mengembalikan Modifier dari tanda tangannyaget_state_variable_from_name (str): Mengembalikan StateVariable dari namanya
Objek Function atau Modifier memiliki:
name (str): Nama fungsicontract (contract): kontrak tempat fungsi dideklarasikannodes (list(Node)): Daftar node yang menyusun CFG dari fungsi/modifierentry_point (Node): Titik masuk CFGvariables_read (list(Variable)): Daftar variabel yang dibacavariables_written (list(Variable)): Daftar variabel yang ditulisstate_variables_read (list(StateVariable)): Daftar variabel status yang dibaca (subset dari variables`read)state_variables_written (list(StateVariable)): Daftar variabel status yang ditulis (subset dari variables`written)
Pembaruan terakhir halaman: 3 Maret 2026

