Участник:SkudarnovYaroslav/Лекция по ассемблеру за 3 марта — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м (Регистры)
(Арифметические команды: так читабельнее)
 
(не показано 7 промежуточных версий 2 участников)
Строка 2: Строка 2:
 
= Регистры =
 
= Регистры =
  
Регистры -- основная память в ассемблере.
+
Регистры — память процессора.
  
Регистры '''общего назначения''' (в скобках -- исторические названия)
+
Регистры '''общего назначения''' (в скобках — изначальное предназначение)
 
   eax (accumulator)
 
   eax (accumulator)
 
   ebx (base)
 
   ebx (base)
 
   ecx (counter)
 
   ecx (counter)
 
   edx (data)
 
   edx (data)
   ebp ...
+
   ebp (stack base pointer)
   esp (stack pointer) трогать нежелательно, стек ПЫЩЬ
+
   esp (stack pointer) трогать <s>нежелательно</s> аккуратно, стек ПЫЩЬ
   esi source
+
   esi (source)
   edi destination
+
   edi (destination)
  
То, что вторые буквы -- первые четыре буквы латинского алфавита -- случайность. Это видно в том числе и по тому, что их естественный порядок виден при переносе на стек: eax, edx, ecx, ebx...
+
То, что вторые буквы &mdash; первые четыре буквы латинского алфавита &mdash; случайность. Это видно в том числе и по тому, что их естественный порядок виден при переносе на стек: eax, edx, ecx, ebx...
  
 
Ещё регистры:
 
Ещё регистры:
 
   eip (instruction pointer)
 
   eip (instruction pointer)
 
   eflags: куча битов, которые означают флаги. пример:
 
   eflags: куча битов, которые означают флаги. пример:
   zf -- 1, если последняя операция вернула 0, 1 otherwise
+
   zf &mdash; 1, если последняя операция вернула 0, 1 otherwise
   cf -- знак переноса (если результат последней операции не влезает в нужное кол-во битов и один переносится)
+
   cf &mdash; знак переноса (если результат последней операции не влезает в нужное кол-во битов и один переносится)
   sf -- знаковый бит. 0 -- полож., 1 -- отриц.
+
   sf &mdash; знаковый бит. 0 &mdash; полож., 1 &mdash; отриц.
  
 
На вышеперечисленные флаги влияет результат выполнения ТОЛЬКО арифметических операций, в том числе comp и test.
 
На вышеперечисленные флаги влияет результат выполнения ТОЛЬКО арифметических операций, в том числе comp и test.
  
   df -- направление выполнения строковых операций.
+
   df &mdash; направление выполнения строковых операций.
  
 
Регистры e** 32-битны. На 64-битных системах действует следующие наименования:
 
Регистры e** 32-битны. На 64-битных системах действует следующие наименования:
Строка 46: Строка 46:
  
 
== Команды загрузки ==
 
== Команды загрузки ==
   mov -- команда загрузки.
+
   mov register|memory, register|memory|immediate ; записать значение второго операнда в первый, размер данных должен совпадать
   mov al, 5 -- загрузить в al 5
+
   mov al, 5 ; загрузить в al 5
Обычно первый аргумент команды -- то, что меняется.
+
   mov cx, di ; скопировать значение di в cx
   mov cx, di -- скопировать значения cx в di. Размер копируемых данных должен совпадать.
 
  movzx -- размер игнорируется; то, что осталось, заполняется нулями
 
  movcx -- размер игнорируется; то, что осталось, заполняется знаковым (старшем) битом
 
  mov(cc): cc -- условие. если cc, то mov, ничего otherwise
 
  xchg -- обмен значений регистров
 
  
   bswap -- меняет порядок байт в регистре на обратный (little в big, big в little)
+
  movzx register, register|memory ; копирование значений меньшей разрядности в регистр большей разрядности, недостающие биты заполняются нулями
 +
  movsx register, register|memory ; копирование значений меньшей разрядности в регистр большей разрядности, недостающие биты заполняются знаковым битом
 +
 
 +
  cmov(cc) register, register|memory ; cc &mdash; условие, команда выполняется только если условие верно
 +
 
 +
  xchg register|memory, register|memory ; обмен значений
 +
 
 +
   bswap register ; меняет порядок байт в регистре на обратный
  
 
Уточнение про порядок байт:
 
