allasm.ru

    Меню

 

Говорят, что Баал, помимо написания своей книги, которая так и называется "Книга Баала", создаёт собственные операционные системы. Поистине с диавольской хитростью он смог сделать так, что почти каждый из нас пользуется одной из них, попадая таким образом в тень Баала, в сумерках которой рыщут его дети в поисках подати, которую не брезгуют взимать чем угодно, но преимущественно зеленью. Когда кто-нибудь, набравшись смелости, замечает Баалу, что поведение его детей слишком вызывающе и даже разрушительно для окружающей среди, он отвечает: "Но разве не достоин я некоторого количества денег от пользователей моих операционных систем? И разве не нужно моим кобольдам из индийских пределов есть, пить и отдыхать? Разве не работают они в поте лица своего, чтобы принести Человечеству лучшие операционные системы? Разве не должны вы отдать мне все свои деньги?". На последнем Баал обычно замолкает, понимая, что ляпнул что-то не то. На сем мы вернёмся к теме данного фолианта: к заклинанию кода.

В прошлой главе я рассказал, что такое байт ModR/M и показал некоторые из его возможностей, но далеко не все. Пришло время поговорить о поле Mod. Но прежде я хотел бы напомнить о том, какие режимы адресации есть в современных процессорах семейства x86.

Как вы должны уже знать, многие инструкции позволяют использовать косвенную адресацию:

        mov eax,[edx]

В данном примере в EAX копируется значение ячейки памяти, адрес которой находится в EDX. Также вам должно быть известно, что в ассемблерах процессоров семейства x86 возможны и такие финты:

        mov eax,[edx+ecx*4+1000]

Здесь в EAX копируется содержимое ячейки памяти, адрес которой вычисляется следующим образом: содержимое ECX умножается 4, прибавляется к содержимому EDX, а к полученному результату добавляется 1000. А ещё вам должно быть известно, что косвенная адресация может быть только в одном операнде, но не в двух. Поэтому инструкции, подобные следующей, недопустимы, а при попытке их трансляции будет выдана ошибка:

        mov [eax],[edx]

Возможно, у вас возникли вопросы: "А где можно взять полный список возможных режимов адресации? И насколько можно умножать регистры? На 4 можно - а на 10?". Ответ на эти вопросы можно получить с помощью магической формулы, данной интеловскими гномами в Книге Двойных Слов. Вот она:

        [регистр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]

Теперь давайте составим заклинание для следующего нехитрого действия:

        mov eax,[edx]

Думаю, взглянув на вышеприведённый список и зная, что опкод операции "MOV регистр,регистр/память" (где "регистр/память" означает, что параметр - это либо регистр, либо адрес ячейки памяти) 8Bh, вы легко догадаетесь, что получится:

        db 8Bh,00000010b ; 08Bh(Опкод), 00(Mod)-000(Reg=EAX)-010(R/M=[EDX])

Теперь предположим, что у нас есть переменная var, содержимое которой нужно скопировать в ESI. В FASM это записывается следующим образом:

        mov esi,[var] ; в MASM можно записать mov esi,var

Чтобы составить правильное заклинание, поместим, согласно списку выше, в поле R/M 101b, а после байта ModR/M поместим адрес переменной:

        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], а заклинание будет следующим:

        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]:

        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: множитель
    • 00b - 1
    • 01b - 2
    • 10b - 4
    • 11b - 8
    Биты 3-5: регистр2
    • 000b - EAX
    • 001b - ECX
    • 010b - EDX
    • 011b - EBX
    • 100b - регистр2 не используется
    • 101b - EBP
    • 110b - ESI
    • 111b - EDI
    Биты 0-2: регистр1
    • 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). В итоге получается следующее прекрасно работающее заклинание:

        db 8Bh,00001100b,10110111b

Вернёмся к вопросу о том, как заколдовать MOV EAX,[ESP]. Теперь вы можете это сделать, поскльку это довольно просто. В самом деле: Mod=00b, Reg=000b (EAX), R/M=100b (нам понадобится поле SIB). SIB: множитель=00b (*1), регистр2=100b (не используется), регистр1=100b (ESP).

        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-битное смещение.

На досуге попытайтесь составить заклинания для различных инструкций.

В следующей главе речь пойдёт о том, как читать Книгу Двойных Слов.

  [C] Aquila / WASM.RU