Jak používat Slither k hledání chyb v chytrých kontraktech
Jak používat Slither
Cílem tohoto tutoriálu je ukázat, jak používat Slither k automatickému hledání chyb v chytrých kontraktech.
- Instalace
- Použití příkazového řádku
- Úvod do statické analýzy: Stručný úvod do statické analýzy
- API: Popis Python API
Instalace
Slither vyžaduje Python >= 3.6. Lze jej nainstalovat přes pip nebo pomocí Dockeru.
Slither přes pip:
pip3 install --user slither-analyzer
Slither přes Docker:
docker pull trailofbits/eth-security-toolbox
docker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolbox
Poslední příkaz spustí eth-security-toolbox v Dockeru, který má přístup k vašemu aktuálnímu adresáři. Můžete měnit soubory ze svého hostitelského systému a spouštět nástroje na souborech z Dockeru.
Uvnitř Dockeru spusťte:
solc-select 0.5.11
cd /home/trufflecon/
Spuštění skriptu
Pro spuštění Python skriptu pomocí Pythonu 3:
python3 script.py
Příkazový řádek
Příkazový řádek versus uživatelsky definované skripty. Slither přichází se sadou předdefinovaných detektorů, které nacházejí mnoho běžných chyb. Volání Slitheru z příkazového řádku spustí všechny detektory, není potřeba žádná detailní znalost statické analýzy:
slither project_paths
Kromě detektorů má Slither schopnosti revize kódu prostřednictvím svých nástrojů pro výpis (printers) (opens in a new tab) a nástrojů (opens in a new tab).
Použijte crytic.io (opens in a new tab) pro získání přístupu k soukromým detektorům a integraci s GitHubem.
Statická analýza
Schopnosti a návrh frameworku pro statickou analýzu Slither byly popsány v příspěvcích na blogu (1 (opens in a new tab), 2 (opens in a new tab)) a v akademickém článku (opens in a new tab).
Statická analýza existuje v různých podobách. Pravděpodobně si uvědomujete, že kompilátory jako clang (opens in a new tab) a gcc (opens in a new tab) závisí na těchto výzkumných technikách, ale tvoří také základ nástrojů jako Infer (opens in a new tab), CodeClimate (opens in a new tab), FindBugs (opens in a new tab) a nástrojů založených na formálních metodách, jako jsou Frama-C (opens in a new tab) a Polyspace (opens in a new tab).
Nebudeme zde vyčerpávajícím způsobem zkoumat techniky statické analýzy a výzkum. Místo toho se zaměříme na to, co je potřeba k pochopení toho, jak Slither funguje, abyste jej mohli efektivněji používat k hledání chyb a porozumění kódu.
Reprezentace kódu
Na rozdíl od dynamické analýzy, která uvažuje o jediné cestě provádění, statická analýza uvažuje o všech cestách najednou. K tomu se spoléhá na jinou reprezentaci kódu. Dvě nejběžnější jsou abstraktní syntaktický strom (AST) a graf toku řízení (CFG).
Abstraktní syntaktické stromy (AST)
AST se používají pokaždé, když kompilátor parsuje kód. Je to pravděpodobně nejzákladnější struktura, na které lze provádět statickou analýzu.
Stručně řečeno, AST je strukturovaný strom, kde obvykle každý list obsahuje proměnnou nebo konstantu a vnitřní uzly jsou operandy nebo operace toku řízení. Zvažte následující kód:
function safeAdd(uint a, uint b) pure internal returns(uint){
if(a + b <= a){
revert();
}
return a + b;
}
Odpovídající AST je zobrazen na:
Slither používá AST exportovaný pomocí solc.
Ačkoli je snadné jej sestavit, AST je vnořená struktura. Někdy to není to nejpřímočařejší pro analýzu. Například k identifikaci operací použitých výrazem a + b <= a musíte nejprve analyzovat <= a poté +. Běžným přístupem je použití takzvaného návrhového vzoru návštěvník (visitor pattern), který prochází stromem rekurzivně. Slither obsahuje obecného návštěvníka v ExpressionVisitor (opens in a new tab).
Následující kód používá ExpressionVisitor k detekci, zda výraz obsahuje sčítání:
from slither.visitors.expression.expression import ExpressionVisitor
from slither.core.expressions.binary_operation import BinaryOperationType
class HasAddition(ExpressionVisitor):
def result(self):
return self._result
def _post_binary_operation(self, expression):
if expression.type == BinaryOperationType.ADDITION:
self._result = True
visitor = HasAddition(expression) # expression je výraz, který má být testován
print(f'The expression {expression} has a addition: {visitor.result()}')
Graf toku řízení (CFG)
Druhou nejběžnější reprezentací kódu je graf toku řízení (CFG). Jak název napovídá, jedná se o reprezentaci založenou na grafech, která odhaluje všechny cesty provádění. Každý uzel obsahuje jednu nebo více instrukcí. Hrany v grafu představují operace toku řízení (if/then/else, smyčka atd.). CFG našeho předchozího příkladu je:
CFG je reprezentace, na které je postavena většina analýz.
Existuje mnoho dalších reprezentací kódu. Každá reprezentace má výhody a nevýhody podle analýzy, kterou chcete provést.
Analýza
Nejjednodušším typem analýz, které můžete se Slitherem provádět, jsou syntaktické analýzy.
Syntaktická analýza
Slither dokáže procházet různými komponentami kódu a jejich reprezentací, aby našel nesrovnalosti a nedostatky pomocí přístupu podobného porovnávání vzorů (pattern matching).
Například následující detektory hledají problémy související se syntaxí:
-
Zastínění stavové proměnné (State variable shadowing) (opens in a new tab): iteruje přes všechny stavové proměnné a kontroluje, zda některá nezastiňuje proměnnou ze zděděného kontraktu (state.py#L51-L62 (opens in a new tab))
-
Nesprávné rozhraní ERC-20 (opens in a new tab): hledá nesprávné podpisy funkcí ERC-20 (incorrect_erc20_interface.py#L34-L55 (opens in a new tab))
Sémantická analýza
Na rozdíl od syntaktické analýzy půjde sémantická analýza hlouběji a bude analyzovat „význam“ kódu. Tato rodina zahrnuje některé široké typy analýz. Vedou k silnějším a užitečnějším výsledkům, ale je také složitější je napsat.
Sémantické analýzy se používají pro nejpokročilejší detekce zranitelností.
Analýza datových závislostí
O proměnné variable_a se říká, že je datově závislá na variable_b, pokud existuje cesta, pro kterou je hodnota variable_a ovlivněna variable_b.
V následujícím kódu je variable_a závislá na variable_b:
// ...
variable_a = variable_b + 1;
Slither přichází s vestavěnými schopnostmi pro datové závislosti (opens in a new tab) díky své průběžné reprezentaci (diskutováno v pozdější části).
Příklad použití datové závislosti lze nalézt v detektoru nebezpečné striktní rovnosti (opens in a new tab). Zde bude Slither hledat porovnání striktní rovnosti s nebezpečnou hodnotou (incorrect_strict_equality.py#L86-L87 (opens in a new tab)) a bude informovat uživatele, že by měl použít >= nebo <= spíše než ==, aby zabránil útočníkovi uvěznit kontrakt. Mimo jiné bude detektor považovat za nebezpečnou návratovou hodnotu volání balanceOf(address) (incorrect_strict_equality.py#L63-L64 (opens in a new tab)) a použije engine datových závislostí ke sledování jejího použití.
Výpočet pevného bodu (Fixed-point computation)
Pokud vaše analýza prochází CFG a sleduje hrany, pravděpodobně uvidíte již navštívené uzly. Například pokud je smyčka prezentována tak, jak je znázorněno níže:
for(uint i; i < range; ++){
variable_a += 1
}
Vaše analýza bude muset vědět, kdy se zastavit. Zde existují dvě hlavní strategie: (1) iterovat na každém uzlu konečný početkrát, (2) vypočítat takzvaný pevný bod (fixpoint). Pevný bod v podstatě znamená, že analýza tohoto uzlu neposkytuje žádné smysluplné informace.
Příklad použití pevného bodu lze nalézt v detektorech reentrance: Slither prozkoumává uzly a hledá externí volání, zápis a čtení do úložiště. Jakmile dosáhne pevného bodu (reentrancy.py#L125-L131 (opens in a new tab)), zastaví průzkum a analyzuje výsledky, aby zjistil, zda je přítomna reentrance, prostřednictvím různých vzorů reentrance (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)).
Psaní analýz pomocí efektivního výpočtu pevného bodu vyžaduje dobré pochopení toho, jak analýza šíří své informace.
Průběžná reprezentace
Průběžná reprezentace (IR) je jazyk, který má být pro statickou analýzu vhodnější než ten původní. Slither překládá Solidity do svého vlastního IR: SlithIR (opens in a new tab).
Porozumění SlithIR není nutné, pokud chcete psát pouze základní kontroly. Bude se vám však hodit, pokud plánujete psát pokročilé sémantické analýzy. Nástroje pro výpis (printers) SlithIR (opens in a new tab) a SSA (opens in a new tab) vám pomohou pochopit, jak je kód překládán.
Základy API
Slither má API, které vám umožní prozkoumat základní atributy kontraktu a jeho funkcí.
Pro načtení kódové základny:
from slither import Slither
slither = Slither('/path/to/project')
Zkoumání kontraktů a funkcí
Objekt Slither má:
contracts (list(Contract): seznam kontraktůcontracts_derived (list(Contract): seznam kontraktů, které nejsou zděděny jiným kontraktem (podmnožina kontraktů)get_contract_from_name (str): Vrátí kontrakt podle jeho názvu
Objekt Contract má:
name (str): Název kontraktufunctions (list(Function)): Seznam funkcímodifiers (list(Modifier)): Seznam funkcíall_functions_called (list(Function/Modifier)): Seznam všech interních funkcí dosažitelných kontrakteminheritance (list(Contract)): Seznam zděděných kontraktůget_function_from_signature (str): Vrátí funkci (Function) podle jejího podpisuget_modifier_from_signature (str): Vrátí modifikátor (Modifier) podle jeho podpisuget_state_variable_from_name (str): Vrátí stavovou proměnnou (StateVariable) podle jejího názvu
Objekt Function nebo Modifier má:
name (str): Název funkcecontract (contract): kontrakt, kde je funkce deklarovánanodes (list(Node)): Seznam uzlů tvořících CFG funkce/modifikátoruentry_point (Node): Vstupní bod CFGvariables_read (list(Variable)): Seznam čtených proměnnýchvariables_written (list(Variable)): Seznam zapisovaných proměnnýchstate_variables_read (list(StateVariable)): Seznam čtených stavových proměnných (podmnožina variables`read)state_variables_written (list(StateVariable)): Seznam zapisovaných stavových proměnných (podmnožina variables`written)
Poslední aktualizace stránky: 15. dubna 2026

