Участник:SkudarnovYaroslav/Лекция по ассемблеру за 3 марта — различия между версиями
(Новая страница: «Регистры -- основная память в ассемблере. Регистры общего назначения: eax (accumulator) ebx (base) ecx (...») |
м |
||
Строка 1: | Строка 1: | ||
+ | |||
Регистры -- основная память в ассемблере. | Регистры -- основная память в ассемблере. | ||
Регистры общего назначения: | Регистры общего назначения: | ||
− | eax (accumulator) | + | eax (accumulator) |
− | ebx (base) | + | ebx (base) |
− | ecx (counter) | + | ecx (counter) |
− | edx (data) | + | edx (data) |
− | ebp ... | + | ebp ... |
− | esp (stack pointer) трогать нежелательно, стек ПЫЩЬ | + | esp (stack pointer) трогать нежелательно, стек ПЫЩЬ |
− | esi ... | + | esi ... |
− | edi ... | + | edi ... |
То, что вторые буквы -- первые четыре буквы лат. алфавита -- случайность. Это видно в том числе и по тому, что их естественный порядок виден при переносе на стек: eax, edx, ecx, ebx... | То, что вторые буквы -- первые четыре буквы лат. алфавита -- случайность. Это видно в том числе и по тому, что их естественный порядок виден при переносе на стек: eax, edx, ecx, ebx... | ||
Ещё регистры: | Ещё регистры: | ||
− | eip (instruction pointer) | + | eip (instruction pointer) |
− | eflags: куча битов, которые означают флаги. пример: | + | eflags: куча битов, которые означают флаги. пример: |
− | zf -- 1, если последняя операция вернула 0, 1 otherwise | + | zf -- 1, если последняя операция вернула 0, 1 otherwise |
− | cf -- знак переноса (если результат последней операции не влезает в нужное кол-во битов и один переносится) | + | cf -- знак переноса (если результат последней операции не влезает в нужное кол-во битов и один переносится) |
− | sf -- знаковый бит. 0 -- полож., 1 -- отриц. | + | sf -- знаковый бит. 0 -- полож., 1 -- отриц. |
(на вышеперечисленные флаги влияет результат выполнения ТОЛЬКО арифметических операций!) | (на вышеперечисленные флаги влияет результат выполнения ТОЛЬКО арифметических операций!) | ||
− | df -- направление выполнения строковых операций. | + | df -- направление выполнения строковых операций. |
Регистры 32-битны. На 64-битных системах действует следующие наименования: | Регистры 32-битны. На 64-битных системах действует следующие наименования: | ||
− | + | e*x | |
− | + | ____^____ | |
− | + | / \ | |
− | 32 | 16 | 8 | 8 | + | 32 | 16 | 8 | 8 |
− | + | *h *l | |
− | \______ ______/ | + | \______ ______/ |
− | + | v | |
− | + | r*x | |
Строка 50: | Строка 51: | ||
bswap -- меняет порядок байт в регистре на обратный (little в big, big в little) | bswap -- меняет порядок байт в регистре на обратный (little в big, big в little) | ||
− | //little-endian: | + | //little-endian: |
− | //байты загружаются в память в порядке: | + | //байты загружаются в память в порядке: |
− | //0...7...15...31 | + | //0...7...15...31 |
− | // | + | // |
− | //big-endian: | + | //big-endian: |
− | //31...15...7...0 | + | //31...15...7...0 |
− | // | + | // |
− | //процессоры: x86 -- little-endian, при обмене данными по сети -- big-endian | + | //процессоры: x86 -- little-endian, при обмене данными по сети -- big-endian |
Обращаться можно и к памяти: | Обращаться можно и к памяти: | ||
Строка 98: | Строка 99: | ||
Арифм. команды: | Арифм. команды: | ||
− | add eax, ebx (eax += ebx) | + | add eax, ebx (eax += ebx) |
− | adc (???) | + | adc (???) |
− | sub / sbb | + | sub / sbb |
− | mul / div | + | mul / div |
− | imul / idiv | + | imul / idiv |
− | mul OP -- после этой команды в edx:eax 64 бита -- результат умножения eax на аргумент | + | mul OP -- после этой команды в edx:eax 64 бита -- результат умножения eax на аргумент |
− | div OP -- после этой команды в edx edx:eax / ebx | + | div OP -- после этой команды в edx edx:eax / ebx |
− | + | в eax же eds:eax % ebx | |
как при делении на ноль, так и переполнении при делении вылетает исключение и программу убивает система. последнее может лечиться обнулением edx'а. | как при делении на ноль, так и переполнении при делении вылетает исключение и программу убивает система. последнее может лечиться обнулением edx'а. | ||
Строка 121: | Строка 122: | ||
Команда, которая неизвестно для чего изначально нужна. | Команда, которая неизвестно для чего изначально нужна. | ||
− | lea (load effective adress): | + | lea (load effective adress): |
− | lea eax, [ebx+ecx] -- фактически, трёхоперандное сложение. eax = abx + ecx | + | lea eax, [ebx+ecx] -- фактически, трёхоперандное сложение. eax = abx + ecx |
− | lea eax, [ebx+ebx*4] -- eax = ebx * 5 (yasm поддерживает запись вида lea eax, [ebx*5], но по стандарту так нельзя) | + | lea eax, [ebx+ebx*4] -- eax = ebx * 5 (yasm поддерживает запись вида lea eax, [ebx*5], но по стандарту так нельзя) |
Как быстрее всего умножить число на 10? | Как быстрее всего умножить число на 10? | ||
− | lea eax, [ebx*5] | + | lea eax, [ebx*5] |
− | add eax, eax | + | add eax, eax |
Команды логики: | Команды логики: | ||
− | and, or, xor, not. Все, кроме not -- унарные. | + | and, or, xor, not. Все, кроме not -- унарные. |
+ | |||
+ | xor eax, eax -- быстрое обнуление регистра | ||
− | |||
команда mov anything, 0 имеет смысл, но редко. когда: | команда mov anything, 0 имеет смысл, но редко. когда: | ||
1) когда нужно сохранить флаги. логика их меняет | 1) когда нужно сохранить флаги. логика их меняет | ||
Строка 142: | Строка 144: | ||
Команды cmp/test аналогичны командам (sub/and). результаты не пишут никуда (!!!), но ставят флаги. | Команды cmp/test аналогичны командам (sub/and). результаты не пишут никуда (!!!), но ставят флаги. | ||
− | test eax,eax -- самый короткий способ проверить, 0 ли регистр | + | test eax,eax -- самый короткий способ проверить, 0 ли регистр |
Команды сдвига: | Команды сдвига: | ||
Во всех сдвигах последний сдвигаемый бит идёт в cf | Во всех сдвигах последний сдвигаемый бит идёт в cf | ||
− | shr/shl -- сишные сдвиги (>>, <<). второй аргумент команды -- либо константа, либо cl. деление на степени двойки -- самое быстрые, ибо сдвиги, которые гораздо более быстрее, нежели "реальное" деление. | + | shr/shl -- сишные сдвиги (>>, <<). второй аргумент команды -- либо константа, либо cl. деление на степени двойки -- самое быстрые, ибо сдвиги, которые гораздо более быстрее, нежели "реальное" деление. |
− | sar -- сдвиг справо со знаком. Сишное >> компилируется в зависимости от "знаковости" переменной в shr/sar. shl, очевидно, == sal. | + | sar -- сдвиг справо со знаком. Сишное >> компилируется в зависимости от "знаковости" переменной в 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 -- третий аргумент) битов того, что указано в первом аргументе == последние k того, что указано во втором. |
Команды вращения (о_О): | Команды вращения (о_О): | ||
− | rol, ror: тупое вращение по кругу, ничего ниоткуда не берётся, не теряется и не придумывается. | + | rol, ror: тупое вращение по кругу, ничего ниоткуда не берётся, не теряется и не придумывается. |
− | rcl, rcr: | + | rcl, rcr: |
− | rcl: | + | rcl: |
− | cf <- 31...<первый аргумент>...0-^ | + | cf <- 31...<первый аргумент>...0-^ |
− | | | | + | | | |
− | v--------------------------------> | + | v--------------------------------> |
Аналогично rol/ror'у, но флаг cf -- часть того, что сдвигается (т.о., сдвигаются 33 байта) | Аналогично rol/ror'у, но флаг cf -- часть того, что сдвигается (т.о., сдвигаются 33 байта) | ||
Строка 168: | Строка 170: | ||
Команды передачи управления: | Команды передачи управления: | ||
− | jmp метка (jmp eax) | + | jmp метка (jmp eax) |
фактически -- mov eip, smth | фактически -- mov eip, smth | ||
Так же есть условия, зависящие от флагов: | Так же есть условия, зависящие от флагов: | ||
− | j(cc) | + | j(cc) |
ex.: jz -- условный переход, если флаг нуля установлен. | ex.: jz -- условный переход, если флаг нуля установлен. | ||
− | jnz -- переход, если флаг нуля не установлен. | + | jnz -- переход, если флаг нуля не установлен. |
− | jc, jnc -- аналогично для флага переноса, и так далее... тысячи их! | + | jc, jnc -- аналогично для флага переноса, и так далее... тысячи их! |
− | ja, jb, jae, jbe, jl, jg, jle, jge (к каждой можно устроить отрицание( | + | ja, jb, jae, jbe, jl, jg, jle, jge (к каждой можно устроить отрицание( |
− | ja -- above (если больше, без знака) | + | ja -- above (если больше, без знака) |
− | jb -- below (если меньше, без знака) | + | jb -- below (если меньше, без знака) |
− | jae -- above/equal (больше или равно без знака) | + | jae -- above/equal (больше или равно без знака) |
− | jbe -- below/equal (меньше или равно без знака) | + | jbe -- below/equal (меньше или равно без знака) |
− | jg \ | + | jg \ |
− | jl |} -- аналогично вышеуказанным, | + | jl |} -- аналогично вышеуказанным, |
− | jge |} -- но с учётом знаков. | + | jge |} -- но с учётом знаков. |
− | jle / | + | jle / |
к каждым командам существуют отрицания. j(n)cc | к каждым командам существуют отрицания. j(n)cc | ||
Строка 192: | Строка 194: | ||
те же самые условия используеются в командах условной загрузки (mov(cc)) | те же самые условия используеются в командах условной загрузки (mov(cc)) | ||
− | call -- аналогично jmp, НО. | + | call -- аналогично jmp, НО. |
− | call x ~= | + | call x ~= |
− | + | | push eip | |
− | + | | mov eip, x | |
− | ret ~= | + | ret ~= |
− | + | | pop eip | |
− | + | ||
У ret есть опциональный параметр -- число. | У ret есть опциональный параметр -- число. | ||
− | ret x ~= | + | ret x ~= |
− | + | pop eip | |
− | + | add esp, x (выкинуть со стека x байт) | |
//пара команд, которые все любят | //пара команд, которые все любят | ||
− | nop -- Команда, Которая Ничего Не Делает | + | nop -- Команда, Которая Ничего Не Делает |
− | ud2 -- Команда, Которой Не Существует. Если она выполняется, программа падает с ошибкой. И БУДЕТ ПАДАТЬ. ВСЕГДА. И НЫНЕ. И ПРИСНО. ВО ВЕКИ. ВЕКОВ | + | ud2 -- Команда, Которой Не Существует. Если она выполняется, программа падает с ошибкой. И БУДЕТ ПАДАТЬ. ВСЕГДА. И НЫНЕ. И ПРИСНО. ВО ВЕКИ. ВЕКОВ |
Версия 15:59, 3 марта 2012
Регистры -- основная память в ассемблере.
Регистры общего назначения:
eax (accumulator) ebx (base) ecx (counter) edx (data) ebp ... esp (stack pointer) трогать нежелательно, стек ПЫЩЬ esi ... edi ...
То, что вторые буквы -- первые четыре буквы лат. алфавита -- случайность. Это видно в том числе и по тому, что их естественный порядок виден при переносе на стек: eax, edx, ecx, ebx...
Ещё регистры:
eip (instruction pointer) eflags: куча битов, которые означают флаги. пример: zf -- 1, если последняя операция вернула 0, 1 otherwise cf -- знак переноса (если результат последней операции не влезает в нужное кол-во битов и один переносится) sf -- знаковый бит. 0 -- полож., 1 -- отриц.
(на вышеперечисленные флаги влияет результат выполнения ТОЛЬКО арифметических операций!)
df -- направление выполнения строковых операций.
Регистры 32-битны. На 64-битных системах действует следующие наименования:
e*x ____^____ / \ 32 | 16 | 8 | 8 *h *l \______ ______/ v r*x
Команды
Команды загрузки mov -- команда загрузки. mov al, 5 -- загрузить в al 5 Обычно первый аргумент команды -- то, что меняется. mov cx, di -- скопировать значения cx в di. Размер копируемых данных должен совпадать. movzx -- размер игнорируется; то, что осталось, заполняется нулями movcx -- размер игнорируется; то, что осталось, заполняется знаковым (старшем) битом mov(cc): cc -- условие. если cc, то mov, ничего otherwise xchg -- обмен значений регистров
bswap -- меняет порядок байт в регистре на обратный (little в big, big в little)
//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. по правилам в команде <= 1 обращения к памяти. сказать, что "mov [eax],[ebx]" разрешено == всё равно, что выгнать себя с экзамена) Оперативную память попортить нельзя, она разделена между процессами. //Си -- высокоуровневый ассемблер, многие вещи в Си пошли от ассемблера В квадратных скобках при использовании 16-битных регистров можно писать три опциональные части: bx (bp) + si (di) +(-) offset (16-битное число) В квадратных скобках при использовании 32-битных регистров позволено писать довольно много разных вещей: любой из регистров общего назначения + (любой из регистров общего назначения, за исключением esp)*(1|2|4|8) +(-) offset (32-битное число)
push eax -- положить eax на стек.
Сишный псевдокод:
| esp -= sizeof(eax) (минус потому, что стек "растёт вниз", чем он больше, тем на меньшее число указывает esp (и наоборот)) | *esp = eax
Вроде, эквивалентный код на ассемблере:
| sub esp, 4 | mov [esp], eax
pop eax -- запихать вершину стека в eax
Как это выглядит на ассемблере:
| 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 (???) sub / sbb mul / div imul / idiv
mul OP -- после этой команды в edx:eax 64 бита -- результат умножения eax на аргумент 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 -- Команда, Которой Не Существует. Если она выполняется, программа падает с ошибкой. И БУДЕТ ПАДАТЬ. ВСЕГДА. И НЫНЕ. И ПРИСНО. ВО ВЕКИ. ВЕКОВ