Разное

Eip регистр: Дневники чайника

Дневники чайника

Дневники чайника

Регистры процессора.


Третий день

Итак, почему же ah?

AH — это регистр процессора.

Регистры — это несколько байт, которые физически находятся в центральном процессоре (ЦП). Регистры нужны для вычислений и связи ЦП с внешним миром.

Большая часть кода программ состоит из команд копирования значений из оперативной памяти в регистры и обратно.

Знаю по себе: понять, зачем нужны регистры можно только через практику. Другого способа нет.

Причём где-то дней 10-15 я вообще не врубался — как оно всё… чтобы вот как-то вот так. :)

Но сейчас мне кажется, что регистры — самое простое, и в то же время важное, что есть в процессоре.

Ну, сами подумайте, что делает программа?

  • Приём данных от пользователя (данные передаются в регистры).
  • Вычисления (загрузили в регистр число и дали команду, как его обработать).
  • Приём данных из устройств (опять они — регистры).
  • Изменение состояния ЦП и периферийных устройств (только регистры! Например, регистр флагов).
  • Вывод данных на устройства.
  • Передача данных другим программам.
И т.д.

Во всех операциях программы (а я перечислил только основные) без регистров не обойтись.

И если даже данные не хранятся в регистрах, то в них обязательно будут указатели на эти данные (адреса данных в памяти), других способов работы с данными у процессора нет, только через собственные регистры. А если речь идёт исключительно о сторонних устройствах (например, звуковуха, видяха, винт и т.д.), то мы тоже используем регистры, только это уже не регистры процессора, а, например, PCI-регистры, или AGP, или SATA и так далее.

Так что тема АРХИВАЖНАЯ! Но непонятная без примеров. Их должно быть как минимум 10-15 (по одному в день).

Что-то я разболтался, давайте уже к делу.

Загрузите prax01.com в отладчик CodeView. Для этого скопируйте файл в каталог отладчика (примечание) и запустите из командной строки «cv prax01.com». Отладчик CodeView у вас должен выглядеть примерно так:

  • В окне 3 — дизассемблированный код.
  • В окне 5 — hex-байты. Их адреса (слева) и символы, которые они означают (справа).
  • В окне 7 — регистры процессора.
  • 9 — командная строка.

Итак, смотрим на код программы в памяти:
[3]
   Адреса Байты    Имена        Операнды
12BD:0100 B409     MOV          AH,09

12BD — сегмент нашей программы, он может быть и другим.

Поскольку все примеры для DOS у нас значительно меньше 64Kb, они будут в одном сегменте.

Значит, как я уже говорил, до следующего витка мы можем игнорировать сегменты вообще, а уж в Win32 тем более.

У com-программ в оперативной памяти адрес первой машинной команды равен 100h.

Так происходит потому, что ДОСу первые адреса нужны для служебной информации о программе: командная строка, атрибуты и всё такое (можете посмотреть в окне[5], что там).

ДОС-программы нас уже не очень интересуют, и об этом больше говорить не будем.

А в Win-программах происходит примерно то же самое, только адрес первой машинной команды обычно находится чуть дальше. В самых простых приложениях — 00401000h. :)

Нужно просто прибавлять 100h, чтоб узнать адрес в памяти из адреса в com-файле. Так же, как вы складываете 8+100d=108d. Будет всё точно так же, 8+100h=108h.

Я легко посчитал адрес текстовой строки в оперативной памяти (010D) и заложил его в код программы.

Ведь мы знаем номер байта в файле, где начинается строка текста. 0D+100h — и вот он, искомый адрес в памяти.

Главная кнопка здесь будет F10, вам нужно нажимать только на неё (всё остальное можно делать мышкой). Эта кнопка с каждым нажатием будет выполнять текущую строку.

Вы уже понимаете, что отображается в окне[5], и также имеете представление, что в окне[3] — код программ. А вот окно [7] для вас пока ничего не означает, и это очень досадно, но мы сейчас исправимся.

Нажмите F10 один раз. Выполнится строка

mov ah,09

Эта команда означает: поместить значение 09 в регистр AH. Но в окне[7] нет регистра AH, а после выполнения этой строки изменится регистр EAX.

