BTC
ETH
HTX
SOL
BNB
ดูตลาด
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt

การแยกโครงสร้างสัญญาอัจฉริยะ (3): ตัวเลือกฟังก์ชัน

猎豹区块链安全
特邀专栏作者
2018-12-17 06:15
บทความนี้มีประมาณ 4218 คำ การอ่านทั้งหมดใช้เวลาประมาณ 7 นาที
เปิดรหัสพื้นฐานของส่วน "ตัวเลือกฟังก์ชัน" ของสัญญาอัจฉริยะพร้อมกัน
สรุปโดย AI
ขยาย
เปิดรหัสพื้นฐานของส่วน "ตัวเลือกฟังก์ชัน" ของสัญญาอัจฉริยะพร้อมกัน


สวัสดี ยินดีต้อนรับสู่การแยกโครงสร้างสัญญาอัจฉริยะกับฉันต่อไป บทความนี้เป็นส่วนที่ 3 ของซีรีส์ ดังนั้นหากคุณยังไม่ได้อ่านบทความก่อนหน้านี้ โปรดดู:

(1)คำนำ: รหัสพื้นฐานและวิธีดำเนินการ;

(2)การสร้างและการแยกวิเคราะห์รหัสรันไทม์

เรากำลังแยกโครงสร้าง EVM bytecode ของ Solidity smart contract อย่างง่าย

ในบทความที่แล้ว เราพบว่า bytecode ของ smart contract แบ่งออกเป็นสองส่วน: การสร้างและการดำเนินการ และเรารู้ว่าทำไมเราจึงทำเช่นนี้ หลังจากทำความเข้าใจอย่างลึกซึ้งในส่วนการสร้างแล้ว ก็ถึงเวลาที่จะเริ่มสำรวจส่วน รันไทม์เพิ่มขึ้น หากคุณดูไดอะแกรมการแยกโครงสร้าง ก่อนอื่นเราจะดูที่บล็อกแยกขนาดใหญ่ที่สองที่เรียกว่า BasicToken.evm (รันไทม์)

สิ่งนี้อาจดูน่ากลัวเล็กน้อยเนื่องจากโค้ดที่จะรันนั้นมีขนาดอย่างน้อยสี่เท่าของโค้ดที่จะสร้าง! แต่ไม่ต้องกังวล ทักษะที่เราได้พัฒนาขึ้นเพื่อทำความเข้าใจโค้ด EVM ในบทความก่อนหน้านี้ เมื่อรวมกับการใช้กลยุทธ์ "แบ่งแยกและพิชิต" ที่มั่นคงของเรา จะทำให้ความท้าทายนี้เป็นระบบมากขึ้นและอาจง่ายยิ่งขึ้นไปอีก นี่เป็นเพียงจุดเริ่มต้น เราจะยังคงระบุโครงสร้างอิสระต่อไป และแยกย่อยต่อไปจนกว่าจะแยกย่อยออกเป็นปัญหาที่แก้ไขได้

ก่อนอื่น กลับไปที่โปรแกรมแก้ไขออนไลน์ของ Remix และเริ่มเซสชันการดีบักด้วย runtime bytecode เราจะทำอย่างไร ครั้งล่าสุด เราได้ปรับใช้สัญญาอัจฉริยะและดีบักธุรกรรมการปรับใช้ ในครั้งนี้ เราจะใช้หนึ่งในฟังก์ชันเพื่อเชื่อมต่อกับสัญญาอัจฉริยะที่ปรับใช้และดีบักธุรกรรม

ก่อนอื่น เรามานึกถึงสัญญาอัจฉริยะของเรากัน:

เราเปิดใช้งาน Javascript VM พร้อมคอมไพเลอร์ที่ปรับให้เหมาะสม เวอร์ชัน v0.4.24 และ 10,000 เป็นตัวสำรองเริ่มต้น หลังจากปรับใช้สัญญาอัจฉริยะแล้ว คุณจะเห็นรายการดังกล่าวอยู่ในส่วน "สัญญาที่ปรับใช้" ของแผง "เรียกใช้" บน Remix คลิกเพื่อขยายเพื่อดูอินเทอร์เฟซของสัญญาอัจฉริยะ

อินเทอร์เฟซนี้คืออะไร เป็นรายการวิธีการสาธารณะหรือภายนอกทั้งหมดในสัญญาอัจฉริยะ -นั่นคือบัญชี Ethereum หรือสัญญาอัจฉริยะสามารถโต้ตอบกับมันได้ วิธีการส่วนตัวและภายในจะไม่แสดงที่นี่ วิธีโต้ตอบกับส่วนเฉพาะของโค้ดรันไทม์ของสัญญาอัจฉริยะจะเป็นจุดเน้นของการแยกส่วนของบทความนี้