Уточнение про порядок байт:
Строка 65: Строка 67:
 
   //31...15...7...0
 
   //31...15...7...0
 
   //
 
   //
   //процессоры: x86 -- little-endian, при обмене данными по сети -- big-endian
+
   //процессоры: x86 &mdash; little-endian, при обмене данными по сети &mdash; big-endian
  
 
Обращаться можно и к памяти:
 
Обращаться можно и к памяти:
mov eax,[ebx] -- загрузка в eax того, что находится по адресу ebx. (в C выглядело бы как eax = *ebx. [] - косвенная адресация. не более одних скобок за раз, ** недопустимы. может использоваться как для левого, так и правого аргумента mov. по правилам в команде <= 1 обращения к памяти. сказать, что "mov [eax],[ebx]" разрешено == всё равно, что выгнать себя с экзамена)
+
  mov eax, [ebx] ; загрузка в eax того, что находится по адресу ebx (в C выглядело бы как eax = *ebx)
 +
 
 +
[] - косвенная адресация. не более одних скобок за раз, ** недопустимы. Может использоваться как для левого, так и правого аргумента mov. В команде может быть не больше одного обращения к памяти.
 +
  <s>mov [eax], [ebx]</s> ; так делать нельзя!
 +
 
 
Оперативную память попортить нельзя, она разделена между процессами.
 
Оперативную память попортить нельзя, она разделена между процессами.
//Си -- высокоуровневый ассемблер, многие вещи в Си пошли от ассемблера
+
 
 
В квадратных скобках при использовании 16-битных регистров можно писать три опциональные части:
 
В квадратных скобках при использовании 16-битных регистров можно писать три опциональные части:
bx (bp) + si (di) +(-) offset (16-битное число)
+
  bx   si
 +
      +     +/- offset (16-битное число)
 +
  bp  di
 
В квадратных скобках при использовании 32-битных регистров позволено писать довольно много разных вещей:
 
В квадратных скобках при использовании 32-битных регистров позволено писать довольно много разных вещей:
любой из регистров общего назначения + (любой из регистров общего назначения, за исключением esp)*(1|2|4|8) +(-) offset (32-битное число)
+
  eax
 +
  ebx  eax
 +
  ecx  ebx  1
 +
  edx  ecx  2
 +
      + edx *   +/- offset (32-битное число)
 +
  ebp  ebp  4
 +
  esp  edi  8
 +
  edi  esi
 +
  esi
  
 
== Работа со стеком ==
 
== Работа со стеком ==
  
   push eax -- положить eax на стек.
+
   push register|memory|immediate ; положить значение на стек
  
 
Сишный псевдокод:
 
Сишный псевдокод:
   | esp -= sizeof(eax) (минус потому, что стек "растёт вниз", чем он больше, тем на меньшее число указывает esp (и наоборот))
+
   esp -= sizeof(eax) // минус потому, что стек "растёт вниз", чем он больше, тем на меньшее число указывает esp (и наоборот)
   | *esp = eax
+
   *esp = eax
Вроде, эквивалентный код на ассемблере:
+
Эквивалентный код на ассемблере:
   | sub esp, 4
+
   sub esp, 4
   | mov [esp], eax
+
   mov [esp], eax
  
pop eax -- запихать вершину стека в eax
+
  pop register|memory ; записать значение верхнего элемента стека в регистр/память
  
 
Как это выглядит на ассемблере:
 
Как это выглядит на ассемблере:
   | mov eax, [esp]
+
   mov eax, [esp]
   | add esp, 4
+
   add esp, 4
  
Стек-пойнтеру (sp) в 16-битном режиме довольно желательно бы быть чётным, в 32-битном -- кратным четырём: всё будет работать существенно быстрее.
+
Стек-пойнтеру (sp) в 16-битном режиме довольно желательно бы быть чётным, в 32-битном &mdash; кратным четырём: всё будет работать существенно быстрее.
Стек -- хорошее быстрое временное хранилище. не слишком большое (1-2 мб)
+
Стек &mdash; хорошее быстрое временное хранилище. Не слишком большое (1-2 мб).
//malloc и free -- системные функции, их можно свободно использовать в любом языке (и в Си, и здесь, и в дельфи)
+
//malloc и free &mdash; системные функции, их можно свободно использовать в любом языке (и в Си, и здесь, и в дельфи)
  
   pusha(d) -- сохранить в стек все 8 регистров общего назначения
