Задача из Ethernaut - Magiс Number. Часть 2
Теперь разберем байткод инициализации. Он отвечает за загрузку нашего runtime кода в память и возвращает его в EVM.
Для этого нам потребуется скопировать код с помощью опкода CODECOPY, который принимает 3 значения:
1. Слот назначения, куда код будет помещен в памяти, для примера возьмем 0х00.
2. Текущая позиция опкода runtime, которую мы не знаем в данный момент.
3. Размер нашего кода в байтах, и его длина сейчас ровно 10 байтов.
Вот так это выглядит:
1. 0x60 - PUSH1 --> PUSH(0x0a) --> 0x600a
(0x0a - это размер нашего кода - 10 байтов)
2. 0x60 - PUSH1 --> PUSH(0x??) --> 0x60??
(?? - позиция, которую мы еще не знаем)
3. 0x60 - PUSH1 --> PUSH(0x00) --> 0x6000
(0x00 выбранный нами ранее слот памяти)
4. 0x39 - CODECOPY --> CODECOPY --> 0x39
Далее возвращаем этот код:
1. 0x60 - PUSH1 --> PUSH(0x0a) --> 0x600a
(размер нашего опкода в 10 байтов)
2. 0x60 - PUSH1 --> PUSH(0x00) --> 0x6000
(значение было сохранено в слоту 0х00)
3. 0xf3 - RETURN --> RETURN --> 0xf3
(возвращает значение в 0х00 и длинной в 0х0а)
Если сложить текущий опкод, мы получим 600a60__600039600a6000f3. Он будет равняться 12 байтам. Это означает, что мы нашли недостающее значение, которое мы ранее пометили, как ??.
12 или 0x0c в hex позволяет закончить создание нашего кода:
600a600c600039600a6000f3
Теперь можно сложить два байткода и получить то, что потребуется нам для решения:
602a60805260206080f3 + 600a600c600039600a6000f3 = 600a600c600039600a6000f3602a60505260206050f3
Говоря кратно, решение задачи можно теперь представить так:
bytes memory code = "\x60\x0a\x60\x0c\x60\x00\x39\x60\x0a\x60\x00\xf3\x60\x2a\x60\x80\x52\x60\x20\x60\x80\xf3";
address solver;
assembly {
solver := create(0, add(code, 0x20), mload(code))
}
Мы используем assembly для создания контракта через create, который принимает три параметра: значение, положение и длину, возвращая адрес контракта после его деплоя.
Надеюсь, эта задача и работа с опкодом стала для вас чуточку понятнее.
#evm #opcode
Post #495
316
- 👍 2
- 🔥 1