... dan belajar bekerja dengan alat pengembang Ethereum menggunakan contoh nyata.
Bagian nol: objek muncul
Saya baru saja menyelesaikan kuliah saya tentang kursus pengembangan aplikasi berbasis Ethereum terdesentralisasi dalam Soliditas dalam Bahasa Mandarin. Saya memberikannya di waktu luang saya untuk meningkatkan tingkat pengetahuan tentang blockchain dan kontrak pintar di antara komunitas pengembang Cina. Selama bekerja, saya berteman dengan beberapa siswa.
Dan tepat di akhir kursus, kami tiba-tiba menemukan diri kami dikelilingi oleh makhluk-makhluk ini:
Gambar dari cryptokitties.coSeperti kebanyakan orang yang mengalami fenomena ini, kami juga tidak bisa menahan cryptocreations lucu ini dan dengan cepat menjadi kecanduan permainan. Kami suka mengeluarkan kucing baru dan kami bahkan mengganti
metode itik dengan metode kripto . Saya percaya bahwa kecanduan game itu buruk, tetapi tidak dalam kasus ini, karena hasrat untuk membesarkan anak kucing dengan cepat membawa kita pada pertanyaan:
Bagaimana kucing crypto tertentu mendapatkan gen mereka?
Kami memutuskan untuk mencurahkan Sabtu malam untuk menemukan jawabannya, dan kami pikir kami berhasil membuat beberapa kemajuan dalam pengembangan perangkat lunak yang memungkinkan kami untuk menentukan mutasi genetik anak kucing kripto yang baru lahir sebelum mereka dilahirkan. Dengan kata lain, program ini dapat membantu Anda memeriksa dan menentukan waktu yang tepat untuk pembuahan ibu kucing, dan dengan demikian mendapatkan yang paling menarik dari kemungkinan mutasi.
Kami menerbitkan materi ini dengan harapan bahwa itu akan melayani semua orang sebagai artikel pengantar untuk berkenalan dengan alat pengembangan Ethereum yang sangat berguna, seperti halnya anak kucing crypto sendiri memungkinkan banyak orang yang tidak terbiasa dengan blockchain untuk bergabung dengan jajaran pengguna cryptocurrency.
Bagian satu: logika tingkat tinggi menghasilkan anak kucing kecil
Pertama-tama, kami bertanya pada diri sendiri: bagaimana kelahiran anak kucing crypto?
Untuk menjawab pertanyaan ini, kami menggunakan konduktor blockchain Etherscan yang luar biasa, yang memungkinkan kami melakukan lebih dari sekadar "mempelajari parameter dan isi blok". Jadi kami menemukan kode sumber untuk kontrak CryptoKittiesCore:
https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#codeHarap dicatat bahwa kontrak yang diperluas sebenarnya sedikit berbeda dari yang digunakan dalam program hadiah. Menurut kode ini, bayi kucing terbentuk dalam dua langkah: 1) induk kucing dibuahi oleh kucing; 2) beberapa saat kemudian, ketika periode pematangan janin berakhir, fungsi kelahiran kembali disebut. Fungsi ini biasanya dipanggil oleh setan proses tertentu, tetapi, seperti yang akan Anda lihat nanti, untuk mendapatkan mutasi yang menarik, Anda harus memilih dengan benar blok tempat anak kucing Anda dilahirkan.
function giveBirth(uint256 _matronId) external whenNotPaused returns(uint256) { Kitty storage matron = kitties[_matronId];
Dalam kode di atas, Anda dapat dengan jelas melihat bahwa gen anak kucing yang baru lahir ditentukan tepat pada saat kelahiran dengan memanggil fungsi mixGenes dari kontrak pintar eksternal genScience. Fungsi ini membutuhkan tiga parameter: gen ibu, gen ayah dan nomor blok di mana kucing akan siap untuk melahirkan.
Anda mungkin akan memiliki pertanyaan logis, mengapa gen tidak ditentukan pada saat pembuahan, seperti halnya di dunia nyata? Seperti yang akan Anda lihat dalam perjalanan narasi berikutnya, ini memungkinkan Anda untuk membela secara agak elegan dari upaya untuk memprediksi dan mendekripsi gen. Pendekatan ini menghilangkan kemungkinan prediksi gen kitten 100% akurat sebelum fakta kehamilan kucing-ibu dicatat dalam blockchain. Dan bahkan jika Anda bisa mengetahui kode pasti yang bertanggung jawab untuk pencampuran gen, ini tidak akan memberi Anda keuntungan apa pun.
Meski begitu, pada awalnya kami belum mengetahui hal ini, jadi mari kita lanjutkan. Sekarang kita perlu mencari tahu alamat kontrak genScience. Untuk melakukan ini, gunakan MyEtherWallet:
Alamat Kontrak GeneScienceInilah yang terlihat seperti bytecode kontrak:
0x60606040526004361061006c5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630d9f5aed81146100715780631597ee441461009f57806354c15b82146100ee57806361a769001461011557806377a74a201461017e575b600080fd5b341561007c57600080fd5b61008d6004356024356044356101cd565b604051908152602001604051809........
Dengan penampilannya, Anda tidak dapat mengatakan bahwa sebagai hasilnya, sesuatu yang lucu seperti anak kucing muncul di semua hal, tetapi kami sangat beruntung bahwa ini adalah alamat publik, dan kami tidak perlu mencarinya di repositori). Bahkan, kami percaya bahwa itu tidak boleh dibuat begitu mudah diakses. Jika pengembang benar-benar ingin memastikan alamat kontraknya benar, mereka harus menggunakan fungsi checkScienceAddress, tetapi kami tidak akan diganggu.
Bagian dua: runtuhnya hipotesis sederhana
Jadi apa yang ingin kita capai pada akhirnya? Harus dipahami bahwa kita tidak menetapkan tujuan untuk mengkompilasi bytecode, mengubahnya menjadi kode soliditas yang dapat dibaca manusia. Kita memerlukan metode yang murah (tanpa perlu membayar untuk transaksi dalam blockchain tempur) untuk menentukan gen anak kucing, asalkan kita tahu siapa orang tuanya. Ini yang akan kita lakukan.
Untuk memulai, mari gunakan
alat opcode Etherscan untuk analisis cepat. Ini terlihat seperti ini:
Jauh lebih jelasKami mengikuti aturan emas penguraian kode assembler: kami mulai dengan hipotesis sederhana dan berani tentang perilaku program dan, alih-alih mencoba memahami kerjanya secara keseluruhan, kami fokus pada mengonfirmasi asumsi yang dibuat. Kami akan membahas bytecode untuk menjawab beberapa pertanyaan:
- Apakah ini menggunakan stempel waktu? Tidak, karena opcode TIMESTAMP hilang. Jika ada kecelakaan sederhana di dalamnya, maka sumbernya pasti opcode lain.
- Apakah hash blok digunakan? Ya, BLOCKHASH terjadi dua kali. Oleh karena itu, keacakan, jika ada, dapat muncul dari opcode mereka, tetapi kami belum yakin tentang ini.
- Apakah ada hash yang digunakan sama sekali? Ya, ada SHA3. Namun, tidak jelas apa yang dia lakukan.
- Apakah pengirim SMS digunakan? Tidak, karena opcode Penelepon tidak ada. Oleh karena itu, tidak ada kontrol akses yang diterapkan pada kontrak.
- Apakah ada kontrak eksternal yang digunakan? Tidak, tidak ada PANGGILAN opcode.
- Apakah COINBASE digunakan? Tidak, dan karenanya kami mengecualikan sumber acak lain yang mungkin.
Setelah menerima jawaban untuk pertanyaan-pertanyaan ini, kami mengajukan dan bermaksud menguji hipotesis sederhana: hasil mixGene ditentukan oleh tiga dan hanya tiga parameter input dari fungsi ini. Jika demikian, maka kita bisa menggunakan kontrak ini secara lokal, terus memanggil fungsi ini dengan parameter yang menarik bagi kita, dan kemudian, mungkin, kita bisa mendapatkan kit gen kitten bahkan sebelum pembuahan induk kucing.
Untuk memverifikasi asumsi ini, kami memanggil fungsi mixGene di jaringan utama dengan tiga parameter acak: 1111115, 80, 40 dan mendapatkan beberapa hasil X. Selanjutnya, gunakan bytecode ini menggunakan
truffle dan testrpc . Jadi kemalasan kami mengarah pada cara truffle yang agak tidak standar.
contract GeneScienceSkeleton { function mixGenes(uint256 genes1, uint256 genes2, uint256 targetBlock) public returns (uint256) {} }
Kita mulai dengan kerangka kontrak, letakkan di struktur folder dari kerangka truffle dan jalankan kompilasi truffle. Namun, alih-alih secara langsung memindahkan kontrak kosong ini ke testrpc, kami mengganti bytecode kontrak di folder build dengan bytecode yang diperluas secara nyata dan bytecode kontrak genScience. Ini adalah cara yang tidak biasa namun cepat jika Anda ingin menggunakan kontrak dengan hanya bytecode dan beberapa antarmuka terbuka terbatas untuk pengujian lokal. Setelah itu, kami langsung memanggil Mixgenes dengan parameter 1111115, 80, 40, dan sayangnya kami mendapatkan kesalahan dengan jawaban kembali sebagai tanggapan. Ok, lihat lebih dalam. Seperti yang kita ketahui, tanda tangan dari fungsi mixGene adalah 0x0d9f5aed, jadi kami mengambil pena dan kertas dan melacak eksekusi bytecode, mulai dari titik masuk fungsi ini untuk memperhitungkan perubahan dalam tumpukan dan penyimpanan. Setelah beberapa JUMP, kami menemukan diri kami di sini:
[497] DUP1 [498] NUMBER [499] DUP14 [500] SWAP1 [501] GT [504] PUSH2 0x01fe [505] JUMPI [507] PUSH1 0x00 [508] DUP1 [509] 'fd'(Unknown Opcode)
Dilihat oleh isi dari baris-baris ini, jika jumlah blok saat ini kurang dari parameter ketiga, maka revert () dipanggil. Nah, ini perilaku yang cukup masuk akal: memanggil fungsi nyata dalam game dengan nomor blok dari masa depan tidak mungkin dan ini logis.
Verifikasi input ini mudah dielakkan: kami hanya menambang beberapa blok di testrpc dan memanggil fungsi lagi. Kali ini, fungsi berhasil mengembalikan Y.
Namun sayangnya X! = Y
Terlalu buruk Ini berarti bahwa hasil dari eksekusi fungsi tidak hanya bergantung pada parameter input, tetapi juga pada keadaan blockchain dari jaringan utama, yang, tentu saja, berbeda dari keadaan testrpc blockchain palsu.
Bagian Tiga: menyingsingkan lengan baju kami dan menggali ke dalam tumpukan
Baiklah Jadi sekarang saatnya menyingsingkan lengan baju Anda. Kertas tidak lagi cocok untuk melacak status tumpukan. Jadi untuk pekerjaan yang lebih serius, kami akan meluncurkan pembongkar EVM yang sangat berguna yang disebut
evmdis .
Dibandingkan dengan kertas dan pena, ini adalah langkah maju yang nyata. Mari kita lanjutkan dengan apa yang kita hentikan di bab terakhir. Berikut ini adalah kesimpulan yang menggembirakan dengan evmdis:
............. :label22 # Stack: [@0x70E @0x70E @0x70E 0x0 0x0 0x0 @0x88 @0x85 @0x82 :label3 @0x34] 0x1EB PUSH(0x0) 0x1ED DUP1 0x1EE DUP1 0x1EF DUP1 0x1F0 DUP1 0x1F1 DUP1 0x1F3 DUP13 0x1F9 JUMPI(:label23, NUMBER() > POP()) # Stack: [0x0 0x0 0x0 0x0 0x0 0x0 @0x70E @0x70E @0x70E 0x0 0x0 0x0 @0x88 @0x85 @0x82 :label3 @0x34] 0x1FA PUSH(0x0) 0x1FC DUP1 0x1FD REVERT() :label23 # Stack: [0x0 0x0 0x0 0x0 0x0 0x0 @0x70E @0x70E @0x70E 0x0 0x0 0x0 @0x88 @0x85 @0x82 :label3 @0x34] 0x1FF DUP13 0x200 PUSH(BLOCKHASH(POP())) 0x201 SWAP11 0x202 POP() 0x203 DUP11 0x209 JUMPI(:label25, !!POP()) # Stack: [0x0 0x0 0x0 0x0 0x0 0x0 @0x70E @0x70E @0x70E 0x0 @0x200 0x0 @0x88 @0x85 @0x82 :label3 @0x34] 0x20C DUP13 0x213 PUSH((NUMBER() & ~0xFF) + (POP() & 0xFF)) 0x214 SWAP13 0x215 POP() 0x217 DUP13 0x21E JUMPI(:label24, !!(POP() < NUMBER())) # Stack: [0x0 0x0 0x0 0x0 0x0 0x0 @0x70E @0x70E @0x70E 0x0 @0x200 0x0 @0x213 @0x85 @0x82 :label3 @0x34] 0x222 DUP13 0x223 PUSH(POP() - 0x100) 0x224 SWAP13 0x225 POP() :label24 # Stack: [0x0 0x0 0x0 0x0 0x0 0x0 @0x70E @0x70E @0x70E 0x0 @0x200 0x0 [@0x223 | @0x213] @0x85 @0x82 :label3 @0x34] 0x227 DUP13 0x228 PUSH(BLOCKHASH(POP())) 0x229 SWAP11 0x22A POP() :label25 # Stack: [0x0 0x0 0x0 0x0 0x0 0x0 @0x70E @0x70E @0x70E 0x0 [@0x200 | @0x228] 0x0 [@0x88 | @0x223 | @0x213] @0x85 @0x82 :label3 @0x34] 0x22C DUP11 0x22D DUP16 0x22E DUP16 ...........
Apa yang benar-benar baik untuk evmdis adalah kegunaannya untuk menganalisis JUMPDEST menjadi label yang tepat, yang tidak dapat ditaksir terlalu tinggi.
Jadi, setelah kita melewati persyaratan awal, kita menemukan diri kita pada label 23. Kita melihat DUP13 dan ingat dari bab sebelumnya bahwa angka 13 pada tumpukan adalah parameter ketiga kita. Jadi kami mencoba untuk mendapatkan BLOCKHASH dari parameter ketiga kami. Namun, tindakan BLOCKHASH terbatas pada 256 blok. Inilah sebabnya mengapa diikuti oleh JUMPI (ini adalah if-construct). Jika kita menerjemahkan logika opcodes ke dalam bahasa pseudo-code, kita mendapatkan sesuatu seperti ini:
func blockhash(p) { if (currentBlockNumber - p < 256) return hash(p); return 0; } var bhash = blockhash(thrid); if (bhash == 0) { thirdProjection = (currentBlockNumber & ~0xff) + (thridParam & 0xff); if (thirdProjection > currentBlockNumber) { thirdProjection -= 256; } thirdParam = thirdProjection; bhash = blockhash(thirdProjection); } label 25 and beyond ..... some more stuff related to thirdParam and bhash
beberapa hal lagi yang terkait dengan thirdParam dan bhash - kode lain yang terkait dengan thirdParam dan blok hash
Sekarang kami percaya bahwa kami telah menemukan alasan mengapa hasil kami berbeda dari yang kami amati di jaringan utama. Lebih penting lagi, kami tampaknya berhasil menemukan sumber peluang. Yaitu: hash blok dihitung berdasarkan parameter ketiga, atau
perkiraan parameter ketiga. Penting untuk dicatat bahwa dalam tumpukan parameter ketiga juga diganti dengan nomor blok yang diprediksi ini.
Jelas, selama eksekusi lokal di luar jaringan utama, kami tidak memiliki opsi sederhana untuk memaksakan pengembalian BLOCKHASH yang cocok dengan nilai-nilai jaringan utama. Bagaimanapun, karena kita tahu ketiga parameter, kita dapat dengan mudah memonitor jaringan utama dan mendapatkan hash dari blok H untuk parameter ketiga, serta hash dari blok yang diprediksi.
Selanjutnya, kita bisa memasukkan hash ini langsung ke kode byte di lingkungan pengujian lokal kita, dan jika semuanya berjalan sesuai rencana, kita akhirnya akan mendapatkan set gen yang benar.
Tetapi ada satu tangkapan: DUP13 dan BLOCKHASH hanya 2 byte dalam kode, dan jika kita hanya menggantinya dengan 33 byte PUSH32 0x * hash *, program counter akan sepenuhnya berubah dan kita harus memperbaiki masing-masing JUMP dan JUMPI. Atau kita harus membuat JUMP di akhir kode dan mengganti instruksi untuk kode yang digunakan, dan seterusnya.
Nah, karena kita sudah sejauh ini, kita akan mengendus sedikit lagi. Karena kita mendorong 32-byte hash non-zero ke if-branch, kondisinya akan selalu benar dan oleh karena itu, segala sesuatu yang ditulis di bagian lain dapat dengan mudah dibuang untuk memberikan ruang bagi hash 32-byte kita. Secara umum, inilah yang kami lakukan:

