பிரதான உள்ளடக்கத்திற்குச் செல்

ஒரு ஒப்பந்தத்தை ரிவர்ஸ் இன்ஜினியரிங் செய்தல்

evm
opcodes
மேம்பட்டவை
Ori Pomerantz
30 டிசம்பர், 2021
28 நிமிட வாசிப்பு

அறிமுகம்

பிளாக்செயினில் ரகசியங்கள் எதுவும் இல்லை, நடக்கும் அனைத்தும் சீரானதாகவும், சரிபார்க்கக்கூடியதாகவும், பொதுவில் கிடைக்கக்கூடியதாகவும் உள்ளது. சிறந்தமுறையில், ஒப்பந்தங்களின் மூலக் குறியீட்டை Etherscan-இல் வெளியிட்டு சரிபார்க்க வேண்டும்opens in a new tab. இருப்பினும், அது எப்போதும் அப்படி இருப்பதில்லைopens in a new tab. இந்தக் கட்டுரையில், மூலக் குறியீடு இல்லாத ஒரு ஒப்பந்தமான 0x2510c039cc3b061d79e564b38836da87e31b342fopens in a new tab-ஐப் பார்த்து, ஒப்பந்தங்களை எவ்வாறு ரிவர்ஸ் இன்ஜினியரிங் செய்வது என்பதை நீங்கள் கற்றுக்கொள்கிறீர்கள்.

ரிவர்ஸ் கம்பைலர்கள் உள்ளன, ஆனால் அவை எப்போதும் பயன்படுத்தக்கூடிய முடிவுகளைத்opens in a new tab தருவதில்லை. இந்தக் கட்டுரையில், the opcodesopens in a new tab இலிருந்து ஒரு ஒப்பந்தத்தை கைமுறையாக ரிவர்ஸ் இன்ஜினியரிங் செய்து புரிந்துகொள்வது எப்படி, அத்துடன் ஒரு டீகம்பைலரின் முடிவுகளை எவ்வாறு விளக்குவது என்பதையும் நீங்கள் கற்றுக்கொள்கிறீர்கள்.

இந்தக் கட்டுரையைப் புரிந்துகொள்ள, நீங்கள் ஏற்கனவே EVM-இன் அடிப்படைகளை அறிந்திருக்க வேண்டும், மேலும் EVM அசெம்ப்ளருடன் ஓரளவாவது பரிச்சயமாக இருக்க வேண்டும். இந்த தலைப்புகளைப் பற்றி இங்கே படிக்கலாம்opens in a new tab.

செயல்படுத்தக்கூடிய குறியீட்டைத் தயார்செய்தல்

ஒப்பந்தத்திற்காக Etherscan-க்குச் சென்று, Contract தாவலைக் கிளிக் செய்து, பின்னர் Switch to Opcodes View என்பதைக் கிளிக் செய்வதன் மூலம் நீங்கள் ஆப்கோட்களைப் பெறலாம். ஒரு வரிக்கு ஒரு opcode கொண்ட ஒரு பார்வையை நீங்கள் பெறுவீர்கள்.

Etherscan-இலிருந்து Opcode பார்வை

இருப்பினும், தாவல்களைப் புரிந்துகொள்ள, ஒவ்வொரு opcode-ம் குறியீட்டில் எங்கு அமைந்துள்ளது என்பதை நீங்கள் தெரிந்து கொள்ள வேண்டும். அதைச் செய்ய, ஒரு வழி, ஒரு Google விரிதாளைத் திறந்து நெடுவரிசை C இல் opcodes-களை ஒட்டுவது. ஏற்கனவே தயாரிக்கப்பட்ட இந்த விரிதாளின் நகலை உருவாக்குவதன் மூலம் பின்வரும் படிகளை நீங்கள் தவிர்க்கலாம்opens in a new tab.

அடுத்த கட்டமாக, சரியான குறியீடு இருப்பிடங்களைப் பெறுவது, அதன்மூலம் நம்மால் தாவல்களைப் புரிந்துகொள்ள முடியும். நாம் opcode அளவை நெடுவரிசை B-யிலும், இருப்பிடத்தை (ஹெக்ஸாடெசிமலில்) நெடுவரிசை A-யிலும் வைப்போம். செல் B1-இல் இந்தச் சார்பைத் தட்டச்சு செய்து, பின்னர் அதை நெடுவரிசை B-இன் மற்ற பகுதிகளுக்கு, குறியீட்டின் இறுதி வரை நகலெடுத்து ஒட்டவும். இதைச் செய்த பிறகு நீங்கள் நெடுவரிசை B-ஐ மறைக்கலாம்.

1=1+IF(REGEXMATCH(C1,"PUSH"),REGEXEXTRACT(C1,"PUSH(\d+)"),0)

முதலில் இந்தச் சார்பு opcode-க்காகவே ஒரு பைட்டைச் சேர்க்கிறது, பின்னர் PUSH-ஐத் தேடுகிறது. Push opcodes சிறப்பானவை, ஏனெனில் தள்ளப்படும் மதிப்புக்கு அவற்றுக்குக் கூடுதல் பைட்டுகள் தேவை. opcode ஒரு PUSH ஆக இருந்தால், நாம் பைட்டுகளின் எண்ணிக்கையைப் பிரித்தெடுத்து அதைச் சேர்ப்போம்.

A1-இல் முதல் ஆஃப்செட்டான பூஜ்ஜியத்தை உள்ளிடவும். பின்னர், A2-இல், இந்தச் சார்பை உள்ளிட்டு, மீண்டும் அதை நெடுவரிசை A-இன் மற்ற பகுதிகளுக்கு நகலெடுத்து ஒட்டவும்:

1=dec2hex(hex2dec(A1)+B1)

இந்தச் சார்பு நமக்கு ஹெக்ஸாடெசிமல் மதிப்பைத் தர வேண்டும். ஏனெனில் தாவல்களுக்கு (JUMP மற்றும் JUMPI) முன்பு தள்ளப்படும் மதிப்புகள் நமக்கு ஹெக்ஸாடெசிமலில் கொடுக்கப்பட்டுள்ளன.

நுழைவு புள்ளி (0x00)

ஒப்பந்தங்கள் எப்போதும் முதல் பைட்டிலிருந்து செயல்படுத்தப்படுகின்றன. இது குறியீட்டின் ஆரம்பப் பகுதி:

ஆஃப்செட்Opcodeஸ்டாக் (opcode-க்கு பிறகு)
0PUSH1 0x800x80
2PUSH1 0x400x40, 0x80
4MSTOREகாலி
5PUSH1 0x040x04
7CALLDATASIZECALLDATASIZE 0x04
8LTCALLDATASIZE<4
9PUSH2 0x005e0x5E CALLDATASIZE<4
சிJUMPIகாலி

இந்தக் குறியீடு இரண்டு விஷயங்களைச் செய்கிறது:

  1. 0x80-ஐ 32 பைட் மதிப்பாக நினைவக இருப்பிடங்கள் 0x40-0x5F-இல் எழுதுங்கள் (0x80 ஆனது 0x5F-இல் சேமிக்கப்படுகிறது, மற்றும் 0x40-0x5E அனைத்தும் பூஜ்ஜியங்களாக உள்ளன).
  2. calldata அளவைப் படிக்கவும். வழக்கமாக ஒரு எத்தேரியம் ஒப்பந்தத்திற்கான அழைப்புத் தரவு the ABI (பயன்பாட்டு பைனரி இடைமுகம்)opens in a new tab-ஐப் பின்பற்றுகிறது, இதற்கு சார்புத் தேர்விக்கு குறைந்தபட்சம் நான்கு பைட்டுகள் தேவை. அழைப்பு தரவு அளவு நான்கிற்கும் குறைவாக இருந்தால், 0x5E-க்குச் செல்லவும்.

இந்த பகுதிக்கான பாய்வு விளக்கப்படம்

