Jak používat Slither k hledání chyb ve smart kontraktech
Jak používat Slither
Cílem tohoto tutoriálu je ukázat, jak používat Slither k automatickému hledání chyb ve smart 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 pomocí pip nebo s použitím dockeru.
Slither přes pip:
pip3 install --user slither-analyzerSlither přes docker:
docker pull trailofbits/eth-security-toolboxdocker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolboxPoslední 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 z vašeho hostitele a spouštět nástroje na souborech z dockeru
Uvnitř dockeru spusťte:
solc-select 0.5.11cd /home/trufflecon/Spuštění skriptu
Pro spuštění pythonového skriptu v pythonu 3:
python3 script.pyPříkazový řádek
Příkazový řádek versus uživatelsky definované skripty. Slither je dodáván se sadou předdefinovaných detektorů, které nacházejí mnoho běžných chyb. Zavolání Slitheru z příkazového řádku spustí všechny detektory, není potřeba žádná podrobná znalost statické analýzy:
slither project_pathsKromě detektorů má Slither také možnosti revize kódu prostřednictvím svých výpisůopens in a new tab a nástrojůopens in a new tab.
Použijte crytic.ioopens in a new tab pro získání přístupu k soukromým detektorům a integraci s GitHub.
Statická analýza
Schopnosti a design frameworku pro statickou analýzu Slither byly popsány v blogových příspěvcích (1opens in a new tab, 2opens in a new tab) a v akademickém článkuopens in a new tab.
Statická analýza existuje v různých variantách. S největší pravděpodobností si uvědomujete, že kompilátory jako clangopens in a new tab a gccopens in a new tab závisí na těchto výzkumných technikách, ale jsou také základem pro (Inferopens in a new tab, CodeClimateopens in a new tab, FindBugsopens in a new tab a nástroje založené na formálních metodách, jako jsou Frama-Copens in a new tab a Polyspaceopens in a new tab.
Nebudeme zde vyčerpávajícím způsobem procházet techniky statické analýzy a výzkum. Místo toho se zaměříme na to, co je potřeba k pochopení fungování Slitheru, 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 jedné cestě spuště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 řízení toku (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.
V kostce, AST je strukturovaný strom, kde obvykle každý list obsahuje proměnnou nebo konstantu a vnitřní uzly jsou operandy nebo operace řízení toku. Zvažte následující kód:
1function safeAdd(uint a, uint b) pure internal returns(uint){2 if(a + b <= a){3 revert();4 }5 return a + b;6}Odpovídající AST je zobrazen v:
Slither používá AST exportovaný kompilátorem solc.
I když je AST jednoduché sestavit, jedná se o vnořenou strukturu. Někdy to není nejjednodušší analyzovat. Například pro identifikaci operací použitých ve výrazu a + b <= a musíte nejprve analyzovat <= a pak +. Běžným přístupem je použití takzvaného vzoru návštěvník, který rekurzivně prochází stromem. Slither obsahuje obecného návštěvníka v ExpressionVisitoropens in a new tab.
Následující kód používá ExpressionVisitor k detekci, zda výraz obsahuje sčítání:
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 je výraz, který se má testovat14print(f'Výraz {expression} obsahuje sčítání: {visitor.result()}')Zobrazit všeGraf řízení toku (CFG)
Druhou nejběžnější reprezentací kódu je graf řízení toku (CFG). Jak název napovídá, jedná se o grafovou reprezentaci, která odhaluje všechny cesty spuštění. Každý uzel obsahuje jednu nebo více instrukcí. Hrany v grafu představují operace řízení toku (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 v závislosti na analýze, 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 může procházet různými komponenty kódu a jejich reprezentací, aby našel nekonzistence a nedostatky pomocí přístupu podobného porovnávání vzorů.
Například následující detektory hledají problémy související se syntaxí:
-
Stínování stavové proměnnéopens in a new tab: iteruje přes všechny stavové proměnné a kontroluje, zda některá nestíní proměnnou ze zděděného kontraktu (state.py#L51-L62opens in a new tab)
-
Nesprávné rozhraní ERC20opens in a new tab: hledá nesprávné podpisy funkcí ERC20 (incorrect_erc20_interface.py#L34-L55opens in a new tab)
Sémantická analýza
Na rozdíl od syntaktické analýzy jde sémantická analýza hlouběji a analyzuje „význam“ kódu. Tato rodina zahrnuje několik širokých typů analýz. Vedou k výkonnějším a užitečnějším výsledkům, ale jsou také složitější na psaní.
Sémantické analýzy se používají pro nejpokročilejší detekce zranitelností.
Analýza závislosti dat
O proměnné variable_a se říká, že je datově závislá na variable_b, pokud existuje cesta, na které je hodnota variable_a ovlivněna variable_b.
V následujícím kódu je variable_a závislá na variable_b:
1// ...2variable_a = variable_b + 1;Slither je dodáván s vestavěnými schopnostmi datové závislostiopens in a new tab, díky své mezilehlé reprezentaci (diskutované v pozdější sekci).
Příklad použití datové závislosti lze nalézt v detektoru nebezpečné striktní rovnostiopens in a new tab. Zde Slither bude hledat porovnání striktní rovnosti s nebezpečnou hodnotou (incorrect_strict_equality.py#L86-L87opens in a new tab), a informuje uživatele, že by měl použít >= nebo <= místo ==, 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-L64opens in a new tab) a použije engine datové závislosti ke sledování jejího použití.
Výpočet pevného bodu
Pokud vaše analýza prochází CFG a sleduje hrany, je pravděpodobné, že uvidíte již navštívené uzly. Například, pokud je smyčka představena, jak je uvedeno níže:
1for(uint i; i < range; ++){2 variable_a += 13}Vaše analýza bude muset vědět, kdy se zastavit. Existují zde dvě hlavní strategie: (1) iterovat na každém uzlu konečný počet krát, (2) vypočítat takzvaný pevný bod. Pevný bod v podstatě znamená, že analýza tohoto uzlu již neposkytuje žádné smysluplné informace.
Příklad použití pevného bodu lze nalézt v detektorech reentrancy: Slither prozkoumává uzly a hledá externí volání, zápisy do úložiště a čtení z něj. Jakmile dosáhne pevného bodu (reentrancy.py#L125-L131opens in a new tab), zastaví průzkum a analyzuje výsledky, aby zjistil, zda je přítomna reentrancy, a to prostřednictvím různých vzorců reentrancy (reentrancy_benign.pyopens in a new tab, reentrancy_read_before_write.pyopens in a new tab, reentrancy_eth.pyopens in a new tab).
Psaní analýz využívajících efektivní výpočet pevného bodu vyžaduje dobré porozumění tomu, jak analýza šíří své informace.
Mezilehlá reprezentace
Mezilehlá 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é vlastní IR: SlithIRopens in a new tab.
Porozumění SlithIR není nutné, pokud chcete psát pouze základní kontroly. Bude se však hodit, pokud plánujete psát pokročilé sémantické analýzy. Výpisyopens in a new tab SlithIR a SSAopens in a new tab vám pomohou pochopit, jak je kód přeložen.
Základy API
Slither má API, které vám umožňuje prozkoumat základní atributy kontraktu a jeho funkcí.
Pro načtení kódové báze:
1from slither import Slither2slither = Slither('/path/to/project')3Prozkoumává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 jména
Objekt Contract má:
name (str): Jméno 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 podle jejího podpisuget_modifier_from_signature (str): Vrátí modifikátor podle jeho podpisuget_state_variable_from_name (str): Vrátí stavovou proměnnou podle jejího jména
Objekt Function nebo Modifier má:
name (str): Jméno 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 přečtených proměnnýchvariables_written (list(Variable)): Seznam zapsaných proměnnýchstate_variables_read (list(StateVariable)): Seznam přečtených stavových proměnných (podmnožina proměnnýchread)state_variables_written (list(StateVariable)): Seznam zapsaných stavových proměnných (podmnožina proměnnýchwritten)
Stránka naposledy aktualizována: 3. února 2025

