Заклинание кода: Хей — Архив WASM.RU
В прошлом веке магия кода пользовалась почтением, а ремесленный подход к программированию, которым грешат многие нынешние адепты, был не так распространён. В те времена заклинание кода было популярно, особенно среди заклинателей демонов. Таинственные маги древности обладали невероятными на взгляд современного обывателя способностями: практически из ничего они вызывали грозных духов, ужасных привидений и призраков давно умерших программ, которые, как армия Франкенштейнов, собирали свою черную жатву. Однако разрушительное влияние Баала коснулось и магов. Со временем клан заклинателей демонов измельчал, старые герои сошли со сцены, на их место пришли новые маги. Но большинство из них отдало предпочтение бааловым лженаукам и дельфовщине. Они также создавали демонов, но их творения были убоги - так же, как и их создатели, и как инструменты, которые они использовали для своей псевдомагии. И поскольку судьба этих заклинателей не слишком завидна, перейдём к чтению Книги Двойных Слов. Да отступит тьма и прольётся на нас благословенный свет!
Книгу Двойных Слов написали интеловские гномы для того, чтобы можно было создавать заклинания для их процессоров. В ней три тома. Первый том посвящён основам архитектуры интеловских процессоров и о них излагается на протяжении более чем 400 страниц. Во втором томе рассказывается о формате инструкций процессора и приводится их подробное описание, что заняло почти 1000 страниц. Наконец, третий том предназначен брухо, которые хотят поселить в ЭВМ собственную операционную систему, посрамив тем самым Баала.
Нам понадобится второй том Книги Двойных Слов. Если вы до сих пор не получили свою копию в пристанище интеловских гномов в Междусети, самое время сделать это сейчас.
Теперь откроем том на 472 странице. Это описание знакомой нам инструкции MOV. Здесь можно видеть, что оно состоит из двух частей: краткий обзор (в квадратной рамке) и подробный (все остальное). Сейчас нас интересует то, что находится в квадратной рамке:
Код (Text):
Opcode Instruction Description 88 /r MOV r/m8,r8 Move r8 to r/m8 89 /r MOV r/m16,r16 Move r16 to r/m16 89 /r MOV r/m32,r32 Move r32 to r/m32 8A /r MOV r8,r/m8 Move r/m8 to r8 8B /r MOV r16,r/m16 Move r/m16 to r16 8B /r MOV r32,r/m32 Move r/m32 to r32 8C /r MOV r/m16,Sreg** Move segment register to r/m16 8E /r MOV Sreg,r/m16** Move r/m16 to segment register A0 MOV AL,moffs8* Move byte at (seg:offset) to AL A1 MOV AX,moffs16* Move word at (seg:offset) to AX A1 MOV EAX,moffs32* Move doubleword at (seg:offset) to EAX A2 MOV moffs8*,AL Move AL to (seg:offset) A3 MOV moffs16*,AX Move AX to (seg:offset) A3 MOV moffs32*,EAX Move EAX to (seg:offset) B0+ rb MOV r8,imm8 Move imm8 to r8 B8+ rw MOV r16,imm16 Move imm16 to r16 B8+ rd MOV r32,imm32 Move imm32 to r32 C6 /0 MOV r/m8,imm8 Move imm8 to r/m8 C7 /0 MOV r/m16,imm16 Move imm16 to r/m16 C7 /0 MOV r/m32,imm32 Move imm32 to r/m32Opcode - это опкод, Instruction - инструкция, а Description - описание. Посмотрите внимательно. Очевидно, что для колдования инструкции MOV есть несколько опкодов (довольно много, если быть точным). Какие же и когда применять? И по каким правилам?
Правила задаются в графе Instruction, рядом находится краткое описание. Всё, что нужно - это уметь читать условные обозначения ("/r", "r8" и т.п.). Далее приведён полный список обозначений, используемых интеловскими гномами. Практически, это перевод одной из глав Книги Двойных Слов. Нижеприведенные описания не обязательно читать подробно сразу - они пригодятся при разборе конкретных опкодов и выполняют справочную роль.
-= Начались даоподобные слова из Книги Двойных слов =-
Обозначения в колонке Opcode
/цифра - в Reg/Opcode всегда находится это число. Своёго рода расширение опкода за счёт поля Reg в ModR/M. Также это значит, что ModR/M задаёт только один операнд (r/m - регистр/память).
/r - ModR/M задаёт оба операнда (регистр и r/m).
eb, cw, cd, cp - однобайтное (eb), двухбайтное (cw), четырехбайтное (cd) или шестибайтное (cp) значение. Следует за опкодом и, возможно, задаёт новое значение регистра.
ib, iw, id - однобайтное (ib), двухбайтное (iw) или четырёхбайтное (id) непосредственное значение (число). Следует за опкодом, ModR/M и SIB (если таковые есть). Со знаком или без знака это значение - определяется опкодом.
+rb, +rw, +rd - номер регистра от нуля до 7. Добавляется к байту слева от знака "+". В результате получается окончательный опкод. Ниже даны номера регистров.
Код (Text):
rb rb rd -- -- -- 0 AL AX EAX 1 CL CX ECX 2 DL DX EDX 3 BL BX EBX 4 AH SP ESP 5 CH BP EBP 6 DH SI ESI 7 BH DI EDI+i - число от 0 до 7. Добавляемое к байту опкода слева от знака "+". Это число используется в инструкциях FPU, когда одним из операндов является ST(i) (FPU-регистр).
Обозначения в колонке Instruction
rel8 - относительный адрес (смещение). Задаёт смещение от 128 байт перед концом инструкции до 127 байт после конца инструкции. Иными словами, смещение от -128 до 127 байт относительно адреса после конца инструкции.
rel16 и rel32 - относительный адрес в пределах того же сегмента кода, в котором находится инструкция. rel16 относится к инструкциям с 16-битным операндом, rel32 - к инструкциям с 32-битным операндом.
ptr16:16 и ptr16:32 - дальний (far) указатель. "16:16" говорит о том, что значение указателя состоит из двух частей. Слева - 16-битное значение для сегментного регистра, справа - смещение внутри сегмента. ptr16:16 относится к инструкциям с 16-битным операндом, ptr16:32 - к 32-битным.
r8 - один из однобайтных регистров общего назначения (AL, CL, DL, BL, AH, CH, DH или BH).
r16 - один из двухбайтных регистров общего назначения (AX, CX, DX, BX, SP, BP, SI или DI).
r32 - один из четырехбайтных регистров общего назначения (EAX, ECX, EDX, EBX, ESP, EBP, ESI или EDI).
imm8 - непосредственное однобайтное значение. Это число в диапазоне от -128 до +127. В инструкциях, где imm8 скомбинировано с 16- или 32-битным операндом, непосредственное значение расширяется до размера операнда с сохранением знака (знаковое расширение: старшие байты операнда заполняются старшим битом непосредственного значения).
imm16 - непосредственное двухбайтное значение. Это число в диапазоне от -32768 до +32767. Используется для инструкций с 16-битным операндом.
imm32 - непосредственное четырёхбайтное значение. Это число в диапазоне от -2,147,483,648 до +2,147,483,647. Используется для инструкций с 32-битным операндом.
r/m8 - однобайтный операнд. Является либо регистром общего назначения (AL, BL, CL, DL, AH, BH, CH и DH), либо адресом байта памяти.
r/m16 - двухбайтный регистр общего назначения (AX, BX, CX, DX, SP, BP, SI или DI), либо адрес ячейки памяти. Используется для инструкций с 16-битным операндом.
r/m32 - четырехбайтный регистр общего назначения (EAX, EBX, ECX, EDX, ESP, EBP, ESI или EDI), либо адрес ячейки памяти. Используемая для инструкций с 32-битным операндом.
m - адрес 16- или 32-битной ячейки памяти.
m8 - адрес однобайтной ячейки памяти в регистре (E)SI или (E)DI. Используется только со строковыми инструкциями.
m16 - адрес двухбайтной ячейки памяти в регистре (E)SI или (E)DI. Используется только со строковыми инструкциями.
m32 - адрес четырёхбайтной ячейки памяти в регистре (E)SI или (E)DI. Используется только со строковыми инструкциями.
m64 - адрес 64-битной (учетверённое слово) ячейки памяти. Используется только с инструкцией CMPXCHG8B.
m128 - адрес 128-битной (увосьмерённое слово) ячейки памяти. Используется только с инструкциями SSE и SSE2.
m16:16, m16:32 - адрес ячейки памяти, содержащей дальний указатель.
m16&32, m16&16, m32&32 - адрес ячейки памяти, состоящей из нескольких элементов, чьи размеры задаются справа и слева от амперсанда. Доступны все режимы адресации памяти. Операнды m16&16 и m32&32 используются инструкцией BOUND (в них задаются верние и нижние границы массива). Операнд m16&32 используется LIDT и LGDT.
moffs8, moffs16, moffs32 - непосредственный адрес (смещение) байта, слова или двойного слова в памяти (число в moffs показывает размер ячейки памяти); байт ModR/M не представлен. Используется некоторыми вариантами инструкции MOV.
Sreg - сегментный регистр. Номера сегментных регистров в поле Reg байта ModR/M: 0=ES, 1=CS, 2=SS, 3=DS, 4=FS и 5=GS.
m32fp, m64fp, m80fp - адрес в памяти операнда с плавающей точкой одинарной точности, двойной точности или расширенной точности. Используются инструкциями FPU.
m16int, m32int, m64int - адрес в памяти целочисленного операнда размером в слово, двойное слово или учетверённое слово.
ST или ST(0) - верхний элемент стека регистров FPU.
ST(i) - i-тый элемент (от 0 до 7) от вершины стека регистров FPU.
mm - 64-битный MMX-регистр (от MM0 до MM7).
mm/m32 - младшие 32 бита MMX-регистра (от MM0 до MM7) или адрес 32-битной ячейки памяти.
mm/m64 - MMX-регистр (от MM0 до MM7) или адрес 64-битной ячейки памяти.
xmm - 128-битный XMM-регистр (от XMM0 до XMM7).
xmm/m32 - XMM-регистр (от XMM0 до XMM7) или адрес 32-битной ячейки памяти.
xmm/m64 - XMM-регистр (от XMM0 до XMM7) или адрес 64-битной ячейки памяти.
xmm/m128 - XMM-регистр (от XMM0 до XMM7) или адрес 128-битной ячейки памяти.
-= Закончились даоподобные слова из Книги Двойных слов =-
Воистину неземное блаженство нисходит, когда читаешь исполненные подобной мудрости слова. Подивимся же искусству интеловских гномов и перейдём к практическому применению информации из Книги Двойных Слов.
Обратимся к инструкции INC. Вот её описание:
Код (Text):
Opcode Instruction Description FE /0 INC r/m8 Increment r/m byte by 1 FF /0 INC r/m16 Increment r/m word by 1 FF /0 INC r/m32 Increment r/m doubleword by 1 40+ rw INC r16 Increment word register by 1 40+ rd INC r32 Increment doubleword register by 1Мы увеличивали EAX на единицу с помощью опкода 40h, к которому прибавлялся номер регистра. Проверим, соответствует ли это описанию из Книги Двойных Слов. Нам нужна последняя строчка:
Код (Text):
40+ rd INC r32 Increment doubleword register by 1В колонке Opcode указано "40+ rd". Согласно Книге Двойных Слов, "+rd" означает, что номер регистра прибавляется к байту слева от знака "+". Слева - 40h. Всё верно, это соответствует формуле "базовый опкод + номер регистра", которая вы использовали раньше, опираясь на мои наставления. Теперь вы можете опираться на Книгу Двойных Слов.
А что, если нужно увеличить на 1 не регистр, а ячейку памяти, на которую этот регистр указывает (например [eax])? Ищем нужную строчку в описании инструкции INC и находим:
Код (Text):
FF /0 INC r/m32 Increment r/m doubleword by 1Начнём с Opcode. "/0" говорит о том, что поле Reg в ModR/M всегда равно 0. Это неудивительно, поскольку здесь всего один операнд. "r/m32" задаёт либо регистр, либо ячейку памяти размером в двойное слово, используя поля ModR/M и, возможно, SIB. Зная это, можно легко составить заклинание:
Код (Text):
db 0FFh,00000000b ; FFh(Опкод), 00(Mod)-000(Reg="/0")-000(R/M=[EAX])Обратите внимание: с помощью опкода "FF /0" можно заколдовать также и "INC EAX". В самом деле:
Код (Text):
db 0FFh,11000000b ; FFh(Опкод), 11(Mod)-000(Reg="/0")-000(R/M=EAX)Не верите? Вот работающий код:
Код (Text):
format PE console entry start include '..\..\include\kernel.inc' include '..\..\include\user.inc' include '..\..\include\macro\stdcall.inc' include '..\..\include\macro\import.inc' section '.data' data writeable readable _d db '%d',0 var dd 1024 section '.code' code executable readable start: mov eax,5 ; Помещаем в eax 5 db 40h ; inc eax db 0FFh,11000000b ; inc eax ; FFh(Опкод), 11(Mod)-000(Reg="/0")-000(R/M=EAX) push eax ; В eax - 7 push _d call [printf] add esp,8 invoke ExitProcess,0 section '.idata' import data readable writeable library kernel32,'kernel32.dll',\ msvcrt,'msvcrt.dll' kernel32: import ExitProcess,'ExitProcess' msvcrt: import printf,'printf'Чудеса? Именно так. Интеловские гномы даровали нам много чудес и необъяснимых явлений. Похожая ситуация обстоит и с MOV. Для того, чтобы заколдовать "MOV ECX,EDX", можно использовать как
Код (Text):
8B /r MOV r32,r/m32 Move r/m32 to r32так и
Код (Text):
89 /r MOV r/m32,r32 Move r32 to r/m32Теперь вы, наверное, понимаете, почему интеловские процессоры, а также совместимые с ними (например те, которые делают гномы Амиды), получили такое широкое распространение. Ибо так неисчислимы чудеса и откровения, даруемые ими, что остается только в немом восхищении преклоняться перед гномьим мастерством. © Aquila / WASM.RU
Заклинание кода: Хей
Дата публикации 20 дек 2002