اسمارٹ کانٹریکٹ کے بگز تلاش کرنے کے لیے Slither کا استعمال کیسے کریں
Slither کا استعمال کیسے کریں
اس ٹیوٹوریل کا مقصد یہ دکھانا ہے کہ اسمارٹ کانٹریکٹس میں خودکار طریقے سے بگز تلاش کرنے کے لیے Slither کا استعمال کیسے کیا جائے۔
- انسٹالیشن
- کمانڈ لائن کا استعمال
- سٹیٹک اینالیسس کا تعارف: سٹیٹک اینالیسس کا مختصر تعارف
- API: Python API کی تفصیل
انسٹالیشن
Slither کے لیے Python >= 3.6 درکار ہے۔ اسے pip کے ذریعے یا docker کا استعمال کرتے ہوئے انسٹال کیا جا سکتا ہے۔
pip کے ذریعے Slither:
pip3 install --user slither-analyzerdocker کے ذریعے Slither:
docker pull trailofbits/eth-security-toolboxdocker run -it -v "$PWD":/share trailofbits/eth-security-toolboxآخری کمانڈ ایک docker میں eth-security-toolbox چلاتی ہے جسے آپ کی موجودہ ڈائرکٹری تک رسائی حاصل ہوتی ہے۔ آپ اپنے ہوسٹ سے فائلیں تبدیل کر سکتے ہیں، اور docker سے فائلوں پر ٹولز چلا سکتے ہیں
docker کے اندر، چلائیں:
cd /shareاسکرپٹ چلانا
python 3 کے ساتھ python اسکرپٹ چلانے کے لیے:
python3 script.pyکمانڈ لائن
کمانڈ لائن بمقابلہ صارف کے متعین کردہ اسکرپٹس۔ Slither پہلے سے طے شدہ ڈیٹیکٹرز کے ایک سیٹ کے ساتھ آتا ہے جو بہت سے عام بگز تلاش کرتے ہیں۔ کمانڈ لائن سے Slither کو کال کرنے سے تمام ڈیٹیکٹرز چلیں گے، اس کے لیے سٹیٹک اینالیسس کے تفصیلی علم کی ضرورت نہیں ہے:
slither project_pathsڈیٹیکٹرز کے علاوہ، Slither میں اپنے printers (opens in a new tab) اور tools (opens in a new tab) کے ذریعے کوڈ کا جائزہ لینے کی صلاحیتیں موجود ہیں۔
نجی ڈیٹیکٹرز اور GitHub انضمام تک رسائی حاصل کرنے کے لیے crytic.io (opens in a new tab) کا استعمال کریں۔
سٹیٹک اینالیسس
Slither سٹیٹک اینالیسس فریم ورک کی صلاحیتوں اور ڈیزائن کو بلاگ پوسٹس (1 (opens in a new tab), 2 (opens in a new tab)) اور ایک اکیڈمک پیپر (opens in a new tab) میں بیان کیا گیا ہے۔
سٹیٹک اینالیسس مختلف اقسام میں موجود ہے۔ آپ کو غالباً اندازہ ہوگا کہ clang (opens in a new tab) اور gcc (opens in a new tab) جیسے کمپائلرز ان تحقیقی تکنیکوں پر انحصار کرتے ہیں، لیکن یہ (Infer (opens in a new tab)، CodeClimate (opens in a new tab)، FindBugs (opens in a new tab) اور رسمی طریقوں پر مبنی ٹولز جیسے Frama-C (opens in a new tab) اور Polyspace (opens in a new tab) کی بھی بنیاد ہے۔
ہم یہاں سٹیٹک اینالیسس کی تکنیکوں اور محققین کا تفصیلی جائزہ نہیں لیں گے۔ اس کے بجائے، ہم اس بات پر توجہ مرکوز کریں گے کہ Slither کے کام کرنے کے طریقے کو سمجھنے کے لیے کیا ضروری ہے تاکہ آپ اسے بگز تلاش کرنے اور کوڈ کو سمجھنے کے لیے زیادہ مؤثر طریقے سے استعمال کر سکیں۔
کوڈ کی نمائندگی
ڈائنامک اینالیسس کے برعکس، جو ایک ہی ایگزیکیوشن پاتھ کے بارے میں استدلال کرتا ہے، سٹیٹک اینالیسس ایک ہی وقت میں تمام پاتھس کے بارے میں استدلال کرتا ہے۔ ایسا کرنے کے لیے، یہ ایک مختلف کوڈ کی نمائندگی پر انحصار کرتا ہے۔ دو سب سے عام نمائندگیاں ایبسٹریکٹ سنٹیکس ٹری (AST) اور کنٹرول فلو گراف (CFG) ہیں۔
ایبسٹریکٹ سنٹیکس ٹریز (AST)
جب بھی کمپائلر کوڈ کو پارس کرتا ہے تو AST کا استعمال کیا جاتا ہے۔ یہ شاید وہ سب سے بنیادی ڈھانچہ ہے جس پر سٹیٹک اینالیسس کیا جا سکتا ہے۔
مختصراً، ایک AST ایک ساختی درخت (structured tree) ہے جہاں، عام طور پر، ہر پتے (leaf) میں ایک متغیر (variable) یا مستقل (constant) ہوتا ہے اور اندرونی نوڈز آپرینڈز یا کنٹرول فلو آپریشنز ہوتے ہیں۔ درج ذیل کوڈ پر غور کریں:
1function safeAdd(uint a, uint b) pure internal returns(uint){2 if(a + b <= a){3 revert();4 }5 return a + b;6}متعلقہ AST اس میں دکھایا گیا ہے:
Slither solc کے ذریعے ایکسپورٹ کردہ AST کا استعمال کرتا ہے۔
اگرچہ اسے بنانا آسان ہے، لیکن AST ایک نیسٹڈ (nested) ڈھانچہ ہے۔ بعض اوقات، اس کا تجزیہ کرنا سب سے سیدھا کام نہیں ہوتا۔ مثال کے طور پر، ایکسپریشن a + b <= a کے ذریعے استعمال ہونے والے آپریشنز کی شناخت کرنے کے لیے، آپ کو پہلے <= اور پھر + کا تجزیہ کرنا ہوگا۔ ایک عام طریقہ نام نہاد وزیٹر پیٹرن (visitor pattern) کا استعمال کرنا ہے، جو درخت کے ذریعے بار بار (recursively) نیویگیٹ کرتا ہے۔ Slither میں ExpressionVisitor (opens in a new tab) میں ایک عام وزیٹر شامل ہے۔
درج ذیل کوڈ یہ پتہ لگانے کے لیے ExpressionVisitor کا استعمال کرتا ہے کہ آیا ایکسپریشن میں اضافہ (addition) شامل ہے:
1from slither.visitors.expression.expression import ExpressionVisitor2from slither.core.expressions.binary_operation import BinaryOperationType3
4class HasAddition(ExpressionVisitor):5
6 def result(self):7 return self._result8
9 def _post_binary_operation(self, expression):10 if expression.type == BinaryOperationType.ADDITION:11 self._result = True12
13visitor = HasAddition(expression) # ایکسپریشن وہ ایکسپریشن ہے جس کی جانچ کی جانی ہے14print(f'The expression {expression} has a addition: {visitor.result()}')کنٹرول فلو گراف (CFG)
کوڈ کی دوسری سب سے عام نمائندگی کنٹرول فلو گراف (CFG) ہے۔ جیسا کہ اس کے نام سے ظاہر ہے، یہ گراف پر مبنی نمائندگی ہے جو تمام ایگزیکیوشن پاتھس کو ظاہر کرتی ہے۔ ہر نوڈ میں ایک یا ایک سے زیادہ ہدایات ہوتی ہیں۔ گراف میں کنارے (Edges) کنٹرول فلو آپریشنز (if/then/else، لوپ، وغیرہ) کی نمائندگی کرتے ہیں۔ ہماری پچھلی مثال کا CFG یہ ہے:
CFG وہ نمائندگی ہے جس کی بنیاد پر زیادہ تر تجزیے بنائے جاتے ہیں۔
کوڈ کی اور بھی بہت سی نمائندگیاں موجود ہیں۔ آپ جو تجزیہ کرنا چاہتے ہیں اس کے لحاظ سے ہر نمائندگی کے فوائد اور نقصانات ہوتے ہیں۔
تجزیہ
Slither کے ساتھ آپ جو سب سے آسان قسم کے تجزیے کر سکتے ہیں وہ سنٹیکٹک (syntactic) تجزیے ہیں۔
سنٹیکس کا تجزیہ
Slither پیٹرن میچنگ جیسی اپروچ کا استعمال کرتے ہوئے تضادات اور خامیوں کو تلاش کرنے کے لیے کوڈ کے مختلف اجزاء اور ان کی نمائندگی کے ذریعے نیویگیٹ کر سکتا ہے۔
مثال کے طور پر درج ذیل ڈیٹیکٹرز سنٹیکس سے متعلق مسائل تلاش کرتے ہیں:
-
اسٹیٹ ویری ایبل شیڈونگ (State variable shadowing) (opens in a new tab): تمام اسٹیٹ ویری ایبلز پر اعادہ (iterate) کرتا ہے اور چیک کرتا ہے کہ آیا کوئی وراثت میں ملے کانٹریکٹ سے کسی ویری ایبل کو شیڈو کرتا ہے (state.py#L51-L62 (opens in a new tab))
-
غلط ERC20 انٹرفیس (opens in a new tab): غلط ERC20 فنکشن سگنیچرز تلاش کرتا ہے (incorrect_erc20_interface.py#L34-L55 (opens in a new tab))
سیمینٹک تجزیہ
سنٹیکس کے تجزیے کے برعکس، ایک سیمینٹک تجزیہ زیادہ گہرائی میں جائے گا اور کوڈ کے "معنی" کا تجزیہ کرے گا۔ اس فیملی میں تجزیوں کی کچھ وسیع اقسام شامل ہیں۔ یہ زیادہ طاقتور اور مفید نتائج کا باعث بنتے ہیں، لیکن انہیں لکھنا بھی زیادہ پیچیدہ ہوتا ہے۔
سیمینٹک تجزیے سب سے جدید کمزوریوں (vulnerabilities) کا پتہ لگانے کے لیے استعمال ہوتے ہیں۔
ڈیٹا ڈیپینڈنسی کا تجزیہ
ایک ویری ایبل variable_a کو variable_b پر ڈیٹا پر منحصر (data-dependent) کہا جاتا ہے اگر کوئی ایسا پاتھ موجود ہو جس کے لیے variable_a کی قدر variable_b سے متاثر ہوتی ہو۔
درج ذیل کوڈ میں، variable_a کا انحصار variable_b پر ہے:
1// ...2variable_a = variable_b + 1;Slither اپنی درمیانی نمائندگی (جس پر بعد کے سیکشن میں بحث کی گئی ہے) کی بدولت بلٹ ان ڈیٹا ڈیپینڈنسی (opens in a new tab) کی صلاحیتوں کے ساتھ آتا ہے۔
ڈیٹا ڈیپینڈنسی کے استعمال کی ایک مثال خطرناک سخت برابری کے ڈیٹیکٹر (dangerous strict equality detector) (opens in a new tab) میں مل سکتی ہے۔ یہاں Slither کسی خطرناک قدر کے ساتھ سخت برابری کے موازنے کو تلاش کرے گا (incorrect_strict_equality.py#L86-L87 (opens in a new tab))، اور صارف کو مطلع کرے گا کہ اسے حملہ آور کو کانٹریکٹ میں پھنسانے سے روکنے کے لیے == کے بجائے >= یا <= کا استعمال کرنا چاہیے۔ دیگر چیزوں کے علاوہ، ڈیٹیکٹر balanceOf(address) پر کال کی ریٹرن ویلیو کو خطرناک سمجھے گا (incorrect_strict_equality.py#L63-L64 (opens in a new tab))، اور اس کے استعمال کو ٹریک کرنے کے لیے ڈیٹا ڈیپینڈنسی انجن کا استعمال کرے گا۔
فکسڈ پوائنٹ کیلکولیشن
اگر آپ کا تجزیہ CFG کے ذریعے نیویگیٹ کرتا ہے اور کناروں (edges) کی پیروی کرتا ہے، تو آپ کو پہلے سے دیکھے گئے نوڈز نظر آنے کا امکان ہے۔ مثال کے طور پر، اگر کوئی لوپ نیچے دکھائے گئے طریقے سے پیش کیا گیا ہے:
1for(uint i; i < range; ++i){2 variable_a += 13}آپ کے تجزیے کو یہ جاننے کی ضرورت ہوگی کہ کب رکنا ہے۔ یہاں دو اہم حکمت عملیاں ہیں: (1) ہر نوڈ پر ایک محدود تعداد میں اعادہ (iterate) کریں، (2) ایک نام نہاد فکس پوائنٹ (fixpoint) کا حساب لگائیں۔ فکس پوائنٹ کا بنیادی مطلب یہ ہے کہ اس نوڈ کا تجزیہ کرنے سے کوئی بامعنی معلومات فراہم نہیں ہوتی ہیں۔
استعمال شدہ فکس پوائنٹ کی ایک مثال ری اینٹرنسی (reentrancy) ڈیٹیکٹرز میں مل سکتی ہے: Slither نوڈز کو دریافت کرتا ہے، اور ایکسٹرنل کالز، اسٹوریج میں لکھنے اور پڑھنے کو تلاش کرتا ہے۔ ایک بار جب یہ فکس پوائنٹ پر پہنچ جاتا ہے (reentrancy.py#L125-L131 (opens in a new tab))، تو یہ دریافت کو روک دیتا ہے، اور مختلف ری اینٹرنسی پیٹرنز (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)) کے ذریعے یہ دیکھنے کے لیے نتائج کا تجزیہ کرتا ہے کہ آیا ری اینٹرنسی موجود ہے۔
موثر فکسڈ پوائنٹ کیلکولیشن کا استعمال کرتے ہوئے تجزیے لکھنے کے لیے اس بات کی اچھی سمجھ کی ضرورت ہوتی ہے کہ تجزیہ اپنی معلومات کو کیسے پھیلاتا ہے۔
درمیانی نمائندگی (Intermediate representation)
ایک درمیانی نمائندگی (IR) ایک ایسی زبان ہے جس کا مقصد اصل زبان کی نسبت سٹیٹک اینالیسس کے لیے زیادہ موزوں ہونا ہے۔ Slither Solidity کا اپنی IR میں ترجمہ کرتا ہے: SlithIR (opens in a new tab)۔
اگر آپ صرف بنیادی چیکس لکھنا چاہتے ہیں تو SlithIR کو سمجھنا ضروری نہیں ہے۔ تاہم، اگر آپ جدید سیمینٹک تجزیے لکھنے کا ارادہ رکھتے ہیں تو یہ کارآمد ثابت ہوگا۔ SlithIR (opens in a new tab) اور SSA (opens in a new tab) پرنٹرز آپ کو یہ سمجھنے میں مدد کریں گے کہ کوڈ کا ترجمہ کیسے کیا جاتا ہے۔
API کی بنیادی باتیں
Slither میں ایک API ہے جو آپ کو کانٹریکٹ اور اس کے فنکشنز کی بنیادی خصوصیات کو دریافت کرنے کی سہولت دیتی ہے۔
کوڈ بیس لوڈ کرنے کے لیے:
1from slither.slither import Slither2slither = Slither('/path/to/project')3
کانٹریکٹس اور فنکشنز کی دریافت
ایک Slither آبجیکٹ میں درج ذیل شامل ہیں:
contracts (list(Contract): کانٹریکٹس کی فہرستcontracts_derived (list(Contract): ان کانٹریکٹس کی فہرست جو کسی دوسرے کانٹریکٹ سے وراثت میں نہیں ملے ہیں (کانٹریکٹس کا ذیلی سیٹ)get_contract_from_name (str): اس کے نام سے ایک کانٹریکٹ واپس کرتا ہے
ایک Contract آبجیکٹ میں درج ذیل شامل ہیں:
name (str): کانٹریکٹ کا نامfunctions (list(Function)): فنکشنز کی فہرستmodifiers (list(Modifier)): فنکشنز کی فہرستall_functions_called (list(Function/Modifier)): کانٹریکٹ کے ذریعے قابل رسائی تمام اندرونی فنکشنز کی فہرستinheritance (list(Contract)): وراثت میں ملے کانٹریکٹس کی فہرستget_function_from_signature (str): اس کے سگنیچر سے ایک فنکشن واپس کرتا ہےget_modifier_from_signature (str): اس کے سگنیچر سے ایک موڈیفائر واپس کرتا ہےget_state_variable_from_name (str): اس کے نام سے ایک StateVariable واپس کرتا ہے
ایک Function یا Modifier آبجیکٹ میں درج ذیل شامل ہیں:
name (str): فنکشن کا نامcontract (contract): وہ کانٹریکٹ جہاں فنکشن ڈکلیئر کیا گیا ہےnodes (list(Node)): فنکشن/موڈیفائر کے CFG پر مشتمل نوڈز کی فہرستentry_point (Node): CFG کا انٹری پوائنٹvariables_read (list(Variable)): پڑھے گئے ویری ایبلز کی فہرستvariables_written (list(Variable)): لکھے گئے ویری ایبلز کی فہرستstate_variables_read (list(StateVariable)): پڑھے گئے اسٹیٹ ویری ایبلز کی فہرست (variables`read کا ذیلی سیٹ)state_variables_written (list(StateVariable)): لکھے گئے اسٹیٹ ویری ایبلز کی فہرست (variables`written کا ذیلی سیٹ)
صفحہ کی آخری اپ ڈیٹ: ۳ مارچ، ۲۰۲۶