Poin kuncinya adalah karena kita mengabaikan bagian lain dari kondisi, kita perlu mengganti parameter input ketiga dari fungsi mixGene dengan perkiraan parameter ketiga sebelum memanggilnya.
Ini ke titik bahwa jika Anda mencoba untuk mendapatkan hasil dari suatu operasi
mixGene (X, Y, Z), di mana currentBlockNumber adalah Z <256, Anda hanya perlu mengganti hash PUSH32 dengan hash dari blok Z.
Namun, jika Anda berniat melakukan hal berikut
mixGene (X, Y, Z), di mana currentBlockNumber adalah Z ≥ 256, Anda harus mengganti hash PUSH32 dengan hash dari blok proj_Z, di mana proj_Z didefinisikan sebagai berikut:
proj_Z = (currentBlockNumber & ~0xff) + (Z & 0xff); if (proj_Z > currentBlockNumber) { proj_Z -= 256; } <b> Z proj_Z , mixGene(X, Y, proj_Z).</b>
Perhatikan bahwa proj_Z akan tetap tidak berubah dalam rentang blok tertentu. Misalnya, jika Z & 0xff = 128, maka proj_Z hanya berubah pada setiap blok nol dan 128.
Untuk mengkonfirmasi hipotesis ini dan memeriksa apakah ada jebakan di depan, kami mengubah bytecode dan menggunakan utilitas keren lain yang disebut
hevm .

