Заклинание кода: Далет — Архив WASM.RU
Говорят, что Баал, помимо написания своей книги, которая так и называется "Книга Баала", создаёт собственные операционные системы. Поистине с диавольской хитростью он смог сделать так, что почти каждый из нас пользуется одной из них, попадая таким образом в тень Баала, в сумерках которой рыщут его дети в поисках подати, которую не брезгуют взимать чем угодно, но преимущественно зеленью. Когда кто-нибудь, набравшись смелости, замечает Баалу, что поведение его детей слишком вызывающе и даже разрушительно для окружающей среди, он отвечает: "Но разве не достоин я некоторого количества денег от пользователей моих операционных систем? И разве не нужно моим кобольдам из индийских пределов есть, пить и отдыхать? Разве не работают они в поте лица своего, чтобы принести Человечеству лучшие операционные системы? Разве не должны вы отдать мне все свои деньги?". На последнем Баал обычно замолкает, понимая, что ляпнул что-то не то. На сем мы вернёмся к теме данного фолианта: к заклинанию кода.
В прошлой главе я рассказал, что такое байт ModR/M и показал некоторые из его возможностей, но далеко не все. Пришло время поговорить о поле Mod. Но прежде я хотел бы напомнить о том, какие режимы адресации есть в современных процессорах семейства x86.
Как вы должны уже знать, многие инструкции позволяют использовать косвенную адресацию:
Код (Text):
mov eax,[edx]В данном примере в EAX копируется значение ячейки памяти, адрес которой находится в EDX. Также вам должно быть известно, что в ассемблерах процессоров семейства x86 возможны и такие финты:
Код (Text):
mov eax,[edx+ecx*4+1000]Здесь в EAX копируется содержимое ячейки памяти, адрес которой вычисляется следующим образом: содержимое ECX умножается 4, прибавляется к содержимому EDX, а к полученному результату добавляется 1000. А ещё вам должно быть известно, что косвенная адресация может быть только в одном операнде, но не в двух. Поэтому инструкции, подобные следующей, недопустимы, а при попытке их трансляции будет выдана ошибка:
Код (Text):
mov [eax],[edx]Возможно, у вас возникли вопросы: "А где можно взять полный список возможных режимов адресации? И насколько можно умножать регистры? На 4 можно - а на 10?". Ответ на эти вопросы можно получить с помощью магической формулы, данной интеловскими гномами в Книге Двойных Слов. Вот она:
Код (Text):
[регистр1+регистр2*множитель+смещение]Регистр1 - это один из 8 регистров общего назначения. Регистр2 - тоже один из регистров общего назначения, за исключением ESP (почему - объяснено ниже). Множителем может быть число 1, 2, 4 или 8, то есть умножить на 10 или 100 не получится (а жаль). Смещение может быть 8-, 16- или 32-битным (последние два - в зависимости от того, в каком режиме работает программа, в 16- или 32-битном).
Теперь я научу вас составлять заклинания, в которых используется косвенная адресация, если множитель равен 1. Другие множители требуют использования ещё одного байта (SIB), но о нём будет рассказано несколько позднее.
Помните поле Mod в ModR/M? Как могли догадаться самые прозорливые, название этого поля происходит от слова MODe, которое означает "режим" (но не как "правящий режим", а как "режим работы"). Это потому, что поле Mod задаёт, что именно кодируется в R/M. Оказывается, в сочетании с Mod, R/M может кодировать не только регистры, но и различные режимы косвенной адресации. Отмечу, что назначение поля Reg не изменяется - в нём всегда кодируется номер регистра (либо расширение опкода, но об этом в другой главе).
Мы помещали в поле Mod 11b. Это означало, что в R/M находится номер регистра. А что представляют из себя другие режимы? Вот как интерпретируется значение поля R/M в режиме 00b.
- 000b - [EAX]
- 001b - [ECX]
- 010b - [EDX]
- 011b - [EBX]
- 100b - за байтом ModR/M следует байт SIB
- 101b - за ModR/M следует 32-битное смещение (адрес памяти)
- 110b - [ESI]
- 111b - [EDI]
Теперь давайте составим заклинание для следующего нехитрого действия:
Код (Text):
mov eax,[edx]Думаю, взглянув на вышеприведённый список и зная, что опкод операции "MOV регистр,регистр/память" (где "регистр/память" означает, что параметр - это либо регистр, либо адрес ячейки памяти) 8Bh, вы легко догадаетесь, что получится:
Код (Text):
db 8Bh,00000010b ; 08Bh(Опкод), 00(Mod)-000(Reg=EAX)-010(R/M=[EDX])Теперь предположим, что у нас есть переменная var, содержимое которой нужно скопировать в ESI. В FASM это записывается следующим образом:
Код (Text):
mov esi,[var] ; в MASM можно записать mov esi,varЧтобы составить правильное заклинание, поместим, согласно списку выше, в поле R/M 101b, а после байта ModR/M поместим адрес переменной:
Код (Text):
db 8Bh,00110101b dd varЧувствуете Силу? С каждым заклинанием вы становитесь все могущественнее и могущественнее. Но успокаиваться рано. Внимательный читатель может заметить, что в списке выше отсутствуют регистры EBP и ESP, хотя, как легко убедиться, инструкции MOV EAX,[EBP] и MOV EAX,[ESP] поддерживаются. Значит, должен быть какой-то другой путь, чтобы их закодировать? Безусловно, он есть, ведь мы ещё не рассмотрели другие возможные значения поля Mod, а посему перейдём к следующему значению - 01b. В этом случае поле R/M интерпретируется следующим образом:
- 000b - [EAX] + 8-битное смещение
- 001b - [ECX] + 8-битное смещение
- 010b - [EDX] + 8-битное смещение
- 011b - [EBX] + 8-битное смещение
- 100b - за ModR/M следует SIB, а далее 8-битное смещение
- 101b - [EBP] + 8-битное смещение
- 110b - [ESI] + 8-битное смещение
- 111b - [EDI] + 8-битное смещение
Из этого списка можно получить ответ, как же заколдовать инструкцию MOV EDI,[EBP]. Фактически, у нас получается MOV EDI,[EBP+0], а заклинание будет следующим:
Код (Text):
db 8Bh,01111101b,0 ; 08Bh(Опкод), ; 01(Mod)-111(Reg=EDI)-101(R/M=[EBP+смещ8b]), ; 0(8-битное смещение)Если вы поняли, как работает режим 01b, то и режим 01b для вас не будет сложным.
- 000b - [EAX] + 32-битное смещение
- 001b - [ECX] + 32-битное смещение
- 010b - [EDX] + 32-битное смещение
- 011b - [EBX] + 32-битное смещение
- 100b - за ModR/M следует SIB, а далее 32-битное смещение
- 101b - [EBP] + 32-битное смещение
- 110b - [ESI] + 32-битное смещение
- 111b - [EDI] + 32-битное смещение
Давайте заколдуем инструкцию ADD ECX,[EBX+100000]:
Код (Text):
db 3,10001011b ; 03h(Опкод) ; 10(Mod)-001(Reg=ECX)-011(R/M=[EBX+смещ32b]) dd 100000 ; 32-битное смещениеОстался последний режим (11b), но думаю, что здесь уже и так всё ясно, ведь мы неоднократно пользовались им в прошлой главе. Вот как интерпретируется поле R/M в режиме 11b:
- 000b - EAX
- 001b - ECX
- 010b - EDX
- 011b - EBX
- 100b - ESP
- 101b - EBP
- 110b - ESI
- 111b - EDI
Итак, теперь вы можете с лёгкостью заколдовывать инструкции, в которых есть косвенная адресация вида [регистр+смещение] или просто адрес переменной. Но как же окончательно обрести власть над инструкциями ассемблера и овладеть искусством заклинания всех режимов адресации? Ответ прост - читайте дальше! Ибо я собираюсь поведать вам о следующем поле, которое и применяется для этих целей - SIB.
Когда интеловские гномы создавали режимы адресации, один из них нашёл подземный опиумный цветок и выпил из него весь нектар. После этого на него снизошла благодать и он придумал, что можно добавить ещё один байт, расширив количество вариантов адресации, и тем самым вселить в сердца пользователей почтение к мастерству интеловских гномов. Сказано - сделано. Хотя злые языки говорят, что способ, предложенный гномами, кривой и надо совсем не так.
Но вернёмся к делу. С помощью байта SIB можно задавать выражения вида [регистр1+регистр2*множитель+смещение]. Структура байта SIB такова:
- Биты 6-7: множитель
Биты 3-5: регистр2
- 00b - 1
- 01b - 2
- 10b - 4
- 11b - 8
Биты 0-2: регистр1
- 000b - EAX
- 001b - ECX
- 010b - EDX
- 011b - EBX
- 100b - регистр2 не используется
- 101b - EBP
- 110b - ESI
- 111b - EDI
- 000b - EAX
- 001b - ECX
- 010b - EDX
- 011b - EBX
- 100b - ESP
- 101b - 32-битное смещение, если Mod в ModR/M равен 00, в противном случае EBP
- 110b - ESI
- 111b - EDI
Попробуйте следующее упражнение: глядя на приведённую таблицу произносите "Ом" на выдохе, а "Ам" на вдохе и одновременно чертите левой рукой круг, а правой - квадрат. Через несколько часов вы, без всякого сомнения, познаете Дао SIB, а я постараюсь вам в этом помочь своими мудрыми наставлениями и замечаниями. Давайте разомнем ваши косточки и немного потренируемся.
Начнём с MOV ECX,[EDI+ESI*4]. Разложим ModR/M: Mod должно быть равно 00, Reg - 001b (ECX), R/M - 100b (потому что далее следует SIB). Теперь разложим SIB: в поле множитель должно быть 10b (что указывает на значение 4), в регистр2 (который должен быть умножен на 4) - 110b (ESI), в регистр1 - 111b (EDI). В итоге получается следующее прекрасно работающее заклинание:
Код (Text):
db 8Bh,00001100b,10110111bВернёмся к вопросу о том, как заколдовать MOV EAX,[ESP]. Теперь вы можете это сделать, поскльку это довольно просто. В самом деле: Mod=00b, Reg=000b (EAX), R/M=100b (нам понадобится поле SIB). SIB: множитель=00b (*1), регистр2=100b (не используется), регистр1=100b (ESP).
Код (Text):
db 8Bh,00000100b,00100100bОбратите внимание на следующее. Во-первых, регистр2 не может быть ESP, ведь значение 100b, которое должно было символизировать ESP, указывает, что регистр2 не используется. Соответственно, заколдовать инструкцию MOV EAX,[EDX+ESP*4] не получится (хотя MOV EAX,[ESP+EDX] или MOV EAX,[ESP+4*EDX] - вполне).
Во-вторых, если в Mod 00b, то 101b в регистр1 означает, что далее следует 32-битное смещение. Поэтому, чтобы заколдовать MOV EAX,[EBP+EDX], нужен описанный выше трюк: в Mod помещается 01b, а после SIB идет нулевое 8-битное смещение.
На досуге попытайтесь составить заклинания для различных инструкций.
В следующей главе речь пойдёт о том, как читать Книгу Двойных Слов. © Aquila / WASM.RU
Заклинание кода: Далет
Дата публикации 5 дек 2002