Ajută la actualizarea acestei pagini

🌏

Există o nouă versiune a acestei pagini, dar acum este doar în engleză. Ajută-ne să traducem cea mai recentă versiune.

Cum se utilizează Slither pentru a găsi erori în contractele inteligente

Cum se utilizează Slither

Intenția acestui tutorial este de a arăta cum se utilizează Slither pentru a găsi automat erori în contracte inteligente.

Instalare

Slitter necesită Python >= 3.6. Acesta poate fi instalat prin pip sau folosind docker.

Slither prin pip:

pip3 install --user slither-analyzer

Slither prin docker:

docker pull trailofbits/eth-security-toolbox
docker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolbox

Ultima comandă rulează eth-Security-Toolbox într-un docker care are acces la directorul curent. Poți să schimbi fișierele din gazdă și să execuți instrumentele de pe fișiere din docker

În interiorul docker, execută:

solc-select 0.5.11
cd /home/trufflecon/

Rularea unui script

Pentru a rula un script python cu python 3:

python3 script.py

Linie de comandă

Linia de comandă în comparație cu scripturile definite de utilizator. Slither vine cu un set de detectoare predefinite care găsesc multe erori obișnuite. Apelarea Slither din linia de comandă va rula toate detectoarele, fără cunoștințe detaliate de analiză statică necesare:

slither project_paths

Pe lângă detectoare, Slither are capacități de revizuire a codului prin imprimante și instrumente.

Utilizează crytic.io pentru a obține acces la detectoarele private și la integrarea GitHub.

Analiză statică

Capacitățile și designul cadrului de analiză statică Slither au fost descrise în postările de pe blog (1, 2) și academic paper.