Jika Anda belum pernah menggunakan hevm, saya sarankan Anda mencobanya. Alat ini tersedia bersama-sama dengan kerangka kerjanya sendiri, tetapi sebagian besar di set itu harus dicatat hal yang sangat berguna sebagai stack debugger interaktif.
Usage: hevm exec --code TEXT [--calldata TEXT] [--address ADDR] [--caller ADDR] [--origin ADDR] [--coinbase ADDR] [--value W256] [--gas W256] [--number W256] [--timestamp W256] [--gaslimit W256] [--gasprice W256] [--difficulty W256] [--debug] [--state STRING] Available options: -h,--help
Di atas adalah opsi peluncuran. Utilitas ini memungkinkan Anda menentukan berbagai parameter. Diantaranya adalah --debug, yang memberi Anda kemampuan untuk debug secara interaktif.
Jadi, di sini kami telah melakukan beberapa panggilan ke kontrak genScience yang digunakan pada blockchain jaringan utama dan mencatat hasilnya. Kemudian kami menggunakan hevm untuk menjalankan bytecode kami yang rusak dengan data yang disiapkan dengan mempertimbangkan aturan yang dijelaskan di atas dan ...
Hasilnya sama!
Bab terakhir: kesimpulan dan kelanjutan kerja (?)
Jadi apa yang bisa kita capai?
Dengan menggunakan perangkat lunak hack kami, Anda dapat 100% kemungkinan untuk memprediksi gen 256-bit untuk anak kucing yang baru lahir jika ia lahir dalam kisaran blok [coolDownEndBlock (ketika bayi siap muncul), blok saat ini adalah + 256 (kurang-lebih)]. Anda dapat mempertimbangkan hal ini sebagai berikut: ketika bayi berada di dalam rahim induk kucing, gen-nya bermutasi seiring waktu, karena sumber entropi dalam bentuk hash dari blok coolDownEndBlock yang diprediksi, yang juga berubah seiring waktu. Oleh karena itu, Anda dapat menggunakan program ini untuk memeriksa bagaimana gen bayi akan terlihat jika itu lahir saat ini. Dan jika Anda tidak menyukai gen ini, Anda bisa menunggu sekitar 256 blok lagi (rata-rata) dan memeriksa gen baru.
Seseorang mungkin mengatakan bahwa ini tidak cukup, karena hanya akurasi prediksi 100% dapat dianggap sebagai peretasan yang ideal bahkan sebelum kehamilan induk-kucing. Namun, ini tidak mungkin, karena gen anak kucing ditentukan tidak hanya oleh gen orang tuanya, tetapi juga oleh hash yang diprediksi dari blok sebagai faktor mutasi, yang tidak dapat diketahui sebelum pembuahan.
Apa yang bisa diperbaiki dan apa nuansa di sini?
Kami dengan cepat membahas perubahan yang terjadi pada tumpukan di bagian logis nyata dari kontrak pintar (label 25 dan semuanya setelah itu) dan kami percaya bahwa bagian yang dapat diprediksi dari kode mixGene ini baik untuk diuraikan dan dipelajari. Kami berharap bahwa hash blok sebagai faktor mutasi juga membawa beberapa signifikansi fisik, membantu, misalnya, menentukan gen mana yang harus dimutasi. Jika kita berhasil menemukan ini, kita akan mendapatkan gen aslinya, tanpa mutasi. Ini berguna karena jika Anda tidak memiliki gen sumber yang baik, maka bahkan mutasi terbaik mungkin tidak cukup.
Kami juga tidak mengukur korelasi antara gen 256-bit dan sifat-sifat anak kucing (warna mata, jenis ekor, dll.), Tetapi kami percaya bahwa ini sangat mungkin dilakukan dengan bantuan bot berkinerja tinggi dan penggolong sederhana.
Dan secara umum, kami sepenuhnya memahami maksud tim pengembangan CryptoKitties untuk menstabilkan mutasi dalam waktu singkat. Tetapi sisi lain dari pendekatan ini adalah kemampuan untuk melakukan analisis seperti yang kami lakukan.
Kami juga ingin mengucapkan terima kasih kepada komunitas ethereum yang luar biasa karena telah mengembangkan alat seperti Etherscan, hevm, evmdis, truffle, testrpc, myetherwallet, dan Solidity. Ini adalah komunitas yang sangat keren dan kami senang menjadi bagian darinya.
Dan akhirnya, kode yang dimodifikasi
https://github.com/modong/GeneScienceCracked/Ingatlah untuk mengubah $ CONSTBLOCKHASH $ ke hash dari blok yang diperkirakan.