Было  EAX=00000000
Стало EAX=00000900

Дело в том, что регистр AH — это часть регистра AX, а он — часть регистра EAX.

Если, предположим, мы загрузили регистр EAX значением 44332211, выглядеть он будет так:

EAX=44332211
 AX=    2211
 AH=    22
 AL=      11

Один 32-разрядный регистр — это всего лишь 4 байта в процессоре.

Точно так же устроены ещё 3 регистра.

Схематично их отображают так:

EAX&nbspEBX&nbspECX&nbspEDX
&nbsp
AX&nbsp&nbspBX&nbsp&nbspCX&nbsp&nbspDX
E-частьAHALE-частьBHBLE-частьCHCLE-частьDHDL

(Каждый отдельно)

EAX (сокращение от Accumulator)
EBX (сокращение от Base)
ECX (сокращение от Counter)
EDX (сокращение от Data)

Можно, кстати, писать и строчными буквами, регистры не обидятся.

EAX, EBX, ECX и EDX — 32-битные регистры данных центрального процессора (от 386-го и по сей день). В эти регистры помещаются необходимые значения, и в них же чаще всего оказываются результаты вычислений. В 32 битах можно хранить hex-число от 00 00 00 00h до FF FF FF FFh. И это всё, что может там храниться. На назначения (Accumulator, Base, Counter, Data) в наших уроках можно вообще не обращать внимания. Когда вы освоите команды Ассемблера, вы сами поймете, почему Counter и почему Base.

Первая строка кода из нашей программы уже почти понятна.

mov  ah,9

Имя команды    операнд1,операнд2

где операнд1 - регистр AH
    операнд2 - цифра 9

Остаётся непонятной только сама команда.

MOV — это основная команда Ассемблера, она встречается в программах гораздо чаще остальных.

Для программирования самое важное понятие — переменная. На самом деле переменная лишь абстракция для удобства программирования.

Такая же абстракция, как в алгебре X, Y, Z. Чтоб решить какую-нибудь задачу, мы говорим, что Y будет равен 5, а Z будет равен 3.

Так вот присвоение переменной Y значения 5 на Ассемблере будет выглядеть так:

mov Y,5

Я расскажу только об основных командах и только самое главное. Полную информацию всё равно нужно брать из справочника. Но каждая новая команда будет оформлена в моей статье почти как в справочнике.

Команда MOV
Происхождениеот англ. слова move — движение, перемена места
Форматmov приёмник , источник
ДействиеКопирует содержимое источника в приёмник.
ПримечаниеMOV не может передавать данные между двумя адресами оперативной памяти (для этой цели существуют команды MOVS)

Этого достаточно, чтоб использовать команду в своих программах. Посмотрите ещё раз на первые две строки prax01:


Имена    Операнды  комментарии
команд  

mov      ah,09   ; поместить значение 9 в регистр AH
mov      dx,010D ; поместить значение 010Dh в DX

Поняли?

Получив первую инструкцию, процессор выполнит инициализацию своего 8-битного регистра AH значением 9, после чего регистр AH будет содержать только байт 09.

При выполнении второй команды процессор поместит в свой 16-битный регистр DX значение 010Dh, после чего регистр DX будет содержать только эти два байта 01 и 0Dh.

Причём, если вы ещё раз посмотрите на устройство регистров, вы обязательно поймёте следующее:
Так как регистр DX состоит из DH и DL, то можно сказать, что после выполнения второй строки кода программы в регистре DH окажется значение 01, а в регистре DL окажется значение 0Dh.

   DH DL
DX=01 0D

Это просто, но важно! В 16-битный регистр (AX,BX,CX,DX) нельзя положить значение больше двух байт (FFFFh).

А в 8-битный (AH,AL, BH,BL, CH,CL, DH,DL) нельзя положить больше байта, то есть FFh.

И еще, допустим:

EAX=99884433
 AX=    4433
 AH=    44  
 AL=      33

Вы должны понять, что физически есть только 4 байта (99 88 44 33h). По отдельности можно обращаться к AX за значением 4433h, или к AH за 44h, или к AL за 33h. Но 9988h находится в E-части, а у неё нет собственного имени, она не является подрегистром. Вы не можете прочитать или загрузить 2 старших байта такого регистра, не обратившись ко всему регистру. Пример:

mov  EAX, 0FFFFFFFFh  ; Так правильно, и EAX будет равен FFFFFFFF
mov  EAX, 01FFFFFFFFh ; Так НЕправильно. Значение больше регистра,
                      ; данной операции быть не может
mov  EAX, 0           ; Так правильно, и EAX станет равен 00000000
mov  AX,  0FFFFh      ; Так правильно, и EAX будет равен 0000FFFF
mov  AX,  1FFFFh      ; Так НЕправильно. Значение больше регистра,
                      ; данной операции быть не может
mov  AX,  0           ; Так правильно, и AX станет равен 0000
mov  AH,  111h        ; Так НЕправильно.
Значение больше регистра, ; данной операции быть не может mov AL, 100h ; Так НЕправильно. Значение больше регистра, ; данной операции быть не может mov AL, 0BBh ; Так правильно, и EAX станет равен 000000BB mov AH, AL ; так правильно, и EAX станет равен 0000BBBB

Это, думаю, понятно. А к старшей части EAX отдельно обращаться можно, например, при помощи команд сдвига битов:

shl EAX,10h           ; Сделает теперь регистр EAX равным BBBB0000
shr EAX,10h           ; А эта команда сделает его обратно равным 0000BBBB

О командах сдвига потом напишу подробно, сейчас я их привёл, только чтоб ответить на вопрос, как можно отдельно читать/записывать 2 старших байта E-регистров.

Итак, мы «познакомились» с четырьмя регистрами общего назначения (РОН), и есть ещё четыре.

Регистры-указатели

Они тоже входят в группу РОН. В этой программе нам они безразличны, но далее мы научимся работать и с ними. Причём на практике всё будет очень похоже на регистры данных. Да и вообще отличий между ними совсем немного. Главное отличие в том, что в регистрах-указателях нет подрегистров, есть только одна вложенная часть.

ESP=44332211   (Extended Stack Pointer)
 SP=    2211   (Stack Pointer)

Мы опять видим четыре байта, ведь все E-регистры 32-разрядные (32 бита — это 4 байта).

EBP включает в себя BP   (Base Pointer)  указатель базы
ESP включает в себя SP   (Stack Pointer) указатель стека
ESI включает в себя SI   (Source Index)  индекс источника
EDI включает в себя DI   (Deliver Index) индекс приёмника

Эти страшные слова «указатель базы», «индекс» на самом деле для нас практически ничего не означают. Вы можете забыть о назначении этих регистров и совершенно свободно использовать регистры ESP, EBP, ESI, EDI, как вам захочется. Только нужно сохранять их содержимое на время использования и восстанавливать обратно. Поэтому сейчас не забивайте голову индексами, базами и стеками. Хотя как раз про стек мы скоро поговорим.

Я уже рассказал почти обо всех регистрах, с которыми нам придётся иметь дело. Остались 2 специальных регистра: EIP и E-flags.

Регистр адреса текущей машинной команды — EIP

Процессор берёт из памяти машинную команду и увеличивает текущий адрес так, чтобы он указывал на следующую команду. Именно для этого и существует EIP.

EIP=44332211   (Extended Instruction Pointer)
 IP=    2211   (Instruction Pointer)

И опять посмотрите на первые две строки кода в отладчике:


   Адрес  Байты    Имена    Операнды

    0100: B409     mov      ah,009
    0102: BA0D01   mov      dx,0010D

Обратите внимание на столбик с байтами. Первая инструкция занимает в памяти два байта (B4h — байт кода операции, 09 — значение в команде). А вторая уже 3 байта, так как значение больше. Есть инструкции, которые занимают аж 15d байт.

Упростив процес выполнения машинной команды, можно сказать так: процессор знает, что если в первом байте содержится код операции 1011 0???b (от B0h до B7h), то это означает, что машинная команда занимает два байта. Тогда он увеличивает значение регистра EIP на два. Затем, выполнив саму маш.команду, он берёт следующий байт по адресу, указанному регистром EIP, и, узнав, что там инструкция с опкодом 1011 1???b (от B8h до BFh), увеличивает значение EIP на три. И так далее.