เราสามารถทดลองได้โดยคลิกที่ปุ่ม totalSupply ในแผง "เรียกใช้" ของ Remix คุณควรเห็นการตอบกลับใต้ปุ่มทันที ซึ่งเป็นสิ่งที่เราคาดไว้เนื่องจากเราปรับใช้สัญญาอัจฉริยะเป็นการจัดหาโทเค็นเริ่มต้น ตอนนี้ในแผงคอนโซล ให้คลิกปุ่มดีบักเพื่อเริ่มเซสชันดีบักด้วยธุรกรรมเฉพาะนี้ โปรดทราบว่าจะมีปุ่ม Debug หลายปุ่มในแผงคอนโซล ตรวจสอบให้แน่ใจว่าคุณใช้เวอร์ชันล่าสุด

ในกรณีนี้ เราไม่ได้ดีบักการทำธุรกรรมไปยังที่อยู่ 0x0 ตามที่เราเห็นในโพสต์ก่อนหน้านี้ มีการสร้างสัญญาอัจฉริยะ เรากำลังดีบักการทำธุรกรรมกับสัญญาอัจฉริยะแทน นั่นคือรหัสรันไทม์ของมัน

หากแผงคำแนะนำปรากฏขึ้น คุณควรสามารถตรวจสอบได้ว่า Remix แสดงรายการคำแนะนำเดียวกันกับในส่วน BasicToken.evm (รันไทม์) ของกราฟการถอดรหัส หากไม่ตรงกัน แสดงว่ามีบางอย่างผิดพลาด ลองเริ่มต้นใหม่และตรวจสอบให้แน่ใจว่าคุณใช้การตั้งค่าที่ถูกต้องตามด้านบน

สิ่งแรกที่คุณอาจสังเกตเห็นคือดีบักเกอร์ให้คุณไปที่คำสั่ง 246 และแถบเลื่อนธุรกรรมอยู่ที่ประมาณ 60% ของ bytecode ทำไม เนื่องจาก Remix เป็นโปรแกรมที่มีประโยชน์มาก จึงนำคุณตรงไปยังส่วนของ EVM ที่กำลังจะเรียกใช้เนื้อความของฟังก์ชัน totalSupply อย่างไรก็ตาม มีหลายสิ่งหลายอย่างเกิดขึ้นก่อนหน้านั้น และนี่คือสิ่งที่เราควรทราบ อันที่จริง เราจะไม่ดูการทำงานของเนื้อความในบทความนี้ด้วยซ้ำข้อกังวลเดียวของเราคือวิธีที่รหัส EVM ที่สร้างโดย Solidity กำหนดเส้นทางธุรกรรมขาเข้า ซึ่งเป็นงานที่เราจะเข้าใจว่าเป็น "ตัวเลือกฟังก์ชัน" ของสัญญา

ดังนั้นให้จับตัวเลื่อนแล้วลากไปทางซ้ายจนสุดเพื่อเริ่มต้นที่คำสั่งศูนย์ ดังที่เราเห็นก่อนหน้านี้ EVM จะรันโค้ดจากคำสั่ง 0 เสมอ ไม่มีข้อยกเว้น จากนั้นจึงไหลผ่านโค้ดที่เหลือ มาดู opcodes เพื่อดำเนินการ opcode นี้

โครงสร้างแรกที่เกิดขึ้นคือโครงสร้างที่เราเคยเห็นมาก่อน (จริง ๆ แล้วเราจะเห็นอีกมาก):

รูปที่ 1 ตัวชี้หน่วยความจำว่าง

นี่คือสิ่งที่รหัส EVM ที่สร้างโดย Solidity จะทำเสมอก่อนที่จะเรียกใช้: บันทึกจุดในหน่วยความจำเพื่อใช้ในภายหลัง

มาดูกันว่าจะเกิดอะไรขึ้นต่อไป:

รูปที่ 2 ตรวจสอบความยาวของข้อมูลการโทร