Analiza statică există în diferite arome. Cel mai probabil, vei realiza că unele compilatoare precum clang și gcc depind de aceste tehnici de cercetare, dar și sprijină (Infer, CodeClimate, FindBugs și instrumente bazate pe metode formale precum Frama-C și Polyspace.

Nu vom analiza exhaustiv tehnicile de analiză statică și cercetătorii aici. În schimb, ne vom concentra pe ce este nevoie pentru a înțelege modul de funcționare a Slither, încât să îl poți folosi mai eficient pentru a găsi erori și înțelege codul.

Reprezentarea codului

Spre deosebire de o analiză dinamică, care analizează o singură cale de execuție, analiza statică raționează toate căile simultan. Pentru a face acest lucru, se bazează pe o reprezentare de cod diferită. Două din cele mai frecvente sunt arborele de sintaxă abstractă (AST) și graficul fluxului de control (CFG).

Arborele de sintaxă abstractă (AST)

AST este utilizat de fiecare dată când compilatorul analizează codul. Este probabil cea mai de bază structură pe care poate fi efectuată analiza statică.

Pe scurt, un AST este un arbore structurat în care, de obicei, fiecare frunză conține o variabilă sau o constantă și nodurile interne sunt operatori sau operațiuni de control al fluxului. Să considerăm următorul cod:

1function safeAdd(uint a, uint b) pure internal returns(uint){
2 if(a + b <= a){
3 revert();
4 }
5 return a + b;
6}
7
📋 Copiere

AST-ul corespunzător este indicat în:

AST

Slither utilizează AST-ul exportat de solc.

Deși este simplu de construit, AST este o structură imbricată. Uneori, acest lucru nu este cel mai simplu de analizat. De exemplu, pentru a identifica operațiile utilizate de expresia a + b <= a, trebuie să analizezi mai întâi <= și apoi +. O abordare comună este utilizarea așa-numitului model de vizitator, care navighează prin arbore recursiv. Slither conține un vizitator generic în ExpressionVisitor.

Următorul cod utilizează ExpresionVisitor pentru a detecta dacă expresia conține o adăugare:

1from slither.visitors.expression.expression import ExpressionVisitor
2from slither.core.expressions.binary_operation import BinaryOperationType
3
4class HasAddition(ExpressionVisitor):
5
6 def result(self):
7 return self._result
8
9 def _post_binary_operation(self, expression):
10 if expression.type == BinaryOperationType.ADDITION:
11 self._result = True
12
13visitor = HasAddition(expression) # expresia este expresia care trebuie testată
14print(f'Expresia {expression} are o adunare: {visitor.result()}')
15
Afișează tot
📋 Copiere

Graficul fluxului de control (CFG)

Cea de-a doua reprezentare a codurilor este graficul fluxului de control (CFG). După cum sugerează și numele, este o reprezentare pe bază de grafic care expune toate căile de execuție. Fiecare nod conține una sau mai multe instrucțiuni. Marginile din grafic reprezintă operațiunile fluxului de control (dacă/atunci/altfel, buclă etc.). CFG-ul exemplului nostru anterior este:

CFG

CFG este reprezentarea pe baza căreia sunt construite cele mai multe analize.

Există multe alte reprezentări de cod. Fiecare reprezentare are avantaje și dezavantaje în funcție de analiza pe care dorești să o efectuezi.

Analiză

Cel mai simplu tip de analize pe care le poți efectua cu Slither sunt analizele sintactice.

Analiza sintaxei

Slither poate naviga prin diferite componente ale codului și reprezentarea lor pentru a găsi incoerențe și defecte folosind o abordare asemănătoare modelului.

De exemplu, următoarele detectoare caută probleme legate de sintaxă:

Analiza semantică

Spre deosebire de analiza sintaxei, o analiză semantică va merge mai adânc și va analiza „sensul" codului. Această familie include câteva tipuri largi de analize. Ele duc la rezultate mai puternice și utile, dar sunt, de asemenea, mai complex de scris.

Analizele semantice sunt utilizate pentru cele mai avansate detectări de vulnerabilitate.

Analiza dependenței de date

Se spune despre o variabilă cum ar fi variable_a că este data dependentă de variable_b dacă există o modalitate prin care valoarea variable_a să fie influențată de variable_b.

În codul următor, variable_a depinde de variable_b:

1// ...
2variable_a = variable_b + 1;
3
📋 Copiere

Slither vine cu capabilități dependențe de date încorporate, datorită reprezentării sale intermediare (discutate într-o secțiune ulterioară).

Un exemplu de utilizare a dependenței de date poate fi găsit în detectorul de egalitate strictă periculoasă. Aici Slither va căuta o comparație strictă a egalității cu o valoare periculoasă (incorrect_strict_equality.py#L86-L87) și va informa utilizatorul că ar trebui să utilizeze >= sau <= în loc de ==, pentru a împiedica un atacator să blocheze contractul. Printre altele, detectorul va considera ca fiind periculoasă valoarea returnată a unui apel către balanceOf(address) (incorrect_strict_equality.py#L63-L64) și va utiliza motorul dependent de date pentru a urmări utilizarea acestuia.

Calcul de punct fix

Dacă analiza ta navighează prin CFG și urmează marginile, este posibil să vezi noduri deja vizitate. De exemplu, dacă o buclă este prezentată precum cea de mai jos:

1for(uint i; i < range; ++){
2 variable_a += 1
3}
4
📋 Copiere

Analiza ta va trebui să știe când să se oprească. Există două strategii principale aici: (1) iterare pe fiecare nod un număr finit de ori, (2) calcularea unui așa-numit fixpoint. Un fixpoint înseamnă că analiza acestui nod nu oferă nici o informație semnificativa.

Un exemplu de „fixpoint” utilizat poate fi găsit în detectoare de reintrare: Slither explorează nodurile și caută apeluri externe, scrie și citește în stocare. După ce a ajuns la un „fixpoint” (reentrancy.py#L125-L131), acesta oprește explorarea și analizează rezultatele pentru a vedea dacă există o reintrare, prin diferite modele de reintrare (reentrancy_benign.py, reentrancy_read_before_write.py, reentrancy_eth.py).

Scrierea analizelor folosind calculul eficient de punct fix necesită o bună înțelegere a modului în care analiza își propagă informațiile.

Reprezentarea intermediară

O reprezentare intermediară (IR) este un limbaj care se dorește a fi mai adecvat la analiza statică decât cea inițială. Slither traduce Solidity în propria sa IR: SlithIR.

Înțelegerea SlithIR nu este necesară dacă dorești numai să scrii verificări de bază. Cu toate acestea, va fi utilă dacă intenționezi să scrii analize semantice avansate. Imprimantele SlithIR și SSA te vor ajuta să înțelegi modul în care este tradus codul.

Principii API

Slither are un API care îți permite să explorezi atributele de bază ale contractului și funcțiile sale.

Pentru a încărca un cod de bază:

1from slither import Slither
2slither = Slither('/path/to/project')
3
4
📋 Copiere

Explorarea contractelor și a funcțiilor

Un obiect Slitter are:

  • contracts (list(Contract): listă de contracte
  • contracts_derived (list(Contract): lista contractelor care nu sunt moștenite de un alt contract (subset de contracte)
  • get_contract_from_name (str): Returnează un contract din numele său

Un obiect contract are:

  • name (str): Numele contractului
  • functions (list(Function)): Listă de funcții
  • modifiers (list(Modifier)): Listă de funcții
  • all_functions_called (list(Function/Modifier)): Lista tuturor funcțiilor interne accesibile prin contract
  • inheritance (list(Contract)): Lista contractelor moștenite
  • get_function_from_signature (str): Returnează o funcție de la semnătură
  • get_modifier_from_signature (str): Returnează un modificator de la semnătură
  • get_state_variable_from_name (str): Returnează o variabilă de stare din numele său

Un obiect Function sau un obiect Modifier are:

  • name (str): Numele funcției
  • contract (contract): contractul în care este declarată funcția
  • nodes (list(Node)): Lista nodurilor care compun CFG-ul funcției/modificatorului
  • entry_point (Node): Punctul de intrare al CFG
  • variables_read (list(Variable)): Lista variabilelor citite
  • variables_written (list(Variable)): Lista variabilelor scrise
  • state_variables_read (list(StateVariable)): Lista variabilelor de stare citite (subset de variabile`read)
  • state_variables_written (list(StateVariable)): Lista variabilelor de stare scrise (subset de variabile`scris)
Ultima editare: , Invalid DateTime
Edit page