Таким образом, когда мы смотрим на выполнение программы в отладчике, регистр EIP всегда будет содержать адрес следующей команды процессора, которая ждёт выполнения (примечание).

Есть такие команды Ассемблера, цель которых просто изменить регистр EIP, например, jmp (от слова jump — прыгать). Допустим, ЦП получает машинную команду EB 10h, тогда он выполняет изменение EIP и сразу же приступает к выполнению следующей инструкции — той, что теперь будет указана в EIP.

Теоретически все это запомнить очень сложно и, самое главное, не нужно. Потому что на практике всё станет просто как 2х2.

Мы ещё не обсудили регистр Eflags, который для нас будет очень важен. Говорить о нём без примера программы, мне кажется, глупо. Скоро и до него доберёмся.

Также вы очень часто будете натыкаться на сегментные регистры (CS,DS,ES,FS,GS,SS). Они фигурируют во всех справочниках, отображаются в отладчиках и даже указываются в командах (например: mov byte ptr DS:[EBX],01).

Но сегментные регистры нужно обсуждать вместе с сегментацией памяти, а это отдельная тема, не первой важности.

На сегодняшнем этапе развития процессоров и операционных систем понимание сегментации можно отложить до следующего витка.

Думаю, что для третьего дня теории вполне достаточно, даже, наверное, через край, вы уж извините.

Проведите prax01.com по отладчику CodeView самостоятельно (клавишей F10). Повторите этот процесс несколько раз, посмотрите, как будут меняться регистры. Найдите в окне памяти[5] код и данные программы. Чем больше времени вы будете проводить в отладчике, тем быстрее вы станете толковым программистом.

Я очень хорошо помню своё обучение. Самые сложные дни уже позади, ещё пару дней будет трудно и непонятно.

Осталось совсем немного корпеть над учебниками, скоро будут сплошные приключения, держись, юнга.

Bitfry


— CodeView DOS’овый отладчик, он не любит длинных, русских или сложных путей, поэтому рекомендую держать его папку в корне диска.

Форточки будут тормозить, пока программа в отладчике. И другие вы загрузить не сможете. Похожие фокусы есть во всех дебагерах — такой уж класс программ.

Кроме того, ДОС-программы в хрюшке сами по себе здорово тормозят машину. Но для нас это не критично… Я надеюсь :)

Чтоб в CodeView всё выглядело именно так, нужно включить в «Options» пункт «32-bit registers». И для правильного отображения нужно выключить «Screen Swap» (если брали CV у меня — всё уже настроено).

— Регистр EIP указывает на следующую инструкцию, которая будет выполнена, только с точки зрения внешней архитектуры процессора. Если говорить о процессоре изнутри, то изменение EIP и выполнение действия нескольких инструкций может происходить параллельно (в разных блоках).

Опять же земля не плоская, но для нас в отладчике — это так. :)


регистр EIP

 
Zorrow   (2003-11-25 17:22) [0]

Всем привет

Можно ли узнать содержимое регистра eip, не используя GetThreadContext? Напомню, что в блоке asm дельфи не позволяет обратиться к этому регистру.
всем спасибо


 
Dimka Maslov   (2003-11-25 17:24) [1]

вопрос а для чего нужно узнавать значение регистра EIP? Оно всё равно изменяется


 
Digitman   (2003-11-25 17:24) [2]

накой он тебе сдался ? можешь пояснить ?


 
Zorrow   (2003-11-25 17:26) [3]

В регистр EIP заносится текущий исполняемый адрес.
нужно изнутри функции узнать ее адрес.


 
Digitman   (2003-11-25 17:28) [4]


> В регистр EIP заносится текущий исполняемый адрес

да … заметь только — при исполнении каждой очередной маш.инструкции !


> нужно изнутри функции узнать ее адрес

зачем ?? объясни вразумительно … возможно, существует иное, гораздо более простое и изящное решение


 
Dimka Maslov   (2003-11-25 17:31) [5]

