Команды ассемблера и команды процессора.
Стоит пояснить, что если к вопросу подойти формально строго, то команды процессора и команды ассемблера — это не одно и то же. Ассеммблер — хоть и низкоуровневый язык программирования, но иногда он без спроса программиста «корректирует код под себя». Причём у каждого ассемблера (masm, tasm, fasm) это может быть по-разному. Самый яркий пример — команда ret. В ассемблерном коде мы запишем ret, а реальный ассемблер ассемблирует её как retf или retn 8. Может также изменяться код, добавлением в качестве выравнивания кода команды процессора nop (об этом ниже в статье) и т.п. Чтобы не усложнять суть вопроса, под понятиями команды процессора и команды ассемблера мы будем подразумевать одно и то же.
Команды процессора (команды ассемблера) в большинстве своём работают с аргументами, которые в ассемблере называются операндами. Система машинного кода процессоров Intel содержит более 300 команд (команды процессора, сопроцессора, MMX-расширения, XMM-расширения). С каждым новым процессором их количество растёт. Для того, чтобы профессионально программировать, не надо зубрить и разбирать все команды процессора. При необходимости можно воспользоваться справочником. В процессе чтения статей, вы поймёте, что основная суть знания ассемблера состоит не в доскональном знании всех команд, а в понимании работы системы.
Не следует забывать, что команды процессор видит в виде цифр, которые можно рассматривать как данные. Например, команда NOP занимает один байт и её машинный код — 90h.
Начиная изучать язык низкого уровня, мы будем иметь дело с ограниченным набором старых-добрых команд процессора. Иные команды ассемблера понадобятся специалистам, заинтересованным в оптимизацией кода, связанного со сложными математическими расчетами данных большого объёма.
Основные (т.н. целочисленные) команды ассемблера позволяют написать практически любую программу для операционных систем MS-DOS и Windows. Количество команд ассемблера, которыми вы будете пользоваться будет расти со временем прохождения курса. Для более детального понимания, в последствии можете обратиться к справочнику команд.
Рассмотрим команды ассемблера на практическом примере.
С использованием среды разработки TASMED или любого текстового редактора набираем код. Программа, задаст вопрос на английском языке о половой принадлежности (имеется ввиду ваш биологический пол при рождении). Если вы нажмете m (Man), будет выведено приветствие с мужчиной, если w (Woman), то с женщиной, после этого программа прекратит работу. Если будет нажата любая другая клавиша, то программа предположит, что имеет дело с гоблином, не поверит и будет задавать вам вопросы о половой принадлежности, пока вы не ответите верно.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | ;goblin.asm .model tiny ; for СОМ .code ; code segment start org 100h ; offset in memory = 100h (for COM) start: main proc begin: mov ah,09h mov dx,offset prompt int 21h inpt: mov ah,01h int 21h cmp al,'m' je mode_man cmp al,'w' je mode_woman call goblin jmp begin mode_man: mov addrs,offset man; указатель на процедуру в addrs jmp cont mode_woman: mov addrs,offset woman; указатель на процедуру в addrs cont: call word ptr addrs; косвенный вызов процедуры mov ax,4c00h int 21h main endp man proc mov ah,09h mov dx,offset mes_man int 21h ret man endp woman proc mov ah,09h mov dx,offset mes_womn int 21h ret woman endp goblin proc mov ah,09h mov dx,offset mes_gobl int 21h ret goblin endp ;DATA addrs dw 0;for procedure adress prompt db 'Are you Man or Woman [m/w]? : $' mes_man db 0Dh,0Ah,"Hello, Strong Man!",0Dh,0Ah,'$' ; строка для вывода. Вместо ASCII смвола '$' можно написать машинный код 24h mes_womn db 0Dh,0Ah,"Hello, Beautyful Woman!",0Dh,0Ah,'$' ; строка для вывода mes_gobl db 0Dh,0Ah,"Hello, Strong and Beautyful GOBLIN!",0Dh,0Ah,24h ; строка для вывода. 24h = '$' . len = $ - mes_gobl end start |
Анализируем ассемблерный код.
В програме используются следующие команды ассемблера:
- mov
- cmp
- jmp (jcc)
- call
- ret
- int
Согласитесь, не так уж и много! На примере четырёх команд ассемблера (mov, cmp, jmp, int) попробуем сформировать общее понимание и выработать алгоритм практической работы. Основное предназначение команд call и ret — работа с процедурами и их мы рассмотрим в следующей статье.
Английский язык — язык программистов и его знать просто необходимо. команды процессора чаще всего представляют сокращённые названия смыслового характера. Смысл операции заложен в ассемблерной команде.
MOV (MOVe operand — переместить операнд).
Команда осуществляет копирование второго операнда в первый операнд. По завершении процедуры данные операнда «источник» не изменяются:
mov приёмник, источник.
1 2 3 4 5 6 | mov addrs,offset woman; переместить (скопировать) в память (addrs dw 0) смещение процедуры woman mov addrs,offset start; переместить (скопировать) в память смещение, на которое указывает метка start (start:). mov dx,offset mes_womn;переместить (скопировать) в регистр dx смещение строки mes_womn (mes_womn db 0Dh,0Ah,"Hello, Beautyful Woman!",0Dh,0Ah,'$'). mov dx,ax;переместить (скопировать) в регистр dx данные из ax. mov ds,ax;переместить (скопировать) в регистр ds дданные из регистра ax. mov ah,09h ; переместить (скопировать) в ah число 09h |
CMP (CoMPare operands — сравнить операнды).
Для сравнения операндов используется вычитание (это полезно знать изучая команду jcc), значения операндов не меняются:
cmp операнд1, операнд2
обычно используется для организации условных переходов с командами jmp и jcc (jcc).
JMP (JuMP — прыгнуть, мгновенно перейти).
Продолжить выполнение кода с указанной метки:
jmp метка
JCC (Jump if conditions — перепрыгнуть, перейти если выполняется условие).
Переход к указанной метке выполняется при осуществлении определённого условия. Практически програмист сталкивается с множеством разновидностей команды (ja,je,jl,jna jz,jpe и т.д. — смотри справочник). Фактически команда проверяет состояние флагов и если условие выполняется, осуществляет переход к метке:
jcc метка
В нашей программе мы используем JE (Jump if Equal), соответственно есть условный переход JNE (Jump if Not Equal). Что любопытно, в данном случае проверяется состояние флага нуля (ZF). Алгоритм работы JE следующий:
- если ZF=1, то перейти к метке, если нет, то продолжить выполнение следующего шага программы.
- команда JE полностью эквивалентна команде JZ (Jump if Zero), а команда JNE (Jump if Not Equal) — команде JNZ (Jump if Not Zero).
- если ZF=0, то перейти к метке, если нет, то продолжить выполнение следующего шага программы.
1 2 3 4 5 6 7 8 | cmp al,'m';сравнить значение al с 'm' je mode_man; если al='m', то продолжить выполнять код с метки mode_man (Jump if Equal) cmp al,'w';сравнить значение al с 'w' je mode_woman;если al='w', то продолжить выполнять код с метки mode_woman (Jump if Equal) call goblin; jmp begin;продолжить выполнение программы с позиции, обозначенной меткой begin ... jmp cont;продолжить выполнение программы с позиции, обозначенной меткой cont |
INT (INTerrupt — вызов подпрограммы прерывания).
Имеется ввиду, что ваша программа прерывается и вызывается подпрограмма под определённым номером:
int номер прерывания.
Через функции прерываний (или просто «прерывания DOS») реализован так называемый DOS API (Application Programing Interfaces). В данном случае подпрограмма просто обозначена номером. В операционной системе Windows подпрограммы (системные функции Win API) имеют осмысленные названия (например, GetCurrentDirrectory, MessageBox, SetDlgItemText).
1 2 3 4 5 6 | mov ax,4c00h;Закрыть программу без кода выхода int 21h;Вызов прерывания ... mov ah,09h;Выводим строку на экран mov dx,offset mes_womn;Адресс строки, оканчивающийся '$' (ASCII - 24H). int 21h;Вызов прерывания |
Прерывания DOS.
В DOS необходимо пользоваться справочником. Наиболее удобный и совершенный из известных — T_HELP (смотрите рисунок в начале статьи, сам справочник есть в архиве — папка T_HELP). Есть прерывание DOS, а есть BIOS. Прерывания зависят от версии DOS. Чтобы вызвать функцию прерывания, необходимо подготовить соответствующие регистры. Обычно — это AX (ah, al) Члены, а также выходные значения функций передаются именно через них.
Разобраться с T_HELP можете сами. Наиболее удобно изучать функции с использованием API Index. Например, запускаем …T_HELP\XVIEW.EXE:
- TECH Topics
- DOS Interrupts
- INT 21H DOS Services
- DOS Function Index (by number)Ищем в табличном списке прерывание 4CH, кликаем по нему левой клавишей мыши и изучаем подробную справку:
Dos Fn 4cH : Terminate Program
AH 4CH
AL Exit Code
…
ну и тд.
На последок, посмотрим, как выглядит наша программа, обработанная дебагером, встроенным в HIEW (Alt+P):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 00000000: B409 mov ah,009 ;" " 00000002: BA4A01 mov dx,0014A ;" J" 00000005: CD21 int 021 00000007: B401 mov ah,001 ;" " 00000009: CD21 int 021 0000000B: 3C6D cmp al,06D ;"m" 0000000D: 7409 je 000000018 -------- (1) 0000000F: 3C77 cmp al,077 ;"w" 00000011: 740E je 000000021 -------- (2) 00000013: E82A00 call 000000040 -------- (3) 00000016: EBE8 jmps 000000000 -------- (4) 00000018: C70648013001 mov w,[00148],00130 ;" 0" 0000001E: EB07 jmps 000000027 -------- (5) 00000020: 90 nop 00000021: C70648013801 mov w,[00148],00138 ;" 8" 00000027: FF164801 call w,[00148] 0000002B: B8004C mov ax,04C00 ;"L " 0000002E: CD21 int 021 00000030: B409 mov ah,009 ;" " 00000032: BA6901 mov dx,00169 ;" i" 00000035: CD21 int 021 00000037: C3 retn 00000038: B409 mov ah,009 ;" " 0000003A: BA8001 mov dx,00180 ;" Ђ" 0000003D: CD21 int 021 0000003F: C3 retn 00000040: B409 mov ah,009 ;" " 00000042: BA9C01 mov dx,0019C ;" њ" 00000045: CD21 int 021 00000047: C3 retn |
Обратите внимание, как HIEW отображает информацию:
0000000F: 3C77 cmp al,077 ;«w» — в качеестве комментария отобразил ASCII символ, сответствующий 077h
…
00000018: C70648013001 mov w,[00148],00130 ;» 0″ — обозначил, что по смещению [00148] находятся данные размером в слово (w) и заполнил
;его нулём (addrs dw 0);
…
00000016: EBE8 jmps 000000000 ——— (4) ; Jump Short — обозначен именно ближний переход.
…
00000020: 90 nop ; а это что за команда? У нас её не было!
При исследовании кода обнаружилась «лишняя» команда NOP.
NOP (No OPeration — нет операции).
Не производит никаких действий. Занимает один байт (машинный код — 90h) и используется для:
- Выравнивания следующей команды или переменной сегмента в целях увеличения быстродействия определённых процессоров (иногда актуально).
- Резервирования «пустого» места в коде.
- Для задержки в целях синхронизации работы компьютера (сейчас не актуально).
- При отладке.
В данном случае команду NOP вставил TASM, посчитав, что такой код будет работать быстрее. Мы уже говорили, что определённым процессорам легче работать с определенными блоками данных. Именно для решения вопроса «блочности» в данном случае TASM сгенерировал такой код.
Команда NOP очень часто используется хакерами, чуть позже мы покажем, в каких целях.
Мы рассмотрели наиболее часто встречающиеся команды ассемблера, уяснили принцип их использования. В следующей статье нас ждут процедуры и команды, которые помогают их организовывать. Напоминаем, что все необходимые программы можно скачать с нашего сайта.