หากคุณเปิดแผงสแต็กของ Remix ในแท็บ Debug และข้ามคำแนะนำที่ 5 ถึง 7 คุณจะเห็นว่าตอนนี้สแต็กประกอบด้วยตัวเลขสองครั้ง หากคุณมีปัญหาในการอ่านตัวเลขที่ยาวเป็นพิเศษ โปรดระวังว่าคุณปรับความกว้างของแผงดีบัก Remix เพื่อให้ตัวเลขพอดีกับบรรทัดเดียว อันแรกมาจากการพุชปกติ แต่อันที่สองเป็นผลมาจากการเรียกใช้งาน opcode ซึ่งตามกระดาษสีเหลืองบอกว่าไม่มีการโต้แย้งและส่งคืนขนาดของ "ข้อมูลอินพุตในสภาพแวดล้อมปัจจุบัน" หรือสิ่งที่เรามักจะ โทร ข้อมูลการโทร: 4CALLDATASIZE

ข้อมูลการโทรคืออะไร? ตามที่อธิบายไว้ในข้อกำหนด ABI เอกสารประกอบของ Solidity ข้อมูลการโทรเป็นบล็อกเข้ารหัสของตัวเลขฐานสิบหกที่มีข้อมูลเกี่ยวกับฟังก์ชันสัญญาอัจฉริยะที่เราต้องการเรียก และพารามิเตอร์หรือข้อมูล พูดง่ายๆ ก็คือ มันประกอบด้วย "รหัสฟังก์ชัน" ซึ่งสร้างขึ้นโดยการแฮชลายเซ็นของฟังก์ชัน (ตัดทอนเป็นสี่ไบต์แรก) แล้วบีบอัดข้อมูลพารามิเตอร์ คุณสามารถศึกษาลิงก์เอกสารอย่างละเอียดได้หากต้องการ แต่อย่ากังวลว่ากระดาษห่อนี้ทำงานอย่างไรจนถึงรายละเอียดที่ดีที่สุด มีการอธิบายไว้ในเอกสาร แต่บางครั้งอาจเข้าใจได้ยากสักหน่อย มันง่ายกว่ามากที่จะเข้าใจด้วยตัวอย่างที่ใช้งานได้จริง

มาดูกันว่าคอลดาต้านี้คืออะไร เปิดแผงข้อมูลการโทรในดีบักเกอร์ของ Remix และดูที่: 0x18160ddd นี่คือสี่ไบต์ที่สร้างขึ้นโดยการใช้อัลกอริทึมบนลายเซ็นของฟังก์ชัน keccak256 เป็นสตริง"totalSupply()"และดำเนินการตัดดังกล่าว เนื่องจากฟังก์ชันเฉพาะนี้ไม่มีพารามิเตอร์ ดังนั้นจึงเป็นเพียงแค่รหัสฟังก์ชันสี่ไบต์ เมื่อมีการเรียกใช้ CALLDATASIZE จะเพียงแค่กด 4 ตัวที่สองลงบนสแต็ก

จากนั้น คำสั่ง 8 LT ใช้เพื่อตรวจสอบว่าขนาดข้อมูลการโทรน้อยกว่า 4 ถ้าใช่ สองคำสั่งต่อไปนี้จะรันคำสั่ง JUMPI 86 (0x0056) นั่นน้อยกว่าสี่ไบต์ ดังนั้นจะไม่มีการกระโดดในกรณีนี้ และขั้นตอนการดำเนินการจะดำเนินการต่อไปยังคำสั่ง 13 แต่ก่อนที่เราจะทำเช่นนั้น สมมติว่าเราเรียกสัญญาอัจฉริยะของเราด้วยข้อมูลการโทรที่ว่างเปล่า ซึ่งก็คือ 0x0 แทน 0x18160ddd คุณไม่สามารถทำได้ด้วย Remix btw แต่คุณสามารถสร้างธุรกรรมได้ด้วยตนเอง

ในกรณีนี้ เราจะลงเอยด้วยคำสั่ง 86 ซึ่งโดยพื้นฐานแล้วจะมีการใส่เลขศูนย์สองสามตัวลงบนสแต็กและป้อนค่าเหล่านั้นไปยัง REVERT opcode ทำไม เนื่องจากสัญญาอัจฉริยะนี้ไม่มีฟังก์ชันสำรอง ถ้า bytecode ไม่รู้จักข้อมูลขาเข้า มันจะเปลี่ยนโฟลว์เป็นฟังก์ชันสำรอง และถ้าโครงสร้างนั้น "จับ" การเรียกไม่ได้ โครงสร้างการกู้คืนนี้จะยุติการดำเนินการโดยไม่มีการย้อนกลับอย่างแน่นอนหากไม่มีอะไรให้ส่งคืน แสดงว่าไม่ต้องทำอะไรและการโทรจะได้รับการกู้คืนอย่างสมบูรณ์

ตอนนี้มาทำสิ่งที่น่าสนใจกันดีกว่า กลับไปที่แท็บ Run ของ Remix คัดลอกที่อยู่บัญชี และใช้เป็นอาร์กิวเมนต์เพื่อเรียก balanceOf แทน totalSupply เพื่อดีบักธุรกรรม นี่คือเซสชันการดีบักใหม่ล่าสุด ลืมเรื่อง totalSupply ไปสักครู่ การนำทางไปยังคำแนะนำที่ 8 ตอนนี้ CALLDATASIZE พุช 36 (0x24) ไปยังสแต็ก หากคุณดูที่ calldata นั่นคือตอนนี้

ข้อมูลการโทรใหม่นี้แยกส่วนได้ง่ายมาก: สี่ไบต์แรก 70a08231 เป็นแฮชของลายเซ็น ตามด้วย"balanceOf(address)"32 ไบต์ประกอบด้วยที่อยู่ที่เราส่งเป็นอาร์กิวเมนต์ ทำไมต้องเป็น 32 ไบต์ หากที่อยู่ Ethereum มีความยาวเพียง 20 ไบต์ ผู้อ่านที่อยากรู้อยากเห็นอาจถาม ABI ใช้ "คำ" หรือ "ช่อง" ขนาด 32 ไบต์เสมอเพื่อเก็บอาร์กิวเมนต์ที่ใช้ในการเรียกใช้ฟังก์ชัน

ดำเนินการต่อในสภาพแวดล้อมการโทรที่สมดุล เรามาทำต่อจากจุดที่เราค้างไว้ที่คำแนะนำที่ 13 โดยไม่มีอะไรอยู่ในสแต็ก ณ จุดนี้ จากนั้นคำสั่ง 13 จะพุช 0xffffffff ลงบนสแต็ก และคำสั่งถัดไปจะพุชตัวเลข 0x000000001000...000 ยาว 29 ไบต์ไปยังสแต็ก เราจะดูว่าทำไมในอีกสักครู่ สำหรับตอนนี้ ให้สังเกตว่าอันหนึ่งมีสี่ไบต์และอีกอันหนึ่งมีสี่ไบต์ของ 0'

CALLDATALOAD ถัดไปใช้อาร์กิวเมนต์ (อันที่พุชไปยังสแต็กที่คำสั่ง 48) และอ่านก้อนขนาด 32 ไบต์จาก calldata ที่ตำแหน่งนั้น ซึ่งในกรณีนี้ใน Yul จะเป็น:

calldataload(0)

โดยทั่วไปจะผลักข้อมูลการโทรทั้งหมดของฉันไปยังสแต็ก ตอนนี้ส่วนที่สนุกมา DIV ใช้พารามิเตอร์สองตัวจากสแต็ก รับข้อมูลการโทรและหารด้วยหมายเลขแปลก ๆ 0x000000001000...000 กรองทุกอย่างได้อย่างมีประสิทธิภาพยกเว้นลายเซ็นของฟังก์ชันในข้อมูลการโทรและทิ้งไว้ในสแต็ก: 0x000...000070a08231 คำสั่งถัดไปใช้ AND ซึ่งใช้สององค์ประกอบบนสแต็กด้วย: รหัสฟังก์ชันของเราและหมายเลข f ที่มีสี่ไบต์ นี่คือเพื่อให้แน่ใจว่าแฮชลายเซ็นมีความยาวแปดไบต์พอดี และเพื่อปกปิดสิ่งอื่นใดหากมี ฉันคิดว่ามาตรการรักษาความปลอดภัยที่ใช้โดย Solidity

ในระยะสั้น เราแค่ตรวจสอบว่า calldata สั้นเกินไปหรือไม่ ถ้าใช่ ให้เปลี่ยนกลับและปรับปรุงเล็กน้อยเพื่อให้เรามีฟังก์ชันในสแต็ก

นอกจากนี้ เราเกือบเสร็จแล้ว ส่วนต่อไปจะเข้าใจง่าย:

รูปที่ 3 ตัวเลือกฟังก์ชั่น

ที่คำแนะนำ 53 รหัสจะพุช 18160ddd (รหัสฟังก์ชัน totalSuppy) ลงบนสแต็ก จากนั้นใช้ DUP2 เพื่อทำซ้ำค่า calldata ขาเข้าของ 70a08231 ซึ่งปัจจุบันอยู่ในตำแหน่งที่สองบนสแต็ก ทำไมต้องคัดลอก? เนื่องจาก opcode ที่คำสั่ง EQ 59 จะใช้สองค่าจากสแต็ก และเราต้องการเก็บค่า 70a08231 ไว้เนื่องจากเราประสบปัญหาในการแยกออกจาก calldata