узнать адрес функции можно так:
lea eax, func
где func — имя функции


 
Zorrow   (2003-11-25 17:39) [6]

да . .. заметь только — при исполнении каждой очередной маш.инструкции !
Да уж заметил, так это не мешает мне просто от значения, считанного с eip, отнять чило пройденных инструкций.
mov eax, eip
sub eax, Number

Ок объясняю:

Как некогда заметил столь догадливый Digitman, я пишу перехватчик апи — и не простой, а …нет, не золотой, а в объектном виде.

так вот, перехват будет ставиться на метод моего объекта.
Вызов функции с неизвестным числом параметров я уже реализовал, но при этом получить доступ к свойствам и методам объекта можно только, зная адрес его экземпляра. Скажем, есть функция, кот. по адресу метода объекта, может определить адрес экземпляра (например, через список). Вот здесь и требуется передавать в эту функцию искомый адрес функции


 
Digitman   (2003-11-25 17:48) [7]


> отнять число пройденных инструкций

почем ты знаешь заранее, СКОЛЬКО инструкций выполнено с момента передачи управления п/программе ?

кр. того, длина разных инструкций разная … это м.б. и 1 байт и 10 байт и …

в общем, если тебя приспичило, см. Dimka Maslov © (25.11.03 17:31) [5]


 
Zorrow   (2003-11-25 17:50) [8]

О, придумал!!!
можно вызвать каую-нибудь функцию изнутри того метода, при этом в стек запишется адрес для возврата, отсюда и будем плясать


 
Zorrow   (2003-11-25 17:52) [9]

>Digitman
почем ты знаешь заранее, СКОЛЬКО инструкций выполнено с момента передачи управления п/программе ?

а в CPU window я что, не могу заглянуть
ну я уже все сделал, спасибо


 
Dimka Maslov   (2003-11-25 17:53) [10]

Нет функции, которая по адресу метода объекта может определить адрес его экземпляра. Все методы объекта — есть обычные процедуры и функции, адреса которых определены после запуска приложения.


 
Zorrow   (2003-11-25 17:55) [11]

>Dimka Maslov
🙂 уже есть


 
Digitman   (2003-11-25 17:56) [12]


> а в CPU window я что, не могу заглянуть

да какой тебе еще CPU Window ?) ты ж в ран-тайм это собрался делать, для любого метода ! Насколько я понял) …


 
Dimka Maslov   (2003-11-25 17:58) [13]

>Zorrow (25.11.03 17:55) [11]
А если есть несколько экземпляров объектов одного класса?


 
Zorrow   (2003-11-25 17:58) [14]

Эх вы, мастера
долго объяснять, вот закончу, выложу в готовые программы


 
Dimka Maslov   (2003-11-25 18:01) [15]

var
T1, T2: TObject;
begin
T1 := TObject. Create;
T2 := TObject.Create;

а теперь зная адрес TObject.Free определи значения T1 и T2


 
Digitman   (2003-11-25 18:07) [16]


> Zorrow

по-моему, ты занимаешься ерундой

тебе следовало двигаться в сторону kernel-level-отладчика (!), имеющего кроме всего прочего функц-ть известного дизассемблера DeDe … все остальные потуги на этом поприще трудно назвать сколь-либо серьезными и оправданными


 
MBo   (2003-11-25 18:09) [17]

>Dimka Maslov
TMethod, видимо, имеется в виду


 
Digitman   (2003-11-25 18:21) [18]


> вот закончу, выложу в готовые программы

«Вырасту, выучусь, куплю тебе, Папакарло, дюжину новых курточек !»

(с)

))))))))))


Эксплойт

— Что означает EIP?

Одной из основных вещей, которую должен отслеживать компьютер, является то, где в памяти находятся выполняемые в данный момент инструкции.

Обычно это делается ЦП с использованием так называемого регистра указателя команд или счетчика программ . Точное название зависит от архитектуры, но концепция универсальна для всех архитектур. Он называется Instruction Pointer 9.0006 на процессорах Intel , и этот термин я буду использовать в этом ответе.