+
   pusha(d) ; сохранить в стек все 8 регистров общего назначения
   popa(d) -- вытащить их из стека
+
   popa(d) ; вытащить их из стека
  
(push|pop)ad работают с 32-битными регистрами, (push|pop)a - c 16-битными
+
(push|pop)ad работают с 32-битными регистрами, (push|pop)a &mdash; c 16-битными.
при восстановлении не меняется esp (логично, дабы с esp не произошло трешака)
+
При восстановлении не меняется esp (логично, дабы с esp не произошло трешака).
 
//"Вас никто не заставляет делать разумные вещи. Если вы хотите делать безумные вещи, вы можете это делать совершенно свободно!"
 
//"Вас никто не заставляет делать разумные вещи. Если вы хотите делать безумные вещи, вы можете это делать совершенно свободно!"
  
Строка 114: Строка 130:
 
   imul / idiv
 
   imul / idiv
  
   mul OP -- после этой команды в edx:eax 64 бита -- результат умножения eax на аргумент
+
   mul OP &mdash; после этой команды в edx:eax 64 бита &mdash; результат умножения eax на OP
   div OP -- после этой команды в edx edx:eax / ebx
+
   div OP &mdash; после этой команды в edx edx:eax / ebx
 
                             в eax же eds:eax % ebx
 
                             в eax же eds:eax % ebx
  
Строка 121: Строка 137:
 
//в документации указана обработка всего этого в 8- и 16-битных случаях
 
//в документации указана обработка всего этого в 8- и 16-битных случаях
  
   imul и idiv -- аналогично mul и div, НО: работают со знаковыми числами
+
   imul и idiv &mdash; аналогично mul и div, НО: работают со знаковыми числами
  
 
кроме того, у imul есть такие формы записи:
 
кроме того, у imul есть такие формы записи:
Строка 143: Строка 159:
  
 
== Команды логики ==
 
== Команды логики ==
   and, or, xor, not. Все, кроме not -- унарные.
+
   and, or, xor, not. Все, кроме not имеют два аргумента.
  
   xor eax, eax -- быстрое обнуление регистра
+
   xor eax, eax &mdash; быстрое обнуление регистра
  
 
команда mov anything, 0 имеет смысл, но редко. когда:
 
команда mov anything, 0 имеет смысл, но редко. когда:
Строка 153: Строка 169:
 
Команды cmp/test аналогичны командам (sub/and). результаты не пишут никуда (!!!), но ставят флаги.
 
Команды cmp/test аналогичны командам (sub/and). результаты не пишут никуда (!!!), но ставят флаги.
  
   test eax,eax -- самый короткий способ проверить, 0 ли регистр
+
   test eax,eax &mdash; самый короткий способ проверить, 0 ли регистр
  
 
== Команды сдвига ==
 
== Команды сдвига ==
 
Во всех сдвигах последний сдвигаемый бит идёт в cf
 
Во всех сдвигах последний сдвигаемый бит идёт в cf
  
   shr/shl -- сишные сдвиги (>>, <<). второй аргумент команды -- либо константа, либо cl. деление на степени двойки -- самое быстрые, ибо сдвиги, которые гораздо более быстрее, нежели "реальное" деление.
+
   shr/shl &mdash; сишные сдвиги (>>, <<). второй аргумент команды &mdash; либо константа, либо cl. деление на степени двойки &mdash; самое быстрые, ибо сдвиги, которые гораздо более быстрее, нежели "реальное" деление.
   sar -- сдвиг справо со знаком. Сишное >> компилируется в зависимости от "знаковости" переменной в shr/sar. shl, очевидно, == sal.
+
   sar &mdash; сдвиг справо со знаком. Сишное >> компилируется в зависимости от "знаковости" переменной в shr/sar. shl, очевидно, == sal.
  
 
   shrd/shld принимают три аргумента.
 
   shrd/shld принимают три аргумента.
 
   shrd eax,ebx,3:
 
   shrd eax,ebx,3:
   ebx,eax двигаются на 3 вправо (О_О). первые k (где k -- третий аргумент) битов того, что указано в первом аргументе == последние k того, что указано во втором.
+
   ebx,eax двигаются на 3 вправо (О_О). первые k (где k &mdash; третий аргумент) битов того, что указано в первом аргументе == последние k того, что указано во втором.
  
 
