Tôi đã học được gì từ kỹ thuật viết mã của ông chủ MEV?
Sự phức tạp của mã kết hợp với cách trình bày nghệ thuật đã dẫn đến một trong những tình trạng mã hóa phức tạp nhất mà tôi từng thấy.
Tựa gốc: "To Reverse A Big Brain"
Tác giả gốc: @jtriley_eth, twitter
Bản gốc biên soạn: zhouzhou, BlockBeats
Ghi chú của biên tập viên: Ngược lại Trong quá trình hợp đồng MEV của bigbrainchad.eth, chúng tôi nhận thấy rằng nó sử dụng các kỹ thuật làm xáo trộn mã phức tạp và phá vỡ ngăn xếp, gây khó khăn cho các công cụ tự động xử lý. Có rất nhiều khối và vòng lặp mã không cần thiết trong hợp đồng và độ phức tạp còn tăng thêm nhờ các bước nhảy động và kiểm soát dấu thời gian. Mặc dù sử dụng các công cụ như Heimdall và Tenderly, tiến độ vẫn còn hạn chế và cuối cùng chúng tôi đã chuyển sang Python để phân tích thủ công, cố gắng tìm ra những đột phá thông qua phân tích cú pháp từng khối và chú thích ngăn xếp. Lần này, kỹ thuật đảo ngược gặp nhiều thách thức nhưng vẫn chưa được giải mã hoàn toàn. .
Bigbrainchad.eth là tài khoản chạy hợp đồng MEV, trong đó mỗi giao dịch Tất cả các hàm băm đều bắt đầu bằng 0xbeef, mỗi lệnh gọi hàm bắt đầu bằng codeIsLaw và mỗi đầu vào giao dịch bắt đầu bằng 0xdeadbeef. Ở mức độ cao, nó là một máy đa năng. Nhưng bên dưới, sự phức tạp của mã kết hợp với cách thể hiện nghệ thuật đã dẫn đến một trong những tình trạng mã hóa phức tạp nhất mà tôi từng thấy.
Tấn công ban đầu
Để đảo ngược hợp đồng thông minh, trước tiên chúng tôi bắt đầu với các công cụ tự động. Mọi kỹ sư đảo ngược đều lãng phí rất nhiều thời gian vào các nhiệm vụ mà các công cụ có thể tự động hóa. Vì vậy, chúng ta sẽ bắt đầu với một số công cụ phổ biến.
Heimdall
Heimdall là người bảo vệ Cầu Cầu vồng trong thần thoại Bắc Âu và trong Ethereum Trong thế giới Python, nó là một công cụ dịch mã bytecode. Heimdall cung cấp nhiều cấp độ dịch ngược, vì vậy chúng ta sẽ bắt đầu với cấp độ cao nhất, Solidity.
Cần lưu ý rằng đầu ra của Heimdall không phải là Solidity "hợp lệ" mà nói chung có thể được biên dịch. Nó giống một công cụ heuristic để giúp chúng ta tốt hơn. Hiểu luồng điều khiển cấp cao.
heimdall dịch ngược bytecode.hex --include-sol
Có đơn giản vậy không? Điều đó không dễ dàng chút nào, có một số byte rỗng được trộn vào chuỗi, đây có thể là sự sơ suất của bigbrainchad, người đã viết quá nhiều mã hợp ngữ. Hãy tìm hiểu sâu hơn một chút và nhìn vào Yul.
heimdall dịch ngược bytecode.hex --include-yul
Bạn đã bao giờ có cảm giác này: Những gì bạn nghĩ là một dự án nhỏ thú vị bỗng nhiên rơi ra khỏi tầm kiểm soát và đi sâu vào EVM (Trong vực thẳm của Máy ảo Ethereum (EVM), cả ngày bạn dự định bỗng chốc biến mất như "Anh Stark, tôi thấy không ổn"?
Sản phẩm của Solidity và Yul hoàn toàn khác nhau. Một số điều kiện nhất định được đáp ứng, chẳng hạn như calldatasize khác 0 hoặc khớp người khởi tạo giao dịch và người gọi, nhưng những điều kiện này không gây ra hiện tượng khôi phục (hoàn nguyên) trong Yul. Hãy đi sâu hơn một chút và nhập cái được gọi là Biểu đồ luồng điều khiển.
heimdall cfg bytecode.hex
Biểu đồ luồng điều khiển chia mã byte của hợp đồng thông minh thành nhiều khối, phân chia dựa trên vị trí mã chọn các đường dẫn khác nhau, đây có thể là lúc bạn cần; một lỗi nhất định có điều kiện khôi phục một giao dịch, nó cũng có thể là một điều kiện ngưỡng tương tự như phép toán đánh dấu trong Uniswap V3. Nội dung hoàn chỉnh được trình chiếu ở trên nhằm mục đích nâng cao kịch tính nhưng ẩn chứa một vấn đề tế nhị trong đó.
Lệnh jumpi là lệnh nhảy có điều kiện. Nó báo cho EVM (Máy ảo Ethereum) rằng "nếu" một điều kiện nhất định khác 0 thì "nhảy". " tới Mã kia tiếp tục thực thi, nếu không thì nó tiếp tục như không có chuyện gì xảy ra. Thông thường, khối này sẽ xuất hiện dưới dạng một khối trỏ đến hai khối khác, một khối biểu thị điều kiện "đúng" và khối còn lại biểu thị điều kiện "sai", tương tự như sau.
Nếu điều kiện là "true", hãy khôi phục giao dịch, nếu không thì trả về thành công. Điều này rất phổ biến trong các câu lệnh yêu cầu của Solidity.
Điều này thật tuyệt vời và giúp chúng ta vẽ sơ đồ luồng điều khiển, nhưng có một vấn đề nhỏ. Nếu chúng ta nhìn vào khối trước của biểu đồ luồng điều khiển, chúng ta sẽ thấy như sau.
Không thể có bước nhảy có điều kiện chỉ với một kết quả trong mã byte.
Chúng ta thấy rằng chỉ có một kết quả duy nhất sau khi nhảy có điều kiện vào một khối. Mặc dù về mặt kỹ thuật, điều này có thể thực hiện được trong các ngôn ngữ cấp cao, nhưng nó không xảy ra với mã byte. Mỗi lệnh jumpi phải có hai kết quả và ngay cả khi một trong số chúng hoàn toàn không hợp lệ, nó vẫn phải được phản ánh trong mã. Vì vậy, chính xác những gì đang xảy ra?
Bây giờ chúng ta hãy đi sâu hơn một chút vào cuộc thảo luận. Nói chung, các ngôn ngữ cấp cao như Solidity hay Vyper đều tự xử lý tất cả logic luồng điều khiển, điều đó có nghĩa là các lệnh nhảy hoặc jumpi không thể được sử dụng trực tiếp. Họ chỉ có thể sử dụng các cấu trúc như lệnh gọi if, switch hoặc hàm, ngay cả trong "tập hợp nội tuyến" của Solidity (còn được gọi là biểu diễn trung gian, Yul).
Điều này có nghĩa là tất cả các lệnh nhảy và jumpi đều được biết tại thời điểm biên dịch, chúng không thể đến từ dữ liệu không thể đoán trước như calldata (đó là lý do tại sao chúng ta thấy ở đây). Điều này ngụ ý rằng hợp đồng được viết bằng hợp ngữ hoặc sử dụng phiên bản Solidity cũ hơn trước khi giới thiệu trình tối ưu hóa Yul. Nói đúng ra, nếu bạn không sử dụng đường dẫn Yul, bạn có thể ghi đè con trỏ hàm thông qua calldata để đạt được hiệu ứng này, nhưng trong trường hợp sử dụng "via-ir" (thông qua biểu diễn trung gian), ngay cả con trỏ hàm cũng không còn trực tiếp nữa Thay vào đó, mục tiêu nhảy được phân phối theo ID hàm.
Trong cả hai trường hợp, Heimdall không thể giúp chúng tôi thêm nữa và chúng tôi cần tiếp tục khám phá các công cụ khác.
Dịu dàng
Dịu dàng đưa ra một số giao dịch Theo dõi thông tin và đã đóng một vai trò quan trọng trong việc phục hồi và ứng phó với lỗ hổng trong quá khứ. Hãy xem liệu chúng ta có thể có được cái nhìn sâu sắc có ý nghĩa về cách thức hoạt động của chương trình này hay không. Chúng tôi sẽ sử dụng hàm băm giao dịch sau: 0xbeef0ad930c2f0052758ce9ce579ad7f83bed913ffedb50046c3c674293d1fe5
Bảng điều khiển nhẹ nhàng
Không có mã nguồn nên không có mã nguồn nó không thể được thực hiện gỡ lỗi. Tuy nhiên, nó bao gồm thông tin cuộc gọi ở mức cao. Mặc dù những điều này có thể hữu ích sau này nhưng bây giờ chúng ta cần các bước điều khiển thực tế để xem điều gì thực sự đang diễn ra bên dưới.
Bytegraph
Tôi chưa từng nghe nói đến ứng dụng này trước đây nhưng nó cũng cung cấp biểu đồ luồng điều khiển trông thú vị hơn và thậm chí có thể được thao tác bằng cách kéo và thả. Hãy nhập mã byte của hợp đồng vào đó và xem chúng tôi tìm thấy gì.
Bytegraph
Thành thật mà nói, ở đây còn có các khối khác và có thể tôi chưa giỏi sử dụng công cụ này lắm nhưng tôi sẽ không tìm và kết nối các nút này theo cách thủ công . Hãy tiếp tục nào.
Mã EVM
Tôi đã đưa ra rất nhiều đánh giá tốt về mã chấm EVM trước đây (và vẫn làm). Nó chứa thông tin về từng opcode EVM (nếu bạn muốn tìm hiểu sâu hơn, tôi khuyên bạn nên đọc từng cái một) và cũng có một sân chơi nơi bạn có thể kiểm tra các opcode. Hãy thử chèn mã byte và dữ liệu cuộc gọi để thực hiện giao dịch mà chúng tôi đã đề cập trước đó.
evm.codes
Chúng tôi đã gặp phải lỗi khôi phục thời gian chạy khi thực hiện lại giao dịch trước đó. Mặc dù không hiển thị ở đây nhưng thông báo lỗi chỉ có một từ: "thời gian". Hóa ra việc kiểm tra này làm giảm dấu thời gian của khối xuống còn 16 bit, sau đó trừ byte 5 và 6 của dữ liệu cuộc gọi từ đó. Theo như tôi biết, Mã EVM không có tính năng ghi đè biến môi trường nên lần thử này không thành công.
Python3 và một giấc mơ
Cho đến nay, tiến độ vẫn còn rất hạn chế, vì vậy hãy thử Python. Chúng tôi sẽ chia nhỏ mã theo từng khối, chia cho jumpdest, jumpi, nhảy, dừng, hoàn nguyên và không hợp lệ. Bây giờ chúng ta hãy viết nội dung này vào một tệp, sau đó bắt đầu thêm một số nhận xét ngăn xếp, hy vọng tìm thấy bước đột phá.
Nhận xét biểu thị trạng thái ngăn xếp
Quá trình này thật khó khăn nhưng có một số điều đã trở nên rõ ràng.
Trước hết, việc lập kế hoạch ngăn xếp rất khó hiểu, điều này chỉ có thể là do phiên bản cũ của Solidity hoặc sự nhầm lẫn về mã mà chúng tôi gặp phải lần đầu tiên - ngăn xếp là xáo trộn .
Xáo trộn ngăn xếp là một phương tiện cố tình đánh lừa các kỹ sư đảo ngược bằng cách thay đổi thứ tự các biến được thêm, di chuyển và sử dụng trên ngăn xếp. Ví dụ: hãy xem xét hai đoạn mã sau, chúng thực hiện chính xác điều tương tự.
Đoạn mã đầu tiên rất đơn giản, chúng tôi đẩy đầu vào vào mstore để lưu trữ một giá trị trong bộ nhớ và sau đó đầu vào được đẩy để trả về, nó trả về một phần bộ nhớ cho người gọi. Đoạn mã thứ hai thực hiện điều tương tự nhưng theo cách ít trực tiếp hơn.
Thật không may, kỹ thuật xáo trộn ngăn xếp của Bigbrainchad phức tạp hơn nhiều.
Ngoài ra còn có một kỹ thuật mã hóa mã giúp chia các hằng số thành các giá trị khác và thực hiện các phép tính số học trong thời gian chạy để tạo ra các hằng số thực tế. Kết hợp kỹ thuật này với xáo trộn ngăn xếp, bạn sẽ thấy rằng 30 giá trị dường như vô dụng được tích lũy trong ngăn xếp, nhưng khi chương trình chạy, các giá trị này sẽ được tập hợp lại từng giá trị một thành các giá trị chương trình thực sự cần. Mọi người phát điên.
Công cụ huyền thoại xuất hiện: GOAT
Trong quá trình đó, sau vài giờ tôi Sau khi nhận được tin nhắn, @plotchy đã phát triển một công cụ tạo biểu đồ luồng điều khiển thông qua thực thi biểu tượng. Công cụ này không chỉ thực hiện công việc của các công cụ biểu đồ luồng điều khiển khác mà còn có thể ánh xạ các bước nhảy gián tiếp bắt nguồn từ dữ liệu như calldata. Sau một số điều chỉnh phụ thuộc, chúng tôi nhận được kết quả như hình bên dưới.
evm-cfg bytecode.hex -o âm mưu /cfg.dot --open
Tuyệt vời! Mặc dù hình ảnh đầy đủ nhằm mục đích tăng thêm kịch tính nhưng nó cũng bao gồm một số điểm nhấn thú vị để giúp chúng tôi tìm ra các đường dẫn mã thành công và thất bại, đồng thời đánh dấu rõ ràng nhánh nào là đúng và nhánh nào là giả. Tuy nhiên, điều này không phải lúc nào cũng rõ ràng.
Tuy nhiên, điều này cũng dẫn chúng ta đến các kỹ thuật che giấu mã thứ hai và thứ ba mà chúng ta gặp phải.
Trước hết, có rất nhiều khối mã không cần thiết. Một số bước nhảy chỉ xảy ra một lần và chỉ có một khối mã được tham chiếu. Có thể đây là sự cố với các phiên bản Solidity cũ hơn đã chèn nhiều bloatware như Microsoft đã làm hoặc có thể có một số mã khó hiểu ở đây.
Thứ hai, có rất nhiều vòng lặp. Một số vòng lặp chỉ xảy ra khi một byte dữ liệu cuộc gọi cụ thể có một giá trị cụ thể, một số vòng lặp khác là kết quả của các bước nhảy động được đề cập ở trên và dường như có tới mười vòng lặp trong số đó. Một số vòng lặp ngắn, trong khi những vòng khác chạy gần như xuyên suốt toàn bộ hợp đồng. Có thể tôi có một số thuyết âm mưu, nhưng một số trong số đó có thể chỉ là hũ mật ong.
Vòng lặp EVM-CFG
Cuộc tấn công thứ hai
Cho đến nay, ngoài việc phát hiện ra một số kỹ thuật che giấu mã rất tinh vi Ngoại trừ có thể là “chiến tranh tâm lý”, chưa đạt được nhiều thành công. Vậy hãy thử một góc độ khác. Chúng ta sẽ quay lại phần Tenderly của giao dịch được đề cập trước đó và cố gắng suy ra điều gì đã xảy ra với nó từ thông tin theo dõi cấp cao.
Dữ liệu cuộc gọi của giao dịch này như sau:
Trong giao dịch này, hợp đồng MEV chỉ thực hiện hai thao tác.
Thao tác đầu tiên gọi hàm phân phốiETH của mã thông báo và chuyển giá trị bằng 0. Vì hợp đồng là chung nên không thể lưu trữ mọi địa chỉ hợp đồng, bộ chọn và giá trị cuộc gọi mà phải được đưa vào qua calldata.
Thao tác thứ hai gọi người gọi ban đầu là bigbrainchad.eth. Lần này không có dữ liệu cuộc gọi nhưng có giá trị cuộc gọi.
Xem xét những điều này, chúng ta có thể phân tách dữ liệu cuộc gọi như sau.
Bốn byte đầu tiên là trang trí, hợp đồng bỏ qua chúng. Các lệnh gọi trước đây đến hợp đồng này thực tế đã sử dụng tất cả các số 0 0x00000000, vì vậy một số yếu tố cách điệu đã dần được thêm vào trong quá trình tối đa hóa hiệu quả. Hai byte tiếp theo tương ứng với luồng kiểm soát dấu thời gian được đề cập trước đó; điều này rất có thể đảm bảo rằng các giao dịch sẽ chỉ được đưa vào khối dự kiến và dấu thời gian dự kiến. Byte tiếp theo liên quan đến honeypot luồng điều khiển mà tôi đã đề cập trước đó; có thể nó có mục đích quan trọng hơn, nhưng ngăn xếp quá sâu và tôi cảm thấy mệt mỏi. Sau đó có một số byte không xác định.
Điều thực sự thú vị là tham số "kích thước địa chỉ đích". Có kích thước tiền tố cho từng địa chỉ đích, giá trị và tải trọng của cuộc gọi. Chúng sử dụng một byte để biểu thị kích thước của địa chỉ đích và giá trị và hai byte để biểu thị kích thước của tải trọng. Lệnh gọi đầu tiên trong chuỗi là dành cho 0xf193..65b1 với giá trị bằng 0 và tải trọng là 0xb8b9b54900, phân phốiETH(). Lệnh gọi thứ hai trong chuỗi là tới 0xd215..0dfd, bigbrainchad.eth, với giá trị là 281314951393027 wei và không có dữ liệu cuộc gọi.
Tóm tắt
Tôi muốn hoàn thành công việc này đến mức nào, hãy phát hành một số mã nguồn và Thực hiện một hợp đồng bắt chước, nhưng hợp đồng này đã thắng tôi. Có quá nhiều thứ cần giải nén, quá nhiều đường dẫn mã để theo dõi thủ công và quá nhiều điều mơ hồ đối với các công cụ tự động. Nếu bạn muốn xem ghi chú từ tác phẩm của tôi, bạn có thể làm như vậy thông qua liên kết GitHub bên dưới, nhưng hiện tại, tôi sẽ từ bỏ điều đó.
Nếu bigbrainchad.eth nhìn thấy điều này thì chúc may mắn, hợp đồng của bạn thực sự là một tác phẩm nghệ thuật.
Tuyên bố miễn trừ trách nhiệm: Mọi thông tin trong bài viết đều thể hiện quan điểm của tác giả và không liên quan đến nền tảng. Bài viết này không nhằm mục đích tham khảo để đưa ra quyết định đầu tư.
Bạn cũng có thể thích
VanEck chỉ trích lập trường lỗi thời của Bộ Tài chính Mỹ về stablecoin
DWF Labs xem xét khởi kiện cựu đối tác Eugene Ng vì cáo buộc đánh thuốc mê
Phân Tích Giá Celestia (TIA): TIA Sẽ Bứt Phá Hay Sụt Giảm?
TIA giữ mức hỗ trợ $4,68, nhắm đến $4,80 khi các nhà giao dịch cân nhắc tiềm năng phục hồi tăng giá Thị trường tạm dừng trên TIA khi khối lượng giảm báo hiệu sự thận trọng trong phiên giao dịch biến động Các lệnh mua dài hạn đối mặt với $3 triệu thanh lý khi phạm vi của TIA gặp khó khăn trong việc kiểm tra tâm lý nhà giao dịch
Danh mục đầu tư tiền điện tử trị giá 800 triệu USD của Florida: Giám đốc tài chính ủng hộ chiến lược Bitcoin
Giám đốc Tài chính của Florida xác nhận rằng tiểu bang đang nắm giữ khoảng 800 triệu đô la trong tiền điện tử. Giám đốc Tài chính tin rằng nhiệm kỳ tổng thống của Donald Trump sẽ thúc đẩy các khoản đầu tư liên quan đến tiền điện tử của Mỹ. Jimmy Patronis đã yêu cầu Florida đầu tư một phần quỹ hưu trí của mình vào tiền điện tử.