Указатель инструкции постоянно увеличивается, пока инструкции выполняются из памяти, а также обновляется всякий раз, когда выполняется инструкция перехода ( jmp , jle и т. д.). Современные архитектуры ЦП с несколькими конвейерами параллельного выполнения и несколькими ядрами усложняют то, что на самом деле происходит внутри ЦП , но для внешнего наблюдателя простого объяснения достаточно, чтобы понять, что происходит, и цель соответствующего регистры.

В силу того, что старые Intel 8086/8088 , 80186 и 80286 были 16-разрядными процессорами , указатель команд обычно выражался как пара 16-разрядных значений, известных как код 0. Сегмент (код , селектор в 80286 , но, за исключением защищенного режима, селекторы функционируют очень похоже на сегменты) и указатель инструкций.

На языке ассемблера, CS:IP , где двоеточие указывает на пару сегмента и смещения для формирования адреса памяти. CS и IP здесь Регистры ЦП — крошечные ячейки памяти внутри самой микросхемы ЦП — в данном конкретном случае каждая имеет размер 16 бит .

У вас также были такие вещи, как DS:DX , который обычно был местом, где считывались или записывались данные в память; DS был сегментом данных , а DX является регистром общего назначения, который в данном случае содержал смещение сегмента данных. Первоначально существовало четыре регистра, предназначенных для общего пользования, именуемых 9.0007 AX , BX , CX и DX соответственно, каждый из которых может быть адресован в терминах старшей половины — AH , например — или младшей половины — AL , плюс небольшое количество регистров общего назначения, предназначенных для конкретных целей. Современные ЦП имеют гораздо больше регистров, причем десятки регистров являются обычными, а более сотни не являются чем-то необычным.

При создании ЦП 80386 в конце 1980-х Intel внесла множество изменений, в том числе расширила адресное пространство до 32 бит , сохраняя при этом селекторы как концепцию . По этой причине старых 16-битных значений стало недостаточно, и указатель команд был расширен до 32 бит. Чтобы поддерживать полную обратную совместимость (в этот момент Intel, вероятно, усвоила урок 80286, который был более односторонним; было легко перейти от 8086/8088 реального режима к 80286 защищенный режим , но вернуться назад было намного сложнее, и это оказалось проблемой на практике), для этого требовался новый регистр для ЦП, чтобы сохранить указатель инструкций при работе в 32-битном режиме .

В 80386 расширенные регистры (32-битные) были названы Exx , тогда как соответствующие 16-битные регистры ранее назывались xx . Таким образом, вы получите, например, старый 16-битный накопительный регистр AX плюс расширенный 32-битный накопительный регистр EAX AX в качестве младшей 16-битной половины ). В соответствии с этим соглашением об именах, расширенный указатель инструкций 9.Регистр 0006 назывался EIP .

Кроме того, чаще всего правильным значением является не просто EIP , а CS:EIP , потому что значение EIP остается смещением от некоторого начального местоположения, которое определяется кодом. селектор. Сборка

— В чем разница между регистрами ESP и EIP

Задай вопрос

спросил

Изменено 4 года, 5 месяцев назад

Просмотрено 27 тысяч раз

В чем разница между регистрами ESP и EIP на следующих примерах? Объясните, что делает код.

 основной ПРОЦ
    0000 0020 позвонить MySub
    0000 0025 мов eax, ebx
        .
        .
    главная ENDP
MySub PROC
    0000 0040 перемещение eax, edx
        .
        .
    рет
MySub ENDP
 

0000 0025 — смещение инструкции сразу после инструкции CALL

0000 0040 — это смещение первой инструкции внутри MySub

Инструкция CALL помещает 0000 0025 в стек и загружает 0000 0040 в EIP

 |-------------| |----------|
| 0000 0025 |<--ESP | 0000 0040| EIP
|-------------| |----------|
| |
|-------------|
| |
|-------------|
 

Инструкция RET извлекает 0000 0025 из стека в EIP (показ стека перед выполнением RET)

 |--------------| |----------|
| 0000 0025 |<--ESP | 0000 0025| EIP
|-------------| |----------|
| |
|-------------|
| |
|-------------|
 
  • сборка
  • x86
  • процедура
  • подпрограмма
  • irvine32

4

EIP — указатель инструкции.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *