Лезем в опкод!
Со вчерашнего дня у меня в голове засела идея узнать больше про опкод в EVM. К тому же еще та задача из ethernaut не давала покоя. Поэтому я решил покопаться в этой теме чуть глубже.
К моему удивлению, про опкод и работу в ним довольно мало информации. Большинство из них приводят примеры списка всех опкодов, вот он. А как он работает никто не говорит.
Ну, как никто, скорее никто не говорит понятным языком. Да и какой "понятный язык" может быть в технической части.
Тем не менее, я нашел несколько интересных ресурсов про опкод и сегодня хочу сделать несколько постов по этой теме.
И для начала представляю вам очень хорошее видео с летней конференции в Нью-Йорке от разработчика в Macro. Да, видео на английском языке, но, если вы не понимаете, то можно включить довольно сносные субтитры. Более того на слайдах все достаточно понятно.
Видео: Демистификация опкода EVM
Приятного просмотра!
#evm #opcode
Со вчерашнего дня у меня в голове засела идея узнать больше про опкод в EVM. К тому же еще та задача из ethernaut не давала покоя. Поэтому я решил покопаться в этой теме чуть глубже.
К моему удивлению, про опкод и работу в ним довольно мало информации. Большинство из них приводят примеры списка всех опкодов, вот он. А как он работает никто не говорит.
Ну, как никто, скорее никто не говорит понятным языком. Да и какой "понятный язык" может быть в технической части.
Тем не менее, я нашел несколько интересных ресурсов про опкод и сегодня хочу сделать несколько постов по этой теме.
И для начала представляю вам очень хорошее видео с летней конференции в Нью-Йорке от разработчика в Macro. Да, видео на английском языке, но, если вы не понимаете, то можно включить довольно сносные субтитры. Более того на слайдах все достаточно понятно.
Видео: Демистификация опкода EVM
Приятного просмотра!
#evm #opcode
YouTube
🎤 Demystifying EVM Opcodes
Join Gilbert G of Macro for a talk titled, "Demystifying EVM Opcodes."
This workshop is part of ETHNewYork 2022, a three-day in person hackathon that will feature hackers, mentors, enthusiasts, sponsors and speakers who are all gathering in New York June…
This workshop is part of ETHNewYork 2022, a three-day in person hackathon that will feature hackers, mentors, enthusiasts, sponsors and speakers who are all gathering in New York June…
👍1
Опкод из байткода
Понравился этот слайд из видео, где объясняется конструкция опкода.
Если говорить кратко, то код нашего смарт контракта в сети Эфира представлен в виде байткода. Например, сложение (add) выглядит так:
0000 0001
В опкодах мы берем каждые 4 символа бинарного кода и приравниваем его к 1 символу в hex, и добавляем "0х", для указания EVM на то, что это конкретно hex код, а не какой-нибудь другой. И получается:
0х01
В итоге, все опкоды равны 1 байту, или 2 hex символам. Более того, каждому опкоду (этому hex) соответствует понятное для разработчика название.
Opcode 01 = name "Add"
Вот эти человекопонятные обозначения мы и видим в дебаггере Ремикса, когда проходим по шагам контракта.
#evm #opcode
Понравился этот слайд из видео, где объясняется конструкция опкода.
Если говорить кратко, то код нашего смарт контракта в сети Эфира представлен в виде байткода. Например, сложение (add) выглядит так:
0000 0001
В опкодах мы берем каждые 4 символа бинарного кода и приравниваем его к 1 символу в hex, и добавляем "0х", для указания EVM на то, что это конкретно hex код, а не какой-нибудь другой. И получается:
0х01
В итоге, все опкоды равны 1 байту, или 2 hex символам. Более того, каждому опкоду (этому hex) соответствует понятное для разработчика название.
Opcode 01 = name "Add"
Вот эти человекопонятные обозначения мы и видим в дебаггере Ремикса, когда проходим по шагам контракта.
#evm #opcode
Магические 6080604052
Уверен, никто не прочитал число в заголовке поста. А зря...
Это число является байткодом, с которого начинается байткод любого смарт контракта. Ну, иногда с легкими изменениями в 6060604052. Хотите узнать что это такое?
И это тоже относится к опкоду EVM. В другом формате это можно было бы записать так:
PUSH1 0x60 PUSH1 0x80 MSTORE
Как мы можем убедиться из таблицы опкодов, PUSH1 = 60, MSTORE = 52.
PUSH1 (0x60) - кладет 0х60 в Stack (мы же помним о форматах памяти?);
PUSH1 (0x60) - потом кладет 0х80 в Stack;
MSTORE (0x52) - берет 0х60 из памяти и перемещает в слот 0х80;
Если выполнить все эти действия по порядку, получится именно 6080604052.
Далее чуть сложнее.
0х80 и 0х60 не могут быть использованы, как простые числа 80 или 60. Так как они hexadecimal, то в переводе в decimal 60 будет ровняться 96, а 80 - 128.
Короче говоря, PUSH1 0x60 PUSH1 0x80 MSTORE берет 96 байтов памяти и перемещает указатель в начало 128 байта. Именно поэтому в памяти первые 64 байта всегда пустые, следующие 32 являются указателем, и потом уже идет запись контракта в саму память.
#evm #opcode
Уверен, никто не прочитал число в заголовке поста. А зря...
Это число является байткодом, с которого начинается байткод любого смарт контракта. Ну, иногда с легкими изменениями в 6060604052. Хотите узнать что это такое?
И это тоже относится к опкоду EVM. В другом формате это можно было бы записать так:
PUSH1 0x60 PUSH1 0x80 MSTORE
Как мы можем убедиться из таблицы опкодов, PUSH1 = 60, MSTORE = 52.
PUSH1 (0x60) - кладет 0х60 в Stack (мы же помним о форматах памяти?);
PUSH1 (0x60) - потом кладет 0х80 в Stack;
MSTORE (0x52) - берет 0х60 из памяти и перемещает в слот 0х80;
Если выполнить все эти действия по порядку, получится именно 6080604052.
Далее чуть сложнее.
0х80 и 0х60 не могут быть использованы, как простые числа 80 или 60. Так как они hexadecimal, то в переводе в decimal 60 будет ровняться 96, а 80 - 128.
Короче говоря, PUSH1 0x60 PUSH1 0x80 MSTORE берет 96 байтов памяти и перемещает указатель в начало 128 байта. Именно поэтому в памяти первые 64 байта всегда пустые, следующие 32 являются указателем, и потом уже идет запись контракта в саму память.
#evm #opcode
👍1
Задача из Ethernaut - Magiс Number. Часть 1
Напомню, что эту задачу мы оставили на потом, так как нужно было разобраться с опкодом. Теперь мы понимаем его чуть лучше, и можно попробовать разобрать задачу.
P.S. Уже не сегодня, но мы порой будем возвращаться к опкоду в будущем, когда появится более интересный материал по этой теме.
Итак, целью задачи было написать код смарт контракта, чтобы он возвращал нам число "42", при этом уложившись в 10 байтов. Это можно сделать только с помощью опкода. Поехали.
Мы помним, что 1 опкод = 1 байту, а число 42 в hex = 0х2а.
Нам потребуется два набора байткода:
1. Байткод инициализации: тот, который подготовит смарт контракт и вернет runtime байткод.
2. Runtime байткод: тот, который используется после создания контракта, и где лежат все наши функции.
Посмотри на runtime сперва.
Для того, чтобы поместить что-то в память нам потребуется минимум три действия: сделать push данных, сделать push места, выполнить mstore, который принимает значение первых двух.
Кладем значение в память:
1. 0x60 - PUSH1 --> PUSH(0x2a) --> 0x602a
2. 0x60 - PUSH1 --> PUSH(0x80) --> 0x6080
3. 0x52 - MSTORE --> MSTORE --> 0x52
И возвращаем его из памяти:
1. 0x60 - PUSH1 --> PUSH(0x20) --> 0x6020
(размер значение в 32 байтах)
2. 0x60 - PUSH1 --> PUSH(0x80) --> 0x6080
3. 0xf3 - RETURN --> RETURN --> 0xf3
RETURN еще один новый для нас опкод, который принимает значения: слота памяти, где хранится значение, а также длину / размер данного значение.
В итоге runtime байткод будет выглядеть так:
602a60805260206080f3
Все "0х" мы удаляем, не забыли?
#evm #opcode
Напомню, что эту задачу мы оставили на потом, так как нужно было разобраться с опкодом. Теперь мы понимаем его чуть лучше, и можно попробовать разобрать задачу.
P.S. Уже не сегодня, но мы порой будем возвращаться к опкоду в будущем, когда появится более интересный материал по этой теме.
Итак, целью задачи было написать код смарт контракта, чтобы он возвращал нам число "42", при этом уложившись в 10 байтов. Это можно сделать только с помощью опкода. Поехали.
Мы помним, что 1 опкод = 1 байту, а число 42 в hex = 0х2а.
Нам потребуется два набора байткода:
1. Байткод инициализации: тот, который подготовит смарт контракт и вернет runtime байткод.
2. Runtime байткод: тот, который используется после создания контракта, и где лежат все наши функции.
Посмотри на runtime сперва.
Для того, чтобы поместить что-то в память нам потребуется минимум три действия: сделать push данных, сделать push места, выполнить mstore, который принимает значение первых двух.
Кладем значение в память:
1. 0x60 - PUSH1 --> PUSH(0x2a) --> 0x602a
2. 0x60 - PUSH1 --> PUSH(0x80) --> 0x6080
3. 0x52 - MSTORE --> MSTORE --> 0x52
И возвращаем его из памяти:
1. 0x60 - PUSH1 --> PUSH(0x20) --> 0x6020
(размер значение в 32 байтах)
2. 0x60 - PUSH1 --> PUSH(0x80) --> 0x6080
3. 0xf3 - RETURN --> RETURN --> 0xf3
RETURN еще один новый для нас опкод, который принимает значения: слота памяти, где хранится значение, а также длину / размер данного значение.
В итоге runtime байткод будет выглядеть так:
602a60805260206080f3
Все "0х" мы удаляем, не забыли?
#evm #opcode
👍1
Задача из 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
Теперь разберем байткод инициализации. Он отвечает за загрузку нашего 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
👍2🔥1
Разбор байткода, opcodes, деплой
У Ильи на канале вышел новый урок про дебаггинг кода. Я уже на канале описывал этот процесс, но у лектора это все прекрасно показано и описано, что поймет даже новичок!
Видео урок.
Для тех, кто любит "мясные" уроки, это прям один из них! Крайне рекомендую к просмотру!
#debug #opcode #remix
У Ильи на канале вышел новый урок про дебаггинг кода. Я уже на канале описывал этот процесс, но у лектора это все прекрасно показано и описано, что поймет даже новичок!
Видео урок.
Для тех, кто любит "мясные" уроки, это прям один из них! Крайне рекомендую к просмотру!
#debug #opcode #remix
YouTube
Solidity и Ethereum, урок #38 | Разбор байткода, opcodes, деплой - идём на самый нижний уровень!
ХОТИТЕ СТАТЬ РАЗРАБОТЧИКОМ Solidity, узнать об Ethereum, блокчейне и многом другом ещё больше?!
Мои друзья из GUIDE DAO (бывшая школа MCS) предлагают скидку 0,1 ETH на ВСЕ СВОИ БУТКЕМЫ ПО КРИПТЕ! Материалы этих буткемов подготовлены мной и другими специалистами:…
Мои друзья из GUIDE DAO (бывшая школа MCS) предлагают скидку 0,1 ETH на ВСЕ СВОИ БУТКЕМЫ ПО КРИПТЕ! Материалы этих буткемов подготовлены мной и другими специалистами:…
👍2
Чуть больше о Yul (assembly)
Чем больше профессиональных контрактов я просматриваю, тем больше там встречается оптимизированного кода с assembly. Появляются даже целые библиотеки со сниппетами кода, который можно сразу вставлять к себе в контракт.
Именно поэтому понимать базовые основы для хорошего разработчика просто необходимо.
Я нашел интересную статью о yul для начинающих, хоть и на английском языке.
Очень длинная, но хорошо объясняющая базовые опкоды.
Оставлю ее здесь на самостоятельное изучение. А если захотите, сможем сделать день yul и разобрать статью по частям.
#yul #assembly #opcode
Чем больше профессиональных контрактов я просматриваю, тем больше там встречается оптимизированного кода с assembly. Появляются даже целые библиотеки со сниппетами кода, который можно сразу вставлять к себе в контракт.
Именно поэтому понимать базовые основы для хорошего разработчика просто необходимо.
Я нашел интересную статью о yul для начинающих, хоть и на английском языке.
Очень длинная, но хорошо объясняющая базовые опкоды.
Оставлю ее здесь на самостоятельное изучение. А если захотите, сможем сделать день yul и разобрать статью по частям.
#yul #assembly #opcode
👍6
Интересная библиотека SSTORE2
На основе ветки в Твиттере от chrisdior.eth.
В августе прошлого года в проекте Solady появилась новая библиотека SSTORE2, которая, по задумке, была призвана заменить использование дорогого по газу опкода SSTORE.
Например, когда мы хотим сохранить что-то в память, мы используем SSTORE, при этом затрачиваем минимум 20 000 газа за каждые 32 байта.
Библиотека SSTORE2 позволяет нам передавать данные в виде байткода контракта, используя опкод CREATE и затем читать данные через опкод EXTCODECOPY.
Вы также могли бы подумать, что в таблице опкодов стоимость CREATE составляет 32 000 газа, что в разы больше SSTORE. Но дело тут немного в другом.
С SSTORE2 вы передаете данные как байты, которые будут интерпретироваться как переменные, которые вы хотите объявить в новом контракте, а затем вы читаете этот байткод с помощью EXTCODECOPY, что стоит всего 2600 газа.
Таким образом, если вы хотите сохранить 8 переменных в памяти, с обычным опкодом SSTORE это будет стоить около 200 000 газа для записи плюс 20 000 для чтения. А с библиотекой SSTORE2 вы заплатите 88 000 газа для записи плюс 3200 для чтения. Существенная экономия, не так ли?
#opcode #sstore #sstore2 #library
На основе ветки в Твиттере от chrisdior.eth.
В августе прошлого года в проекте Solady появилась новая библиотека SSTORE2, которая, по задумке, была призвана заменить использование дорогого по газу опкода SSTORE.
Например, когда мы хотим сохранить что-то в память, мы используем SSTORE, при этом затрачиваем минимум 20 000 газа за каждые 32 байта.
Библиотека SSTORE2 позволяет нам передавать данные в виде байткода контракта, используя опкод CREATE и затем читать данные через опкод EXTCODECOPY.
Вы также могли бы подумать, что в таблице опкодов стоимость CREATE составляет 32 000 газа, что в разы больше SSTORE. Но дело тут немного в другом.
С SSTORE2 вы передаете данные как байты, которые будут интерпретироваться как переменные, которые вы хотите объявить в новом контракте, а затем вы читаете этот байткод с помощью EXTCODECOPY, что стоит всего 2600 газа.
Таким образом, если вы хотите сохранить 8 переменных в памяти, с обычным опкодом SSTORE это будет стоить около 200 000 газа для записи плюс 20 000 для чтения. А с библиотекой SSTORE2 вы заплатите 88 000 газа для записи плюс 3200 для чтения. Существенная экономия, не так ли?
#opcode #sstore #sstore2 #library
👍4❤3
Подборка статей для самообучения - 2
Вторая подборка статей из моего архива.
1. Немного о библиотеке Solmate's SafeTransferLib. Ее реализация немного отличается от привычной нам библиотеке от OpenZeppelin. При аудите следует обращать на некоторые особенные моменты, которые описываются в данной статье.
2. DAO Voting Vulnerabilities. Хорошая статья от MixBytes, которая рассказывает о популярных уязвимостях в проектах DAO.
3. Кратко о SMTChecker в Solidity и его конфигурации в Foundry. Еще не совсем разобрался, для каких практических целей это может потребоваться, но где-то чую, что может быть полезно для изучения.
4. gasLeft() exploit. Показан пример решения одной из задач в Ethernaut альтернативным способом через внутреннюю функцию проверки остатков газа. Не обычный способ, показывающий, что всегда есть как минимум еще один способ для решения любой задачи. P.S. Обратите внимание на другие статьи от данного автора.
5. Видео об уязвимости в ZK. Популярная сейчас тема, поэтому основы знать нужно.
6. Опкоды с необычным "поведением". Ветка от jtriley, где собраны описания опкодов, которые могут вести себя не так, как запланировано. Будет сложно даже для продвинутых разработчиков.
7. 35 репозиториев для разработчиков. Простая подборка с полезными репо.
Как вы видите, очень много материала можно найти в зарубежных источниках. Если кто-то делает их перевод, дайте мне знать, обязательно будут делиться на канале.
#scope #dao #zk #solmate #opcode
Вторая подборка статей из моего архива.
1. Немного о библиотеке Solmate's SafeTransferLib. Ее реализация немного отличается от привычной нам библиотеке от OpenZeppelin. При аудите следует обращать на некоторые особенные моменты, которые описываются в данной статье.
2. DAO Voting Vulnerabilities. Хорошая статья от MixBytes, которая рассказывает о популярных уязвимостях в проектах DAO.
3. Кратко о SMTChecker в Solidity и его конфигурации в Foundry. Еще не совсем разобрался, для каких практических целей это может потребоваться, но где-то чую, что может быть полезно для изучения.
4. gasLeft() exploit. Показан пример решения одной из задач в Ethernaut альтернативным способом через внутреннюю функцию проверки остатков газа. Не обычный способ, показывающий, что всегда есть как минимум еще один способ для решения любой задачи. P.S. Обратите внимание на другие статьи от данного автора.
5. Видео об уязвимости в ZK. Популярная сейчас тема, поэтому основы знать нужно.
6. Опкоды с необычным "поведением". Ветка от jtriley, где собраны описания опкодов, которые могут вести себя не так, как запланировано. Будет сложно даже для продвинутых разработчиков.
7. 35 репозиториев для разработчиков. Простая подборка с полезными репо.
Как вы видите, очень много материала можно найти в зарубежных источниках. Если кто-то делает их перевод, дайте мне знать, обязательно будут делиться на канале.
#scope #dao #zk #solmate #opcode
❤2🔥2