=== Команды вращения (о_О) ===
 
=== Команды вращения (о_О) ===
Строка 175: Строка 191:
 
   v-------------------------------->
 
   v-------------------------------->
  
Аналогично rol/ror'у, но флаг cf -- часть того, что сдвигается (т.о., сдвигаются 33 бита)
+
Аналогично rol/ror'у, но флаг cf &mdash; часть того, что сдвигается (т.о., сдвигаются 33 бита)
  
 
== Команды передачи управления ==
 
== Команды передачи управления ==
  
 
   jmp метка (jmp eax)
 
   jmp метка (jmp eax)
фактически -- mov eip, smth
+
фактически &mdash; mov eip, smth
  
 
Так же есть условия, зависящие от флагов:
 
Так же есть условия, зависящие от флагов:
 
   j(cc)
 
   j(cc)
   ex.: jz -- условный переход, если флаг нуля установлен.
+
   ex.: jz &mdash; условный переход, если флаг нуля установлен.
   jnz -- переход, если флаг нуля не установлен.
+
   jnz &mdash; переход, если флаг нуля не установлен.
   jc, jnc -- аналогично для флага переноса, и так далее... тысячи их!
+
   jc, jnc &mdash; аналогично для флага переноса, и так далее... тысячи их!
  
 
   ja, jb, jae, jbe, jl, jg, jle, jge (к каждой можно устроить отрицание(
 
   ja, jb, jae, jbe, jl, jg, jle, jge (к каждой можно устроить отрицание(
   ja -- above (если больше, без знака)
+
   ja &mdash; above (если больше, без знака)
   jb -- below (если меньше, без знака)
+
   jb &mdash; below (если меньше, без знака)
   jae -- above/equal (больше или равно без знака)
+
   jae &mdash; above/equal (больше или равно без знака)
   jbe -- below/equal (меньше или равно без знака)
+
   jbe &mdash; below/equal (меньше или равно без знака)
 
   jg  \
 
   jg  \
   jl  |} -- аналогично вышеуказанным,
+
   jl  |} &mdash; аналогично вышеуказанным,
   jge |} -- но с учётом знаков.
+
   jge |} &mdash; но с учётом знаков.
 
   jle /
 
   jle /
  
Строка 203: Строка 219:
 
те же самые условия используеются в командах условной загрузки (mov(cc))
 
те же самые условия используеются в командах условной загрузки (mov(cc))
  
   call -- аналогично jmp, НО.
+
   call &mdash; аналогично jmp, НО.
 
   call x ~=  
 
   call x ~=  
 
       | push eip
 
       | push eip
Строка 210: Строка 226:
 
       | pop eip
 
       | pop eip
 
    
 
    
У ret есть опциональный параметр -- число.
+
У ret есть опциональный параметр &mdash; число.
 
   ret x ~=
 
   ret x ~=
 
       pop eip
 
       pop eip
Строка 216: Строка 232:
  
 
//пара команд, которые все любят
 
//пара команд, которые все любят
   nop -- Команда, Которая Ничего Не Делает
+
   nop &mdash; Команда, Которая Ничего Не Делает
   ud2 -- Команда, Которой Не Существует. Если она выполняется, программа падает с ошибкой. И БУДЕТ ПАДАТЬ. ВСЕГДА. И НЫНЕ. И ПРИСНО. ВО ВЕКИ. ВЕКОВ
+
   ud2 &mdash; Команда, Которой Не Существует. Если она выполняется, программа падает с ошибкой. И БУДЕТ ПАДАТЬ. ВСЕГДА. И НЫНЕ. И ПРИСНО. ВО ВЕКИ. ВЕКОВ

Текущая версия на 17:11, 3 мая 2012

Регистры

Регистры — память процессора.

Регистры общего назначения (в скобках — изначальное предназначение)

  eax (accumulator)
  ebx (base)
  ecx (counter)
  edx (data)
  ebp (stack base pointer)
  esp (stack pointer) трогать нежелательно аккуратно, стек ПЫЩЬ
  esi (source)
  edi (destination)

То, что вторые буквы — первые четыре буквы латинского алфавита — случайность. Это видно в том числе и по тому, что их естественный порядок виден при переносе на стек: eax, edx, ecx, ebx...