0x5E-இல் உள்ள ஹேண்ட்லர் (ABI அல்லாத அழைப்புத் தரவுகளுக்கு)

ஆஃப்செட்Opcode
5EJUMPDEST
5FCALLDATASIZE
60PUSH2 0x007c
63JUMPI

இந்தத் துணுக்கு ஒரு JUMPDEST உடன் தொடங்குகிறது. EVM (எத்தேரியம் மெய்நிகர் இயந்திரம்) நிரல்கள் நீங்கள் JUMPDEST இல்லாத ஒரு opcode-க்கு தாவினால் ஒரு விதிவிலக்கை வீசும். பின்னர் அது CALLDATASIZE-ஐப் பார்க்கிறது, அது "உண்மை" ஆக இருந்தால் (அதாவது, பூஜ்ஜியம் இல்லை) 0x7C-க்கு தாவுகிறது. நாம் கீழே அதைப்பற்றி பார்ப்போம்.

ஆஃப்செட்Opcodeஸ்டாக் (opcode-க்கு பிறகு)
64CALLVALUEஅழைப்பால் வழங்கப்படும் . Solidity-இல் msg.value என்று அழைக்கப்படுகிறது
65PUSH1 0x066 CALLVALUE
67PUSH1 0x000 6 CALLVALUE
69DUP3CALLVALUE 0 6 CALLVALUE
6ADUP36 CALLVALUE 0 6 CALLVALUE
6BSLOADStorage[6] CALLVALUE 0 6 CALLVALUE

எனவே அழைப்புத் தரவு இல்லாதபோது, நாம் Storage[6]-இன் மதிப்பை படிக்கிறோம். இந்த மதிப்பு என்னவென்று நமக்கு இன்னும் தெரியாது, ஆனால் அழைப்புத் தரவுகள் இல்லாத ஒப்பந்தம் பெற்ற பரிவர்த்தனைகளை நாம் தேடலாம். எந்தவொரு அழைப்புத் தரவும் இல்லாமல் (எனவே எந்த முறையும் இல்லாமல்) ETH-ஐ மட்டும் மாற்றும் பரிவர்த்தனைகள் Etherscan-இல் Transfer முறையைக் கொண்டுள்ளன. உண்மையில், ஒப்பந்தம் பெற்ற முதல் பரிவர்த்தனைopens in a new tab ஒரு பரிமாற்றமாகும்.

அந்தப் பரிவர்த்தனையில் நாம் மேலும் பார்க்க கிளிக் செய்யவும் என்பதை கிளிக் செய்தால், உள்ளீட்டுத் தரவு என்று அழைக்கப்படும் அழைப்புத் தரவு உண்மையில் காலியாக (0x) இருப்பதைக் காணலாம். மதிப்பு 1.559 ETH என்பதையும் கவனியுங்கள், அது பின்னர் பொருத்தமானதாக இருக்கும்.

அழைப்புத் தரவு காலியாக உள்ளது

அடுத்து, State தாவலைக் கிளிக் செய்து, நாம் ரிவர்ஸ் இன்ஜினியரிங் செய்யும் ஒப்பந்தத்தை (0x2510...) விரிவாக்கவும். பரிவர்த்தனையின் போது Storage[6] மாறியதைக் காணலாம், மேலும் நீங்கள் Hex-ஐ எண் ஆக மாற்றினால், அது 1,559,000,000,000,000,000 ஆக மாறியதைக் காண்பீர்கள். இது wei-இல் மாற்றப்பட்ட மதிப்பாகும் (தெளிவுக்காக நான் காற்புள்ளிகளைச் சேர்த்துள்ளேன்), அடுத்த ஒப்பந்த மதிப்புக்கு ஒத்திருக்கிறது.

Storage[6] இல் மாற்றம்

அதே காலகட்டத்தில் இருந்து பிற Transfer பரிவர்த்தனைகளால் ஏற்படும் நிலை மாற்றங்களைப்opens in a new tab பார்த்தால், Storage[6] சிறிது காலத்திற்கு ஒப்பந்தத்தின் மதிப்பைக் கண்காணித்திருப்பதைக் காணலாம். இப்போதைக்கு அதை Value* என்று அழைப்போம். நட்சத்திரக்குறி (*) இந்த மாறி என்ன செய்கிறது என்பது நமக்கு இன்னும் தெரியாது என்பதை நினைவூட்டுகிறது, ஆனால் அது ஒப்பந்த மதிப்பை மட்டும் கண்காணிப்பதாக இருக்க முடியாது. ஏனெனில், ADDRESS BALANCE-ஐப் பயன்படுத்தி உங்கள் கணக்குகளின் இருப்பைப் பெறக்கூடிய நிலையில், மிகவும் விலையுயர்ந்த சேமிப்பகத்தைப் பயன்படுத்தத் தேவையில்லை. முதல் opcode ஒப்பந்தத்தின் சொந்த முகவரியை தள்ளுகிறது. இரண்டாவது, ஸ்டேக்கின் மேலே உள்ள முகவரியைப் படித்து, அதை அந்த முகவரியின் இருப்புடன் மாற்றுகிறது.

ஆஃப்செட்Opcodeஅடுக்கு
6CPUSH2 0x00750x75 Value* CALLVALUE 0 6 CALLVALUE
6FSWAP2CALLVALUE Value* 0x75 0 6 CALLVALUE
70SWAP1Value* CALLVALUE 0x75 0 6 CALLVALUE
71PUSH2 0x01a70x01A7 Value* CALLVALUE 0x75 0 6 CALLVALUE
74JUMP

இந்தக் குறியீட்டை நாம் ஜம்ப் டெஸ்டினேஷனில் தொடர்ந்து கண்டுபிடிப்போம்.

ஆஃப்செட்Opcodeஅடுக்கு
1A7JUMPDESTValue* CALLVALUE 0x75 0 6 CALLVALUE
1A8PUSH1 0x000x00 Value* CALLVALUE 0x75 0 6 CALLVALUE
1AADUP3CALLVALUE 0x00 Value* CALLVALUE 0x75 0 6 CALLVALUE
1ABNOT2^256-CALLVALUE-1 0x00 Value* CALLVALUE 0x75 0 6 CALLVALUE

NOT என்பது பிட் வாரியானது, எனவே அது அழைப்பு மதிப்பில் உள்ள ஒவ்வொரு பிட்டின் மதிப்பையும் மாற்றுகிறது.

ஆஃப்செட்Opcodeஅடுக்கு
1ACDUP3Value* 2^256-CALLVALUE-1 0x00 Value* CALLVALUE 0x75 0 6 CALLVALUE
1ADGTValue*>2^256-CALLVALUE-1 0x00 Value* CALLVALUE 0x75 0 6 CALLVALUE
1AEISZEROValue*<=2^256-CALLVALUE-1 0x00 Value* CALLVALUE 0x75 0 6 CALLVALUE
1AFPUSH2 0x01df0x01DF Value*<=2^256-CALLVALUE-1 0x00 Value* CALLVALUE 0x75 0 6 CALLVALUE
1B2JUMPI

Value* ஆனது 2^256-CALLVALUE-1 ஐ விட சிறியதாக அல்லது அதற்கு சமமாக இருந்தால் நாம் தாவுகிறோம். இது வழிதலைத் தடுக்கும் தர்க்கம் போல் தெரிகிறது. உண்மையில், சில அர்த்தமற்ற செயல்பாடுகளுக்குப் பிறகு (உதாரணமாக, நினைவகத்தில் எழுதுவது நீக்கப்பட உள்ளது), ஓவர்ஃப்ளோ கண்டறியப்பட்டால், ஆஃப்செட் 0x01DE-இல் ஒப்பந்தம் திரும்புகிறது, இது இயல்பான நடத்தை.

அத்தகைய ஒரு ஓவர்ஃப்ளோ மிகவும் சாத்தியமற்றது என்பதை கவனியுங்கள், ஏனெனில் அதற்கு அழைப்பு மதிப்பு மற்றும் Value* ஆகியவை 2^256 wei உடன் ஒப்பிடக்கூடியதாக இருக்க வேண்டும், இது சுமார் 10^59 ETH. எழுதும்போது மொத்த ETH விநியோகம் இருநூறு மில்லியனுக்கும் குறைவாக உள்ளதுopens in a new tab.

ஆஃப்செட்Opcodeஅடுக்கு
1DFJUMPDEST0x00 Value* CALLVALUE 0x75 0 6 CALLVALUE
1E0POPValue* CALLVALUE 0x75 0 6 CALLVALUE
1E1ADDValue*+CALLVALUE 0x75 0 6 CALLVALUE
1E2SWAP10x75 Value*+CALLVALUE 0 6 CALLVALUE
1E3JUMP

நாம் இங்கே வந்தால், Value* + CALLVALUE பெற்று ஆஃப்செட் 0x75 க்கு தாவவும்.

ஆஃப்செட்Opcodeஅடுக்கு
75JUMPDESTValue*+CALLVALUE 0 6 CALLVALUE
76SWAP10 Value*+CALLVALUE 6 CALLVALUE
77SWAP26 Value*+CALLVALUE 0 CALLVALUE
78SSTORE0 CALLVALUE

நாம் இங்கே வந்தால் (அழைப்புத் தரவு காலியாக இருக்க வேண்டும்), Value*-க்கு அழைப்பு மதிப்பைச் சேர்ப்போம். இது Transfer பரிவர்த்தனைகள் செய்வதாக நாம் கூறுவதுடன் ஒத்துப்போகிறது.

ஆஃப்செட்Opcode
79POP
7APOP
7BSTOP

இறுதியாக, அடுக்கைத் துடைத்து (இது அவசியமில்லை) பரிவர்த்தனையின் வெற்றிகரமான முடிவை சமிக்ஞை செய்யவும்.

சுருக்கமாகச் சொன்னால், ஆரம்பக் குறியீட்டிற்கான ஒரு பாய்வு விளக்கப்படம் இங்கே.

நுழைவு புள்ளி பாய்வு விளக்கப்படம்

0x7C இல் உள்ள ஹேண்ட்லர்

இந்த ஹேண்ட்லர் என்ன செய்கிறது என்பதை நான் வேண்டுமென்றே தலைப்பில் வைக்கவில்லை. இந்த குறிப்பிட்ட ஒப்பந்தம் எவ்வாறு செயல்படுகிறது என்பதை உங்களுக்கு கற்பிப்பது நோக்கம் அல்ல, ஆனால் ஒப்பந்தங்களை எவ்வாறு ரிவர்ஸ் இன்ஜினியரிங் செய்வது என்பதே நோக்கம். நான் செய்ததைப் போலவே, குறியீட்டைப் பின்பற்றி அது என்ன செய்கிறது என்பதை நீங்கள் கற்றுக்கொள்வீர்கள்.

பல இடங்களில் இருந்து நாம் இங்கே வருகிறோம்:

  • 1, 2, அல்லது 3 பைட்டுகள் கொண்ட அழைப்புத் தரவு இருந்தால் (ஆஃப்செட் 0x63 இலிருந்து)
  • முறை கையொப்பம் தெரியவில்லை என்றால் (ஆஃப்செட்டுகள் 0x42 மற்றும் 0x5D இலிருந்து)
ஆஃப்செட்Opcodeஅடுக்கு
7CJUMPDEST
7DPUSH1 0x000x00
7FPUSH2 0x009d0x9D 0x00
82PUSH1 0x030x03 0x9D 0x00
84SLOADStorage[3] 0x9D 0x00

இது மற்றொரு சேமிப்பக செல், எந்தவொரு பரிவர்த்தனையிலும் என்னால் கண்டுபிடிக்க முடியாத ஒன்று, எனவே இதன் பொருள் என்னவென்று அறிவது கடினம். கீழேயுள்ள குறியீடு அதைத் தெளிவாக்கும்.

ஆஃப்செட்Opcodeஅடுக்கு
85PUSH20 0xffffffffffffffffffffffffffffffffffffffff0xff....ff Storage[3] 0x9D 0x00
9AANDStorage[3]-as-address 0x9D 0x00

இந்த ஆப்கோட்கள் நாம் Storage[3]-இலிருந்து படிக்கும் மதிப்பை 160 பிட்களுக்கு, அதாவது ஒரு எத்தேரியம் முகவரியின் நீளத்திற்கு, குறைக்கின்றன.

ஆஃப்செட்Opcodeஅடுக்கு
9BSWAP10x9D Storage[3]-as-address 0x00
9CJUMPStorage[3]-as-address 0x00

இந்த ஜம்ப் தேவையற்றது, ஏனெனில் நாம் அடுத்த opcode-க்குச் செல்கிறோம். இந்தக் குறியீடு இருக்கக்கூடிய அளவுக்கு எரிவாயு திறன் கொண்டதாக இல்லை.

ஆஃப்செட்Opcodeஅடுக்கு
9DJUMPDESTStorage[3]-as-address 0x00
9ESWAP10x00 Storage[3]-as-address
9FPOPStorage[3]-as-address
A0PUSH1 0x400x40 Storage[3]-as-address
A2MLOADMem[0x40] Storage[3]-as-address

குறியீட்டின் ஆரம்பத்திலேயே Mem[0x40]-ஐ 0x80 என அமைத்தோம். பின்னர் 0x40-ஐ தேடினால், அதை மாற்றவில்லை என்பதைக் காணலாம் - எனவே அது 0x80 என்று கருதலாம்.

ஆஃப்செட்Opcodeஅடுக்கு
A3CALLDATASIZECALLDATASIZE 0x80 Storage[3]-as-address
A4PUSH1 0x000x00 CALLDATASIZE 0x80 Storage[3]-as-address
A6DUP30x80 0x00 CALLDATASIZE 0x80 Storage[3]-as-address
A7CALLDATACOPY0x80 Storage[3]-as-address

அனைத்து அழைப்புத் தரவையும் நினைவகத்தில், 0x80-இல் தொடங்கி, நகலெடுக்கவும்.

ஆஃப்செட்Opcodeஅடுக்கு
A8PUSH1 0x000x00 0x80 Storage[3]-as-address
AADUP10x00 0x00 0x80 Storage[3]-as-address
ABCALLDATASIZECALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address
ACDUP40x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address
ADDUP6Storage[3]-as-address 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address
AEGASGAS Storage[3]-as-address 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address
AFDELEGATE_CALL

இப்போது விஷயங்கள் மிகவும் தெளிவாக உள்ளன. இந்த ஒப்பந்தம் ஒரு ப்ராக்ஸிopens in a new tab-ஆக செயல்பட முடியும், உண்மையான வேலையைச் செய்ய Storage[3]-இல் உள்ள முகவரியை அழைக்கிறது. DELEGATE_CALL ஒரு தனி ஒப்பந்தத்தை அழைக்கிறது, ஆனால் அதே சேமிப்பகத்தில் உள்ளது. இதன் பொருள், நாம் ப்ராக்ஸியாக இருக்கும் பிரதிநிதித்துவ ஒப்பந்தம், அதே சேமிப்பக இடத்தை அணுகுகிறது. அழைப்பின் அளவுருக்கள்:

  • Gas: மீதமுள்ள அனைத்து எரிவாயு
  • அழைக்கப்பட்ட முகவரி: Storage[3]-as-address
  • அழைப்புத் தரவு: 0x80-இல் தொடங்கும் CALLDATASIZE பைட்டுகள், இங்குதான் நாம் அசல் அழைப்புத் தரவை வைத்தோம்
  • திரும்பும் தரவு: எதுவுமில்லை (0x00 - 0x00) நாம் திரும்பும் தரவை மற்ற வழிகளில் பெறுவோம் (கீழே காண்க)
ஆஃப்செட்Opcodeஅடுக்கு
B0RETURNDATASIZERETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
B1DUP1RETURNDATASIZE RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
B2PUSH1 0x000x00 RETURNDATASIZE RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
B4DUP50x80 0x00 RETURNDATASIZE RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
B5RETURNDATACOPYRETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address

இங்கே நாம் அனைத்து திரும்பும் தரவையும் 0x80-இல் தொடங்கும் நினைவக பஃப்பரில் நகலெடுக்கிறோம்.

ஆஃப்செட்Opcodeஅடுக்கு
B6DUP2(((அழைப்பு வெற்றி/தோல்வி))) RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
B7DUP1(((அழைப்பு வெற்றி/தோல்வி))) (((அழைப்பு வெற்றி/தோல்வி))) RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
B8ISZERO(((அழைப்பு தோல்வியுற்றதா))) (((அழைப்பு வெற்றி/தோல்வி))) RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
B9PUSH2 0x00c00xC0 (((அழைப்பு தோல்வியுற்றதா))) (((அழைப்பு வெற்றி/தோல்வி))) RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
BCJUMPI(((அழைப்பு வெற்றி/தோல்வி))) RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
BDDUP2RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
BEDUP50x80 RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
BFRETURN

எனவே அழைப்பிற்குப் பிறகு, திரும்பும் தரவை 0x80 - 0x80+RETURNDATASIZE என்ற பஃபரில் நகலெடுத்து, அழைப்பு வெற்றிகரமாக இருந்தால், அந்த பஃபரைக் கொண்டு RETURN செய்கிறோம்.

DELEGATECALL தோல்வியுற்றது

நாம் இங்கே 0xC0-க்கு வந்தால், நாம் அழைத்த ஒப்பந்தம் திரும்பப் பெறப்பட்டது என்று பொருள். நாம் அந்த ஒப்பந்தத்திற்கு ஒரு ப்ராக்ஸியாக இருப்பதால், அதே தரவைத் திருப்பித் தந்து, திரும்பப் பெறவும் விரும்புகிறோம்.

ஆஃப்செட்Opcodeஅடுக்கு
C0JUMPDEST(((அழைப்பு வெற்றி/தோல்வி))) RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
C1DUP2RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
C2DUP50x80 RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) RETURNDATASIZE (((அழைப்பு வெற்றி/தோல்வி))) 0x80 Storage[3]-as-address
C3REVERT

எனவே நாம் முன்பு RETURN க்கு பயன்படுத்திய அதே பஃபர் மூலம் REVERT செய்கிறோம்: 0x80 - 0x80+RETURNDATASIZE

ப்ராக்ஸிக்கு அழைப்பு பாய்வு விளக்கப்படம்

ABI அழைப்புகள்

அழைப்பு தரவு அளவு நான்கு பைட்டுகள் அல்லது அதற்கு அதிகமாக இருந்தால், இது ஒரு செல்லுபடியாகும் ABI அழைப்பாக இருக்கலாம்.

ஆஃப்செட்Opcodeஅடுக்கு
டிPUSH1 0x000x00
எஃப்CALLDATALOAD(((அழைப்பு தரவின் முதல் சொல் (256 பிட்கள்))))
10PUSH1 0xe00xE0 (((அழைப்பு தரவின் முதல் சொல் (256 பிட்கள்))))
12SHR(((அழைப்பு தரவின் முதல் 32 பிட்கள் (4 பைட்டுகள்))))

1C என்பது அறியப்படாத opcode என்று Etherscan நமக்குச் சொல்கிறது, ஏனெனில் Etherscan இந்த அம்சத்தை எழுதிய பிறகு அது சேர்க்கப்பட்டதுopens in a new tab மற்றும் அவர்கள் அதை புதுப்பிக்கவில்லை. ஒரு புதுப்பிக்கப்பட்ட opcode அட்டவணைopens in a new tab இது வலதுபுறம் நகர்த்துதல் என்பதைக் காட்டுகிறது

ஆஃப்செட்Opcodeஅடுக்கு
13DUP1(((அழைப்பு தரவின் முதல் 32 பிட்கள் (4 பைட்டுகள்))) (((அழைப்பு தரவின் முதல் 32 பிட்கள் (4 பைட்டுகள்))))
14PUSH4 0x3cd8045e0x3CD8045E (((அழைப்பு தரவின் முதல் 32 பிட்கள் (4 பைட்டுகள்))) (((அழைப்பு தரவின் முதல் 32 பிட்கள் (4 பைட்டுகள்))))
19GT0x3CD8045E>அழைப்பு-தரவின்-முதல்-32-பிட்கள் (((அழைப்பு தரவின் முதல் 32 பிட்கள் (4 பைட்டுகள்))))
1APUSH2 0x00430x43 0x3CD8045E>அழைப்பு-தரவின்-முதல்-32-பிட்கள் (((அழைப்பு தரவின் முதல் 32 பிட்கள் (4 பைட்டுகள்))))
1DJUMPI(((அழைப்பு தரவின் முதல் 32 பிட்கள் (4 பைட்டுகள்))))

முறை கையொப்பம் பொருந்தக்கூடிய சோதனைகளை இதுபோன்று இரண்டாகப் பிரிப்பதன் மூலம் சராசரியாக பாதி சோதனைகளை சேமிக்க முடியும். இதைத் தொடர்ந்து வரும் குறியீடு மற்றும் 0x43-இல் உள்ள குறியீடு ஒரே மாதிரியான வடிவத்தைப் பின்பற்றுகின்றன: அழைப்புத் தரவின் முதல் 32 பிட்களை DUP1 செய்யவும், PUSH4 (((முறை கையொப்பம்)))-ஐ செய்யவும், சமத்துவத்தைச் சரிபார்க்க EQ-ஐ இயக்கவும், பின்னர் முறை கையொப்பம் பொருந்தினால் JUMPI` செய்யவும். இங்கே முறை கையொப்பங்கள், அவற்றின் முகவரிகள், மற்றும் தெரிந்தால் தொடர்புடைய முறை வரையறைopens in a new tab:

முறைமுறை கையொப்பம்குதிப்பதற்கான ஆஃப்செட்
splitter()opens in a new tab0x3cd8045e0x0103
???0x81e580d30x0138
currentWindow()opens in a new tab0xba0bafb40x0158
???0x1f1358230x00C4
merkleRoot()opens in a new tab0x2eb4a7ab0x00ED

பொருத்தம் காணப்படவில்லை என்றால், குறியீடு 0x7C இல் உள்ள ப்ராக்ஸி ஹேண்ட்லருக்கு தாவுகிறது, நாம் ப்ராக்ஸியாக இருக்கும் ஒப்பந்தத்தில் ஒரு பொருத்தம் இருக்கும் என்ற நம்பிக்கையில்.

ABI அழைப்புகள் பாய்வு விளக்கப்படம்

splitter()

ஆஃப்செட்Opcodeஅடுக்கு
103JUMPDEST
104CALLVALUECALLVALUE
105DUP1CALLVALUE CALLVALUE
106ISZEROCALLVALUE==0 CALLVALUE
107PUSH2 0x010f0x010F CALLVALUE==0 CALLVALUE
10AJUMPICALLVALUE
10BPUSH1 0x000x00 CALLVALUE
10DDUP10x00 0x00 CALLVALUE
10EREVERT

இந்தச் சார்பு செய்யும் முதல் விஷயம், அழைப்பு எந்த ETH-ஐயும் அனுப்பவில்லை என்பதைச் சரிபார்ப்பது. இந்தச் சார்பு payableopens in a new tab அல்ல. யாராவது நமக்கு ETH அனுப்பினால், அது ஒரு தவறாக இருக்க வேண்டும், மேலும் அவர்கள் அதைத் திரும்பப் பெற முடியாத இடத்தில் அந்த ETH இருப்பதைத் தவிர்க்க நாம் REVERT செய்ய விரும்புகிறோம்.

ஆஃப்செட்Opcodeஅடுக்கு
10FJUMPDEST
110POP
111PUSH1 0x030x03
113SLOAD(((Storage[3] a.k.a நாம் ஒரு ப்ராக்ஸியாக இருக்கும் ஒப்பந்தம்)))
114PUSH1 0x400x40 (((Storage[3] a.k.a நாம் ஒரு ப்ராக்ஸியாக இருக்கும் ஒப்பந்தம்)))
116MLOAD0x80 (((Storage[3] a.k.a நாம் ஒரு ப்ராக்ஸியாக இருக்கும் ஒப்பந்தம்)))
117PUSH20 0xffffffffffffffffffffffffffffffffffffffff0xFF...FF 0x80 (((Storage[3] a.k.a நாம் ஒரு ப்ராக்ஸியாக இருக்கும் ஒப்பந்தம்)))
12CSWAP10x80 0xFF...FF (((Storage[3] a.k.a நாம் ஒரு ப்ராக்ஸியாக இருக்கும் ஒப்பந்தம்)))
12DSWAP2(((Storage[3] a.k.a நாம் ஒரு ப்ராக்ஸியாக இருக்கும் ஒப்பந்தம்))) 0xFF...FF 0x80
12EANDProxyAddr 0x80
12FDUP20x80 ProxyAddr 0x80
130MSTORE0x80

மற்றும் 0x80 இப்போது ப்ராக்ஸி முகவரியைக் கொண்டுள்ளது

ஆஃப்செட்Opcodeஅடுக்கு
131PUSH1 0x200x20 0x80
133ADD0xA0
134PUSH2 0x00e40xE4 0xA0
137JUMP0xA0

E4 குறியீடு

இந்த வரிகளை நாம் முதல் முறையாகப் பார்க்கிறோம், ஆனால் அவை பிற முறைகளுடன் பகிரப்பட்டுள்ளன (கீழே காண்க). எனவே நாம் ஸ்டேக்கில் உள்ள மதிப்பை X என்று அழைப்போம், மேலும் splitter() இல் இந்த X இன் மதிப்பு 0xA0 என்பதை நினைவில் கொள்வோம்.

ஆஃப்செட்Opcodeஅடுக்கு
E4JUMPDESTX
E5PUSH1 0x400x40 X
E7MLOAD0x80 X
E8DUP10x80 0x80 X
E9SWAP2X 0x80 0x80
EASUBX-0x80 0x80
EBSWAP10x80 X-0x80
ECRETURN

எனவே இந்தக் குறியீடு ஸ்டேக்கில் (X) ஒரு நினைவக சுட்டிக்காட்டியியைப் பெறுகிறது, மேலும் ஒப்பந்தத்தை 0x80 - X என்ற பஃபருடன் RETURN செய்ய வைக்கிறது.

splitter() வழக்கில், இது நாம் ப்ராக்ஸியாக இருக்கும் முகவரியைத் தருகிறது. RETURN ஆனது 0x80-0x9F இல் உள்ள பஃப்பரைத் தருகிறது, இங்குதான் நாம் இந்தத் தரவை எழுதினோம் (மேலே ஆஃப்செட் 0x130).

currentWindow()

ஆஃப்செட்கள் 0x158-0x163 இல் உள்ள குறியீடு, நாம் splitter() இல் 0x103-0x10E இல் பார்த்தது போலவே உள்ளது (JUMPI இலக்கைத் தவிர), எனவே currentWindow() என்பதும் payable அல்ல என்பதை நாம் அறிவோம்.

ஆஃப்செட்Opcodeஅடுக்கு
164JUMPDEST
165POP
166PUSH2 0x00da0xDA
169PUSH1 0x010x01 0xDA
16BSLOADStorage[1] 0xDA
16CDUP20xDA Storage[1] 0xDA
16DJUMPStorage[1] 0xDA

DA குறியீடு

இந்தக் குறியீடு பிற முறைகளுடனும் பகிரப்பட்டுள்ளது. எனவே நாம் ஸ்டேக்கில் உள்ள மதிப்பை Y என்று அழைப்போம், மேலும் currentWindow() இல் இந்த Y இன் மதிப்பு Storage[1] என்பதை நினைவில் கொள்வோம்.

ஆஃப்செட்Opcodeஅடுக்கு
DAJUMPDESTY 0xDA
DBPUSH1 0x400x40 Y 0xDA
DDMLOAD0x80 Y 0xDA
DESWAP1Y 0x80 0xDA
DFDUP20x80 Y 0x80 0xDA
E0MSTORE0x80 0xDA

Y-ஐ 0x80-0x9F இல் எழுதுங்கள்.

ஆஃப்செட்Opcodeஅடுக்கு
E1PUSH1 0x200x20 0x80 0xDA
E3ADD0xA0 0xDA

மீதமுள்ளவை ஏற்கனவே மேலே விளக்கப்பட்டுள்ளன. எனவே 0xDA-க்குச் செல்லும் தாவல்கள், ஸ்டேக்கின் மேல் மதிப்பை (Y) 0x80-0x9F-க்கு எழுதி, அந்த மதிப்பைத் திருப்பித் தருகின்றன. currentWindow() வழக்கில், அது Storage[1]-ஐத் தருகிறது.

merkleRoot()

ஆஃப்செட்கள் 0xED-0xF8-இல் உள்ள குறியீடு, நாம் splitter()-இல் 0x103-0x10E-இல் பார்த்தது போலவே உள்ளது (JUMPI இலக்கைத் தவிர), எனவே merkleRoot() என்பதும் payable அல்ல என்பதை நாம் அறிவோம்.

ஆஃப்செட்Opcodeஅடுக்கு
F9JUMPDEST
FAPOP
FBPUSH2 0x00da0xDA
FEPUSH1 0x000x00 0xDA
100SLOADStorage[0] 0xDA
101DUP20xDA Storage[0] 0xDA
102JUMPStorage[0] 0xDA

தாண்டலுக்குப் பிறகு என்ன நடக்கும் என்பதை நாம் ஏற்கனவே கண்டுபிடித்துவிட்டோம். எனவே merkleRoot() Storage[0]-ஐத் தருகிறது.

0x81e580d3

ஆஃப்செட்கள் 0x138-0x143-இல் உள்ள குறியீடு, நாம் splitter()-இல் 0x103-0x10E-இல் பார்த்தது போலவே உள்ளது (JUMPI இலக்கைத் தவிர), எனவே இந்த சார்பும் payable அல்ல என்பதை நாம் அறிவோம்.

ஆஃப்செட்Opcodeஅடுக்கு
144JUMPDEST
145POP
146PUSH2 0x00da0xDA
149PUSH2 0x01530x0153 0xDA
14CCALLDATASIZECALLDATASIZE 0x0153 0xDA
14DPUSH1 0x040x04 CALLDATASIZE 0x0153 0xDA
14FPUSH2 0x018f0x018F 0x04 CALLDATASIZE 0x0153 0xDA
152JUMP0x04 CALLDATASIZE 0x0153 0xDA
18FJUMPDEST0x04 CALLDATASIZE 0x0153 0xDA
190PUSH1 0x000x00 0x04 CALLDATASIZE 0x0153 0xDA
192PUSH1 0x200x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA
194DUP30x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA
195DUP5CALLDATASIZE 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA
196SUBCALLDATASIZE-4 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA
197SLTCALLDATASIZE-4<32 0x00 0x04 CALLDATASIZE 0x0153 0xDA
198ISZEROCALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA
199PUSH2 0x01a00x01A0 CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA
19CJUMPI0x00 0x04 CALLDATASIZE 0x0153 0xDA

இந்தச் சார்பு குறைந்தபட்சம் 32 பைட்டுகள் (ஒரு சொல்) அழைப்புத் தரவை எடுப்பதாகத் தெரிகிறது.

ஆஃப்செட்Opcodeஅடுக்கு
19DDUP10x00 0x00 0x04 CALLDATASIZE 0x0153 0xDA
19EDUP20x00 0x00 0x00 0x04 CALLDATASIZE 0x0153 0xDA
19FREVERT

அது அழைப்புத் தரவைப் பெறவில்லை என்றால், பரிவர்த்தனை எந்தவொரு திரும்பும் தரவும் இல்லாமல் திரும்பப் பெறப்படுகிறது.

சார்பு தனக்குத் தேவையான அழைப்புத் தரவைப் பெற்றால் என்ன நடக்கும் என்று பார்ப்போம்.

ஆஃப்செட்Opcodeஅடுக்கு
1A0JUMPDEST0x00 0x04 CALLDATASIZE 0x0153 0xDA
1A1POP0x04 CALLDATASIZE 0x0153 0xDA
1A2CALLDATALOADcalldataload(4) CALLDATASIZE 0x0153 0xDA

calldataload(4) என்பது முறை கையொப்பத்திற்குப் பிறகு அழைப்புத் தரவின் முதல் சொல்

ஆஃப்செட்Opcodeஅடுக்கு
1A3SWAP20x0153 CALLDATASIZE calldataload(4) 0xDA
1A4SWAP1CALLDATASIZE 0x0153 calldataload(4) 0xDA
1A5POP0x0153 calldataload(4) 0xDA
1A6JUMPcalldataload(4) 0xDA
153JUMPDESTcalldataload(4) 0xDA
154PUSH2 0x016e0x016E calldataload(4) 0xDA
157JUMPcalldataload(4) 0xDA
16EJUMPDESTcalldataload(4) 0xDA
16FPUSH1 0x040x04 calldataload(4) 0xDA
171DUP2calldataload(4) 0x04 calldataload(4) 0xDA
172DUP20x04 calldataload(4) 0x04 calldataload(4) 0xDA
173SLOADStorage[4] calldataload(4) 0x04 calldataload(4) 0xDA
174DUP2calldataload(4) Storage[4] calldataload(4) 0x04 calldataload(4) 0xDA
175LTcalldataload(4)<Storage[4] calldataload(4) 0x04 calldataload(4) 0xDA
176PUSH2 0x017e0x017EC calldataload(4)<Storage[4] calldataload(4) 0x04 calldataload(4) 0xDA
179JUMPIcalldataload(4) 0x04 calldataload(4) 0xDA

முதல் சொல் Storage[4]-ஐ விடக் குறைவாக இல்லையென்றால், சார்பு தோல்வியடைகிறது. அது எந்தத் திரும்பிய மதிப்பும் இல்லாமல் திரும்பப் பெறுகிறது:

ஆஃப்செட்Opcodeஅடுக்கு
17APUSH1 0x000x00 ...
17CDUP10x00 0x00 ...
17DREVERT

calldataload(4) Storage[4]-ஐ விடக் குறைவாக இருந்தால், இந்தக் குறியீட்டைப் பெறுகிறோம்:

ஆஃப்செட்Opcodeஅடுக்கு
17EJUMPDESTcalldataload(4) 0x04 calldataload(4) 0xDA
17FPUSH1 0x000x00 calldataload(4) 0x04 calldataload(4) 0xDA
181SWAP20x04 calldataload(4) 0x00 calldataload(4) 0xDA
182DUP30x00 0x04 calldataload(4) 0x00 calldataload(4) 0xDA
183MSTOREcalldataload(4) 0x00 calldataload(4) 0xDA

மற்றும் நினைவக இருப்பிடங்கள் 0x00-0x1F இப்போது தரவு 0x04-ஐக் கொண்டுள்ளது (0x00-0x1E அனைத்தும் பூஜ்ஜியங்கள், 0x1F நான்கு ஆகும்)

ஆஃப்செட்Opcodeஅடுக்கு
184PUSH1 0x200x20 calldataload(4) 0x00 calldataload(4) 0xDA
186SWAP1calldataload(4) 0x20 0x00 calldataload(4) 0xDA
187SWAP20x00 0x20 calldataload(4) calldataload(4) 0xDA
188SHA3(((0x00-0x1F இன் SHA3))) calldataload(4) calldataload(4) 0xDA
189ADD(((0x00-0x1F இன் SHA3)))+calldataload(4) calldataload(4) 0xDA
18ASLOADStorage[(((0x00-0x1F இன் SHA3))) + calldataload(4)] calldataload(4) 0xDA

எனவே சேமிப்பகத்தில் ஒரு தேடல் அட்டவணை உள்ளது, இது 0x000...0004 இன் SHA3 இல் தொடங்குகிறது மற்றும் ஒவ்வொரு முறையான அழைப்பு தரவு மதிப்பிற்கும் ஒரு பதிவைக் கொண்டுள்ளது (Storage[4]-இன் கீழ் மதிப்பு).

ஆஃப்செட்Opcodeஅடுக்கு
18BSWAP1calldataload(4) Storage[(((0x00-0x1F இன் SHA3))) + calldataload(4)] 0xDA
18CPOPStorage[(((0x00-0x1F இன் SHA3))) + calldataload(4)] 0xDA
18DDUP20xDA Storage[(((0x00-0x1F இன் SHA3))) + calldataload(4)] 0xDA
18EJUMPStorage[(((0x00-0x1F இன் SHA3))) + calldataload(4)] 0xDA

offset 0xDA-வில் உள்ள குறியீடு என்ன செய்கிறது என்பதை நாம் ஏற்கனவே அறிவோம், அது ஸ்டேக்கின் மேல் மதிப்பை அழைப்பாளருக்குத் திருப்பித் தருகிறது. எனவே இந்தச் சார்பு தேடல் அட்டவணையில் இருந்து அழைப்பாளருக்கு மதிப்பைத் திருப்பித் தருகிறது.

0x1f135823

ஆஃப்செட்கள் 0xC4-0xCF-இல் உள்ள குறியீடு, நாம் splitter()-இல் 0x103-0x10E-இல் பார்த்தது போலவே உள்ளது (JUMPI இலக்கைத் தவிர), எனவே இந்த சார்பும் payable அல்ல என்பதை நாம் அறிவோம்.

ஆஃப்செட்Opcodeஅடுக்கு
D0JUMPDEST
D1POP
D2PUSH2 0x00da0xDA
D5PUSH1 0x060x06 0xDA
D7SLOADValue* 0xDA
D8DUP20xDA Value* 0xDA
D9JUMPValue* 0xDA

offset 0xDA-வில் உள்ள குறியீடு என்ன செய்கிறது என்பதை நாம் ஏற்கனவே அறிவோம், அது ஸ்டேக்கின் மேல் மதிப்பை அழைப்பாளருக்குத் திருப்பித் தருகிறது. எனவே இந்தச் சார்பு Value*-ஐத் தருகிறது.

முறை சுருக்கம்

இந்த ஒப்பந்தத்தை இப்போது நீங்கள் புரிந்துகொண்டதாக உணர்கிறீர்களா? நான் அப்படி உணரவில்லை. இதுவரை நாம் இந்த முறைகளைக் கொண்டுள்ளோம்:

முறைபொருள்
மாற்றம்அழைப்பால் வழங்கப்படும் மதிப்பை ஏற்றுக்கொண்டு, அந்தத் தொகையால் Value* ஐ அதிகரிக்கவும்
splitter()Storage[3], ப்ராக்ஸி முகவரியைத் திருப்பித் தரவும்
currentWindow()Storage[1] ஐ திருப்பியனுப்புக
merkleRoot()Storage[0] ஐ திருப்பியனுப்புக
0x81e580d3அளவுரு Storage[4]-ஐ விடக் குறைவாக இருந்தால், ஒரு தேடல் அட்டவணையில் இருந்து மதிப்பைத் திருப்பித் தரவும்
0x1f135823Storage[6]-ஐத் திருப்பித் தரவும், அதாவது Value*

ஆனால் மற்ற எந்த செயல்பாடும் Storage[3]-இல் உள்ள ஒப்பந்தத்தால் வழங்கப்படுகிறது என்பதை நாம் அறிவோம். ஒருவேளை அந்த ஒப்பந்தம் என்னவென்று நமக்குத் தெரிந்தால் அது நமக்கு ஒரு துப்பு கொடுக்கலாம். அதிர்ஷ்டவசமாக, இது பிளாக்செயின் மற்றும் அனைத்தும் அறியப்பட்டவை, குறைந்தபட்சம் கோட்பாட்டளவில். Storage[3]-ஐ அமைக்கும் எந்த முறைகளையும் நாம் பார்க்கவில்லை, எனவே அது கன்ஸ்ட்ரக்டரால் அமைக்கப்பட்டிருக்க வேண்டும்.

கன்ஸ்ட்ரக்டர்

நாம் ஒரு ஒப்பந்தத்தைப் பார்க்கும்போதுopens in a new tab, அதை உருவாக்கிய பரிவர்த்தனையையும் பார்க்கலாம்.

உருவாக்கு பரிவர்த்தனையைக் கிளிக் செய்யவும்

அந்தப் பரிவர்த்தனையைக் கிளிக் செய்து, பின்னர் State தாவலைக் கிளிக் செய்தால், அளவுருக்களின் ஆரம்ப மதிப்புகளைப் பார்க்கலாம். குறிப்பாக, Storage[3] 0x2f81e57ff4f4d83b40a9f719fd892d8e806e0761opens in a new tab-ஐக் கொண்டுள்ளது என்பதைக் காணலாம். அந்த ஒப்பந்தம் விடுபட்ட செயல்பாட்டைக் கொண்டிருக்க வேண்டும். நாம் விசாரிக்கும் ஒப்பந்தத்திற்கு நாம் பயன்படுத்திய அதே கருவிகளைப் பயன்படுத்தி அதை நாம் புரிந்துகொள்ளலாம்.

ப்ராக்ஸி ஒப்பந்தம்

மேலே உள்ள அசல் ஒப்பந்தத்திற்கு நாம் பயன்படுத்திய அதே நுட்பங்களைப் பயன்படுத்தி, ஒப்பந்தம் பின்வரும் சந்தர்ப்பங்களில் திரும்பப் பெறுகிறது என்பதைக் காணலாம்:

  • அழைப்பில் ஏதேனும் ETH இணைக்கப்பட்டிருந்தால் (0x05-0x0F)
  • அழைப்புத் தரவு அளவு நான்கிற்கும் குறைவாக இருந்தால் (0x10-0x19 மற்றும் 0xBE-0xC2)

அது ஆதரிக்கும் முறைகள்:

முறைமுறை கையொப்பம்குதிப்பதற்கான ஆஃப்செட்
scaleAmountByPercentage(uint256,uint256)opens in a new tab0x8ffb5c970x0135
isClaimed(uint256,address)opens in a new tab0xd2ef07950x0151
claim(uint256,address,uint256,bytes32[])opens in a new tab0x2e7ba6ef0x00F4
incrementWindow()opens in a new tab0x338b1d310x0110
???0x3f26479e0x0118
???0x1e7df9d30x00C3
currentWindow()opens in a new tab0xba0bafb40x0148
merkleRoot()opens in a new tab0x2eb4a7ab0x0107
???0x81e580d30x0122
???0x1f1358230x00D8

கீழே உள்ள நான்கு முறைகளை நாம் புறக்கணிக்கலாம், ஏனெனில் நாம் அவற்றை ஒருபோதும் அடைய மாட்டோம். அவற்றின் கையொப்பங்கள் நமது அசல் ஒப்பந்தம் அவற்றைத் தானாகவே கவனித்துக் கொள்ளும் வகையில் உள்ளன (விவரங்களை மேலே காண கையொப்பங்களைக் கிளிக் செய்யலாம்), எனவே அவை மீறப்பட்ட முறைகளாகopens in a new tab இருக்க வேண்டும்.

மீதமுள்ள முறைகளில் ஒன்று claim(<params>), மற்றொன்று isClaimed(<params>), எனவே இது ஒரு ஏர்டிராப் ஒப்பந்தம் போல் தெரிகிறது. மீதமுள்ளவற்றை ஆப்கோட் வாரியாகப் பார்ப்பதற்குப் பதிலாக, டீகம்பைலரை முயற்சி செய்யலாம்opens in a new tab, இது இந்த ஒப்பந்தத்தில் இருந்து மூன்று சார்புகளுக்குப் பயன்படுத்தக்கூடிய முடிவுகளைத் தருகிறது. மற்றவற்றை ரிவர்ஸ் இன்ஜினியரிங் செய்வது வாசகருக்கு ஒரு பயிற்சியாக விடப்படுகிறது.

scaleAmountByPercentage

இந்தச் சார்புக்கு டீகம்பைலர் தருவது இதுதான்:

1def unknown8ffb5c97(uint256 _param1, uint256 _param2) payable:
2 require calldata.size - 4 >=64
3 if _param1 and _param2 > -1 / _param1:
4 revert with 0, 17
5 return (_param1 * _param2 / 100 * 10^6)

முதல் require சோதனையானது, அழைப்புத் தரவு, சார்பு கையொப்பத்தின் நான்கு பைட்டுகளுக்கு மேலாக, குறைந்தபட்சம் 64 பைட்டுகளைக் கொண்டுள்ளது என்பதைச் சோதிக்கிறது, இது இரண்டு அளவுருக்களுக்கும் போதுமானது. இல்லையெனில் வெளிப்படையாக ஏதோ தவறு இருக்கிறது.

if கூற்று _param1 பூஜ்ஜியம் அல்ல என்பதையும், _param1 * _param2 எதிர்மறையாக இல்லை என்பதையும் சரிபார்ப்பதாகத் தெரிகிறது. இது அநேகமாக சுழற்சி நிகழ்வுகளைத் தடுப்பதற்காக இருக்கலாம்.

இறுதியாக, சார்பு ஒரு அளவிடப்பட்ட மதிப்பைத் தருகிறது.

உரிமைகோரல்

டீகம்பைலர் உருவாக்கும் குறியீடு சிக்கலானது, மேலும் அது அனைத்தும் நமக்குத் தொடர்புடையது அல்ல. நான் பயனுள்ள தகவல்களை வழங்கும் என்று நம்பும் வரிகளில் கவனம் செலுத்த சிலவற்றைத் தவிர்க்கப் போகிறேன்

1def unknown2e7ba6ef(uint256 _param1, uint256 _param2, uint256 _param3, array _param4) payable:
2 ...
3 require _param2 == addr(_param2)
4 ...
5 if currentWindow <= _param1:
6 revert with 0, 'cannot claim for a future window'

இங்கே இரண்டு முக்கியமான விஷயங்களைப் பார்க்கிறோம்:

  • _param2, uint256 என அறிவிக்கப்பட்டாலும், உண்மையில் ஒரு முகவரி
  • _param1 கோரப்படும் சாளரம், இது currentWindow அல்லது அதற்கு முந்தையதாக இருக்க வேண்டும்.
1 ...
2 if stor5[_claimWindow][addr(_claimFor)]:
3 revert with 0, 'Account already claimed the given window'

எனவே இப்போது Storage[5] என்பது சாளரங்கள் மற்றும் முகவரிகளின் ஒரு வரிசை என்றும், ஒரு முகவரி அந்தச் சாளரத்திற்கான வெகுமதியைக் கோரியதா இல்லையா என்பதையும் நாம் அறிவோம்.

1 ...
2 idx = 0
3 s = 0
4 while idx < _param4.length:
5 ...
6 if s + sha3(mem[(32 * _param4.length) + 328 len mem[(32 * _param4.length) + 296]]) > mem[(32 * idx) + 296]:
7 mem[mem[64] + 32] = mem[(32 * idx) + 296]
8 ...
9 s = sha3(mem[_62 + 32 len mem[_62]])
10 continue
11 ...
12 s = sha3(mem[_66 + 32 len mem[_66]])
13 continue
14 if unknown2eb4a7ab != s:
15 revert with 0, 'Invalid proof'
அனைத்தையும் காட்டு

நாம் unknown2eb4a7ab என்பது உண்மையில் merkleRoot() சார்பு என்பதை அறிவோம், எனவே இந்தக் குறியீடு ஒரு மெர்க்கிள் சான்றைopens in a new tab சரிபார்ப்பதாகத் தெரிகிறது. இதன் பொருள் _param4 என்பது ஒரு மெர்க்கிள் சான்று.

1 call addr(_param2) with:
2 value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei
3 gas 30000 wei

ஒரு ஒப்பந்தம் தனது சொந்த ETH-ஐ மற்றொரு முகவரிக்கு (ஒப்பந்தம் அல்லது வெளிப்புறமாகச் சொந்தமானது) மாற்றுவது இப்படித்தான். அது மாற்றப்பட வேண்டிய தொகையின் மதிப்புடன் அதை அழைக்கிறது. எனவே இது ETH-இன் ஏர்டிராப் போல் தெரிகிறது.

1 if not return_data.size:
2 if not ext_call.success:
3 require ext_code.size(stor2)
4 call stor2.deposit() with:
5 value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei

கீழே உள்ள இரண்டு வரிகள் Storage[2]-ம் நாம் அழைக்கும் ஒரு ஒப்பந்தம் என்று கூறுகின்றன. கன்ஸ்ட்ரக்டர் பரிவர்த்தனையைப்opens in a new tab பார்த்தால், இந்த ஒப்பந்தம் 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2opens in a new tab, ஒரு Wrapped Ether ஒப்பந்தம் அதன் மூலக் குறியீடு Etherscan-க்கு பதிவேற்றப்பட்டுள்ளதுopens in a new tab என்பதைக் காணலாம்.

எனவே ஒப்பந்தங்கள் _param2-க்கு ETH அனுப்ப முயற்சிப்பதாக தெரிகிறது. அதைச் செய்ய முடிந்தால், நல்லது. இல்லையென்றால், அது WETHopens in a new tab-ஐ அனுப்ப முயற்சிக்கிறது. _param2 என்பது ஒரு வெளிப்புறமாகச் சொந்தமான கணக்காக (EOA) இருந்தால், அது எப்போதும் ETH-ஐப் பெற முடியும், ஆனால் ஒப்பந்தங்கள் ETH-ஐப் பெறுவதை மறுக்கலாம். இருப்பினும், WETH என்பது ERC-20 ஆகும், மேலும் ஒப்பந்தங்கள் அதை ஏற்றுக்கொள்வதை மறுக்க முடியாது.

1 ...
2 log 0xdbd5389f: addr(_param2), unknown81e580d3[_param1] * _param3 / 100 * 10^6, bool(ext_call.success)

சார்பின் இறுதியில் ஒரு பதிவுப் பதிவு உருவாக்கப்படுவதைப் பார்க்கிறோம். உருவாக்கப்பட்ட பதிவுப் பதிவுகளைப் பாருங்கள்opens in a new tab மற்றும் 0xdbd5... உடன் தொடங்கும் தலைப்பில் வடிகட்டவும். அத்தகைய ஒரு பதிவை உருவாக்கிய பரிவர்த்தனைகளில் ஒன்றைக் கிளிக் செய்தால்opens in a new tab, அது உண்மையில் ஒரு கோரிக்கை போல் தெரிகிறது - கணக்கு ரிவர்ஸ் இன்ஜினியரிங் செய்யும் ஒப்பந்தத்திற்கு ஒரு செய்தியை அனுப்பியது, அதற்கு பதிலாக ETH-ஐப் பெற்றது.

ஒரு கோரிக்கை பரிவர்த்தனை

1e7df9d3

இந்தச் சார்பு மேலே உள்ள claim ஐப் போன்றது. இது ஒரு மெர்க்கிள் சான்றையும் சரிபார்க்கிறது, முதல்வருக்கு ETH-ஐ மாற்ற முயற்சிக்கிறது, மேலும் அதே வகையான பதிவுப் பதிவைத் தருகிறது.

1def unknown1e7df9d3(uint256 _param1, uint256 _param2, array _param3) payable:
2 ...
3 idx = 0
4 s = 0
5 while idx < _param3.length:
6 if idx >= mem[96]:
7 revert with 0, 50
8 _55 = mem[(32 * idx) + 128]
9 if s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]]) > mem[(32 * idx) + 128]:
10 ...
11 s = sha3(mem[_58 + 32 len mem[_58]])
12 continue
13 mem[mem[64] + 32] = s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]])
14 ...
15 if unknown2eb4a7ab != s:
16 revert with 0, 'Invalid proof'
17 ...
18 call addr(_param1) with:
19 value s wei
20 gas 30000 wei
21 if not return_data.size:
22 if not ext_call.success:
23 require ext_code.size(stor2)
24 call stor2.deposit() with:
25 value s wei
26 gas gas_remaining wei
27 ...
28 log 0xdbd5389f: addr(_param1), s, bool(ext_call.success)
அனைத்தையும் காட்டு

முக்கிய வேறுபாடு என்னவென்றால், முதல் அளவுரு, திரும்பப் பெறுவதற்கான சாளரம், அங்கு இல்லை. அதற்குப் பதிலாக, கோரக்கூடிய அனைத்து சாளரங்களுக்கும் ஒரு சுழற்சி உள்ளது.

1 idx = 0
2 s = 0
3 while idx < currentWindow:
4 ...
5 if stor5[mem[0]]:
6 if idx == -1:
7 revert with 0, 17
8 idx = idx + 1
9 s = s
10 continue
11 ...
12 stor5[idx][addr(_param1)] = 1
13 if idx >= unknown81e580d3.length:
14 revert with 0, 50
15 mem[0] = 4
16 if unknown81e580d3[idx] and _param2 > -1 / unknown81e580d3[idx]:
17 revert with 0, 17
18 if s > !(unknown81e580d3[idx] * _param2 / 100 * 10^6):
19 revert with 0, 17
20 if idx == -1:
21 revert with 0, 17
22 idx = idx + 1
23 s = s + (unknown81e580d3[idx] * _param2 / 100 * 10^6)
24 continue
அனைத்தையும் காட்டு

எனவே இது அனைத்து சாளரங்களையும் கோரும் ஒரு claim மாறுபாடு போல் தெரிகிறது.

முடிவுரை

இப்போது மூலக் குறியீடு கிடைக்காத ஒப்பந்தங்களை, ஆப்கோட்கள் அல்லது (அது வேலை செய்யும் போது) டீகம்பைலரைப் பயன்படுத்தி எப்படிப் புரிந்துகொள்வது என்று நீங்கள் அறிந்திருக்க வேண்டும். இந்தக் கட்டுரையின் நீளத்திலிருந்து தெளிவாகத் தெரிவது போல, ஒரு ஒப்பந்தத்தை ரிவர்ஸ் இன்ஜினியரிங் செய்வது அற்பமானதல்ல, ஆனால் பாதுகாப்பு அவசியமான ஒரு அமைப்பில், ஒப்பந்தங்கள் வாக்குறுதியளித்தபடி செயல்படுகின்றன என்பதை சரிபார்க்கும் திறமை ஒரு முக்கியமான திறமையாகும்.

எனது மேலும் பணிகளை இங்கே பார்க்கவும்opens in a new tab.

பக்கத்தின் கடைசி புதுப்பிப்பு: 14 பிப்ரவரி, 2026

இந்தப் பயிற்சி உதவியாக இருந்ததா?