ตอนนี้รหัสจะพยายามจับคู่รหัสฟังก์ชันใน calldata กับหนึ่งในรหัสฟังก์ชันที่รู้จัก ตั้งแต่ 70a08231 เข้ามา จะไม่ตรงกับ 18160ddd โดยข้าม JUMPI ที่คำสั่ง 63 แต่จะจับคู่ในการตรวจสอบถัดไป และข้ามไปที่ JUMPI ที่คำสั่ง 74

ลองมาสังเกตดูว่าการตรวจสอบความเท่าเทียมกันเหล่านี้ดำเนินการอย่างไรในแต่ละฟังก์ชันสาธารณะหรือภายนอกของสัญญาอัจฉริยะนี่คือแกนหลักของตัวเลือกฟังก์ชัน: ทำหน้าที่เป็นคำสั่งสวิตช์ที่เพียงแค่กำหนดเส้นทางการดำเนินการไปยังส่วนที่ถูกต้องของโค้ดของคุณ นี่คือ "ฮับ" ของเรา

ดังนั้น เนื่องจากกรณีสุดท้ายคือการจับคู่ โฟลว์การดำเนินการจึงนำเราไปยัง JUMPDEST ที่ตำแหน่ง 130 ซึ่งเราจะเห็นในส่วนถัดไปของซีรีส์นี้ว่า "wrapper" ของฟังก์ชัน balanceOf ดังที่เราจะเห็นในส่วนถัดไป ดังที่เราจะเห็นกันแล้วว่า wrapper นี้จะรับผิดชอบในการแกะข้อมูลของธุรกรรมสำหรับการใช้โดยตัวฟังก์ชัน

พยายามถ่ายโอนฟังก์ชันการดีบักนี้ต่อไป ไม่มีความลึกลับสำหรับตัวเลือกคุณสมบัติ เป็นโครงสร้างที่เรียบง่ายแต่มีประสิทธิภาพซึ่งอยู่ที่ประตูของทุกสัญญา (อย่างน้อยทั้งหมดที่รวบรวมจาก Solidity) และเปลี่ยนเส้นทางการดำเนินการไปยังตำแหน่งที่เหมาะสมในโค้ด นี่คือวิธีที่ Solidity ให้ bytecode ของสัญญาอัจฉริยะที่มีความสามารถในการจำลองจุดเข้าใช้งานหลายจุด และด้วยเหตุนี้จึงเป็นอินเทอร์เฟซ

เมื่อดูที่ไดอะแกรมการแยกโครงสร้าง นี่คือสิ่งที่เราเพิ่งแยกส่วน:

รูปที่ 4 ตัวเลือกฟังก์ชันและจุดเริ่มต้นหลักสำหรับรหัสรันไทม์ของสัญญาอัจฉริยะ

สรุปแล้ว ก่อนที่คุณจะรู้ คุณคุ้นเคยกับรหัสพื้นฐานของความแข็งแกร่งมากกว่าคนส่วนใหญ่ ติดกับมัน และคุณจะสามารถเปิดมันได้อย่างเต็มที่


*บทความนี้เผยแพร่ครั้งแรกบนสื่อโดย Alejandro Santander แปลและเรียบเรียงโดย Cheetah Blockchain*

Cheetah blockchain security ใช้เทคโนโลยีของ Kingsoft Internet Security รวมกับปัญญาประดิษฐ์, nlp และเทคโนโลยีอื่นๆ เพื่อให้ผู้ใช้ blockchain ได้รับบริการด้านความปลอดภัยทางระบบนิเวศ เช่น การตรวจสอบสัญญาและการวิเคราะห์ความรู้สึก

เว็บไซต์อย่างเป็นทางการของ Ratingtokenhttps://www.ratingtoken.net/?from=z

ยินดีต้อนรับเข้าร่วมชุมชนทางการของ Odaily
กลุ่มสมาชิก
https://t.me/Odaily_News
กลุ่มสนทนา
https://t.me/Odaily_CryptoPunk
บัญชีทางการ
https://twitter.com/OdailyChina
กลุ่มสนทนา
https://t.me/Odaily_CryptoPunk
ค้นหา
สารบัญบทความ
空头猎人
คลังบทความของผู้เขียน
猎豹区块链安全
ดาวน์โหลดแอพ Odaily พลาเน็ตเดลี่
ให้คนบางกลุ่มเข้าใจ Web3.0 ก่อน
IOS
Android