Ещё регистры:

  eip (instruction pointer)
  eflags: куча битов, которые означают флаги. пример:
  zf — 1, если последняя операция вернула 0, 1 otherwise
  cf — знак переноса (если результат последней операции не влезает в нужное кол-во битов и один переносится)
  sf — знаковый бит. 0 — полож., 1 — отриц.

На вышеперечисленные флаги влияет результат выполнения ТОЛЬКО арифметических операций, в том числе comp и test.

  df — направление выполнения строковых операций.

Регистры e** 32-битны. На 64-битных системах действует следующие наименования:

[math] \underbrace {\fbox{32 \quad\quad\quad\quad} \overbrace{\fbox{16 \quad\quad} \overbrace{ \underbrace{\fbox{8 \quad}}_{*h} \underbrace{\fbox{8 \quad}}_{*l} }^{*x} }^{e*x} }_{r*x} [/math]

Собственно, этимология названий: буква e (extended) появилась в названиях регистров с 386 процессором и 32-битным режимом работы. h и l пошли от high и low соответственно (старший и младший байты)

Обзор команд

Команды загрузки

  mov register|memory, register|memory|immediate ; записать значение второго операнда в первый, размер данных должен совпадать
  mov al, 5 ; загрузить в al 5
  mov cx, di ; скопировать значение di в cx
  movzx register, register|memory ; копирование значений меньшей разрядности в регистр большей разрядности, недостающие биты заполняются нулями
  movsx register, register|memory ; копирование значений меньшей разрядности в регистр большей разрядности, недостающие биты заполняются знаковым битом
  cmov(cc) register, register|memory ; cc — условие, команда выполняется только если условие верно
  xchg register|memory, register|memory ; обмен значений
  bswap register ; меняет порядок байт в регистре на обратный

Уточнение про порядок байт:

  //little-endian:
  //байты загружаются в память в порядке:
  //0...7...15...31
  //
  //big-endian:
  //31...15...7...0
  //
  //процессоры: x86 — little-endian, при обмене данными по сети — big-endian

Обращаться можно и к памяти:

  mov eax, [ebx] ; загрузка в eax того, что находится по адресу ebx (в C выглядело бы как eax = *ebx)

[] - косвенная адресация. не более одних скобок за раз, ** недопустимы. Может использоваться как для левого, так и правого аргумента mov. В команде может быть не больше одного обращения к памяти.

  mov [eax], [ebx] ; так делать нельзя!

Оперативную память попортить нельзя, она разделена между процессами.

В квадратных скобках при использовании 16-битных регистров можно писать три опциональные части:

  bx   si
     +     +/- offset (16-битное число)
  bp   di

В квадратных скобках при использовании 32-битных регистров позволено писать довольно много разных вещей:

  eax
  ebx   eax
  ecx   ebx   1
  edx   ecx   2 
      + edx *   +/- offset (32-битное число)
  ebp   ebp   4
  esp   edi   8
  edi   esi
  esi

Работа со стеком

  push register|memory|immediate ; положить значение на стек

Сишный псевдокод:

  esp -= sizeof(eax) // минус потому, что стек "растёт вниз", чем он больше, тем на меньшее число указывает esp (и наоборот)
  *esp = eax

Эквивалентный код на ассемблере:

  sub esp, 4
  mov [esp], eax
  pop register|memory ; записать значение верхнего элемента стека в регистр/память

Как это выглядит на ассемблере:

  mov eax, [esp]
  add esp, 4

Стек-пойнтеру (sp) в 16-битном режиме довольно желательно бы быть чётным, в 32-битном — кратным четырём: всё будет работать существенно быстрее. Стек — хорошее быстрое временное хранилище. Не слишком большое (1-2 мб). //malloc и free — системные функции, их можно свободно использовать в любом языке (и в Си, и здесь, и в дельфи)

  pusha(d) ; сохранить в стек все 8 регистров общего назначения
  popa(d) ; вытащить их из стека

(push|pop)ad работают с 32-битными регистрами, (push|pop)a — c 16-битными. При восстановлении не меняется esp (логично, дабы с esp не произошло трешака). //"Вас никто не заставляет делать разумные вещи. Если вы хотите делать безумные вещи, вы можете это делать совершенно свободно!"

  • Начало второй лекции. Начало пропущено:*

Арифметические команды

  add eax, ebx ; eax += ebx
  adc eax, ebx ; eax += ebx + CF // CF - флаг переноса
  sub / sbb
  mul / div
  imul / idiv
  mul OP — после этой команды в edx:eax 64 бита — результат умножения eax на OP
  div OP — после этой команды в edx edx:eax / ebx
                            в eax же eds:eax % ebx

как при делении на ноль, так и переполнении при делении вылетает исключение и программу убивает система. последнее может лечиться обнулением edx'а. //в документации указана обработка всего этого в 8- и 16-битных случаях

  imul и idiv — аналогично mul и div, НО: работают со знаковыми числами

кроме того, у imul есть такие формы записи:

  imul ecx, ebx == ecx *= ebx (верхние биты обнуляются)
  imul ecx, ebx, 5 == ecx = ebx * 5
  inc и dec. принимают один операнд, увеличивают (уменьшают) его на один; не трогают флаг переноса

Команда, которая неизвестно для чего изначально нужна.

  lea (load effective adress):
  lea eax, [ebx+ecx] ; фактически, трёхоперандное сложение. eax = abx + ecx
  lea eax, [ebx+ebx*4] ; eax = ebx * 5 (yasm поддерживает запись вида lea eax, [ebx*5], но по стандарту так нельзя)

Как быстрее всего умножить число на 10?

  lea eax, [ebx*5]
  add eax, eax

Команды логики

  and, or, xor, not. Все, кроме not имеют два аргумента.
  xor eax, eax — быстрое обнуление регистра

команда mov anything, 0 имеет смысл, но редко. когда: 1) когда нужно сохранить флаги. логика их меняет 2) когда нужно обнулять память. ксорить память нельзя (её можно указать лишь один раз)

Команды cmp/test аналогичны командам (sub/and). результаты не пишут никуда (!!!), но ставят флаги.

  test eax,eax — самый короткий способ проверить, 0 ли регистр

Команды сдвига

Во всех сдвигах последний сдвигаемый бит идёт в cf

  shr/shl — сишные сдвиги (>>, <<). второй аргумент команды — либо константа, либо cl. деление на степени двойки — самое быстрые, ибо сдвиги, которые гораздо более быстрее, нежели "реальное" деление.
  sar — сдвиг справо со знаком. Сишное >> компилируется в зависимости от "знаковости" переменной в shr/sar. shl, очевидно, == sal.
  shrd/shld принимают три аргумента.
  shrd eax,ebx,3:
  ebx,eax двигаются на 3 вправо (О_О). первые k (где k — третий аргумент) битов того, что указано в первом аргументе == последние k того, что указано во втором.

Команды вращения (о_О)

  rol, ror: тупое вращение по кругу, ничего ниоткуда не берётся, не теряется и не придумывается.
  rcl, rcr:
  rcl:
  cf <- 31...<первый аргумент>...0-^
  |                                |
  v-------------------------------->

Аналогично rol/ror'у, но флаг cf — часть того, что сдвигается (т.о., сдвигаются 33 бита)

Команды передачи управления

  jmp метка (jmp eax)

фактически — mov eip, smth

Так же есть условия, зависящие от флагов:

  j(cc)
  ex.: jz — условный переход, если флаг нуля установлен.
  jnz — переход, если флаг нуля не установлен.
  jc, jnc — аналогично для флага переноса, и так далее... тысячи их!
  ja, jb, jae, jbe, jl, jg, jle, jge (к каждой можно устроить отрицание(
  ja — above (если больше, без знака)
  jb — below (если меньше, без знака)
  jae — above/equal (больше или равно без знака)
  jbe — below/equal (меньше или равно без знака)
  jg  \
  jl  |} — аналогично вышеуказанным,
  jge |} — но с учётом знаков.
  jle /

к каждым командам существуют отрицания. j(n)cc логично, что, например, jb == jnae

те же самые условия используеются в командах условной загрузки (mov(cc))

  call — аналогично jmp, НО.
  call x ~= 
      | push eip
      | mov eip, x
  ret ~= 
      | pop eip
  

У ret есть опциональный параметр — число.

  ret x ~=
      pop eip
      add esp, x ; выкинуть со стека x байт

//пара команд, которые все любят

  nop — Команда, Которая Ничего Не Делает
  ud2 — Команда, Которой Не Существует. Если она выполняется, программа падает с ошибкой. И БУДЕТ ПАДАТЬ. ВСЕГДА. И НЫНЕ. И ПРИСНО. ВО ВЕКИ. ВЕКОВ