Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование на ассемблере под Windows с нуля


Информационный Канал Subscribe.Ru


В поисках истины

или программирование на ассемблере под Windows с нуля.


Выпуск №6

Сегодня в выпуске:


Обращение к подписчикам

   

    Вот и вышел очередной выпуск рассылки. Вы, наверно, уже решили, что рассылка будет выходить раз в неделю. Не беспокойтесь, больше перебоев в выпуске рассылки не будет, надеюсь.
    Сегодня ОЧЕНЬ ВАЖНАЯ тема, если вам станет что-то не понятно ОБЯЗАТЕЛЬНО пишите. Я всегда буду рад вам помочь.



Глава №3

Что-то с памятью моею стало...

Начальные сведения об организации памяти.

    При написании этого выпуска у меня возникла дилемма, то ли углубляться в такие дебри, из которых не вылезешь и за несколько выпусков, либо бросить пару общих фраз, которыми отделываются туториалщики и некоторые авторы книг. Подумав хорошенько я решил из двух зол выбрать меньшее - общие фразы, и в бой =), т.е. писать программы. А когда будет небольшой практический багаж, тогда может и обсудим организацию памяти на самом низком уровне. Это зависит только от вас, захотите узнать, напишите, я сделаю спецвыпуски про организацию памяти. Ну, что же, вперед.

    Сперва, скажу что такое адрес. Адрес - это номер ячейки памяти. Элементарная ячейка памяти размером в один байт. Когда я говорю, что переменная А находится в памяти по адресу 00123456, это значит что это число занимает ячейки в памяти, начиная с ячейки с номером 00123456.

   Если вы помните, я говорил, что младший байт слова расположен по меньшему адресу. Что есть младший байт? В слове 0ABCDh, младшим байтом является CD, соответственно старший байт - АВ. Хотя, это вы уже должны знать ;-). Итак, возьмем наше число 0ABCDh, и запишем его, так как оно будет расположено в памяти. Записано оно будет так - СD AB. Ячейка расположенная "левее" имеет меньший номер, поэтому младший байт будет записываться в "левую" ячейку, старший в "правую". Теперь, возьмем какое-нибудь двойное слово - 089ABCDEFh. В данном случае срабатывает тоже правило, младшее слово по младшему адресу, и "внутри" каждого слова, младший байт по меньшему адресу. Поясняю, CDEF - младшее слово, 89AB - старшее слово. Теперь запишем это число так как оно располагается в памяти -

EF CD AB 89

   Как тоже самое сделать проще? Возьмем число еще большое чем двойное слово - учетверенное слово (qword) - 089ABCDEF12345678h. А теперь пишем это число задом на перед, соблюдая одно правило, полубайты в байтах не меняем местами. Для своего удобства, вы можете разбить это число на байты 89 AB CD EF 12 34 56 78, и записывать их в обратном порядке. И мы получаем

78 56 43 21 EF CD AB 89

   Теперь надо убедится в том, что это правда ;-), а то мало ли что я наговорю =). А лучший способ проверить теорию - написать программу. Но прежде чем мы это сделаем, я расскажу о еще не знакомых вам ассемблерным директивах - директивах резервирования и инициализации данных (читай памяти). Существует этих директив всего 7 штук. Я вам расскажу сегодня только о трех. Но перечислю все, чтобы не обижались на меня ;-), что укрываю от вас нужную информацию. А что эти директивы делают? Согласно своему названию, они резервируют определенное количество байт в памяти и инициализируют его каким-либо значением. Может быть резервирование без инициализации, переменные образованные таким способом называются неинициализированными. Вот табличка со всеми директивами резервирования и инициализации.

Директива Размер данных
db 1 байт
dw 2 байта (слово)
dd 4 байта (двойное слово)
df 6 байтов
dp 6 байтов
dq 8 байтов
dt 10 байтов

   Из всего многообразия вам пока нужно запомнить только три директивы - db, dw, dd. Еще несколько напутственных слов, если у нас переменная число, то мы выбираем директиву в соответствии с размером числа, ну а если переменная - строка, тогда только - db. Почему так? Дело в том, что в таблице ASCII, один символ кодируется одним байтом, таким образом, строки - это последовательность байтов, не важно какой длины наша строка. Мне кажется требуется небольшой примерчик, для ясного понимания, того о чем я сказал.

A db 0ABh              
B dw 0ABCDh            
C dd 089ABCDEFh        
                                      Stroka db "The assembler the best programming language!!! ",0


   Вот еще одна особенность при инициализации переменной строкой, необходимо дописать нуль в конце строки. Нуль в конце строки - это опознавательный знак, что данная строка - ASCII строка. Вот теперь вы готовы написать свою первую программу с данными. Дальше мы делаем все как обычно пишем код в редакторе,

   ;==================================
   ; Модель процессора, модель памяти
   ; Это вы уже знаете
   ;==================================
   .386
   .model flat, stdcall
   option casemap:none
   ;====================================
   ; Это секция инициализированных данных
   ; Тут и развернется основное действо
   ;====================================
   .data
   Abyte db 0ABh ;Присваиваем пременной Abyte значение 0ABh
   rty00 db 0 ;"Разделитель", вставленный мной для удобства нахождения в дампе памяти наших чисел
    Aword dw 0ABCDh ;Присваиваем пременной Aword значение 0ABCDh
    rty01 db 0 ;"Разделитель"
    Adword dd 089ABCDEFh ;Присваиваем пременной Adword значение089ABCDEFh
    rty02 db 0 ;"Разделитель"
    Stroka db "The assembler the best programming language!!! ",0 ;Присваиваем пременной Stroka значение ASM is great
   ;====================================
   ; Секция кода, в ней хорошо знакомая
   ; команда ret
   ;====================================
   .code
   start:
    ret
   end start

сохраняем с расширением *.asm, запускаем батник, с ним я думаю вы уже научились обращаться, если нет читайте предыдущий выпуск, и радостно находим в нашей папке work объектный модуль и программу. Теперь запускаем OllyDbg, загружаем в него нашу програму и смотрим ... А куда собственно смотреть? У нас только одна команда. Смотрим в нижний левый угол там находится окно, разделенное на три части, эти части имеют следующие заголовки: Adress (собственно адрес в памяти), Hex dump (о нем чуть позже), ASCII ("изображение" циферок в ASCII-коде). Hex dump - это всего лишь окошко показывающее, то что находится в памяти в виде шестнадцатеричных цифр, вот третья с.с. где еще себя проявила ;-). Но в этом окне находится содержимое не всей памяти, а только той части, в которой расположены данные. Давайте внимательно посмотрим на в самой левой части окна с заголовком Adress мы видим следующее

00402000
00402010
00402020
00402030
00402040
00402050
...     


   Какую мы можем увидеть закономерность - каждое следующее число больше предыдущего на 10h, именно на 10h, а не на 10d. Можете сами посчитать сколько групп сдвоенных шестнадцатеричных цифр расположено в одном ряду в окошке Hex dump (далее просто окно дампа). Ну, что посчитали? Сколько? Шестнадцать пар цифр, или если в hex - 10. Это не главное. Нам необходимо найти наши числа. Ищем. Чтобы было проще найти числа, я вставил еще "разделители" в виде нулей. Итак первое что мы замечаем - это AB, а после него 00, так скорее всего это наше перовое число, после 00 должно быть второе. Вроде бы оно, только как-то странно написано, и тут бьем себя по голове ;-), с криками, так же ведь и должно быть - младший байт (CD) по меньшему адресу. Смотрим дальше, еще 00, а дальше EF CD AB 89, опять как и обещал, вроде все числа на месте ;-). Дальше в окне дампа видим ещё циферки, которые мы не писали, может быть это наша строка? Да, это она! Глядим в окошечко с заголовком ASCII и видим там после непонятных символов нашу строку. Вот теперь все на месте :-).

   Теперь давайте объединим регистры и память - это велико единение ;-). А объединим их мы с помощью программы, а как же еще ;-).

   ;==================================
   ; Модель процессора, модель памяти
   ; Это вы уже знаете
   ;==================================
   .386
   .model flat, stdcall
   option casemap:none
   ;====================================
   ; Это секция инициализированных данных
   ;====================================
   .data
   A db 050h
   A1 db 064h
   A01 dw 0ABCDh
   A02 dw 0ABEFh
   .code
   start:
    xor eax,eax ;===================
    xor ebx,ebx ; Очищаем регистры
    xor ecx,ecx ; с которыми будем работать
    xor edx,edx ;===================
    mov al, A   ; помещаем значение A в al
    mov bl, A1  ; помещаем значение A1 в bl
    mov cx,A01  ; помещаем значение A01 в cx
    mov dx,A02  ; помещаем значение A02 в dx
    add ax,bx   ; сумируем значение ax и bx
    add cx,ax   ; сумируем значение cx и ax
    sub dx, A01 ; вычитаем из регистра dx значение перемнной A01
ret
end start

   Далее все как обычно запускаем батник, и полученую программу загружаем в отладчик. Посмотрев в окно дизассемблера (верхнее левое), мы кроме знакомых нам команд видим новые. XOR EAX,EAX

XOR EAX,EAX        
XOR EBX,EBX        
XOR ECX,ECX        
XOR EDX,EDX        
        MOV AL,BYTE PTR DS:[402000]
        MOV BL,BYTE PTR DS:[402001]
        MOV CX,WORD PTR DS:[402002]
        MOV DX,WORD PTR DS:[402004]
ADD AX,BX          
ADD CX,AX          
        SUB DX,WORD PTR DS:[402002]
RETN               


   Хотя нет, команды те же самые, только как-то странно записаны команды mov. Вместо ожидаемых mov al, A, мы видим MOV AL,BYTE PTR DS:[402000], непорядок. Вот еще одна особенность применения переменых. Если мы помещаем в регистр значение переменой, то вместо её имя в регистр помещается адрес по которому расположена она, по-моему логично.

   Хорошо, адрес переменной равен 00402000, ну а зачем же тогда ещё какая-то ерунда написана 8-0. Что это такое я сейчас вам расскажу. Дело в том, что программа полностью расположена в момент исполнения в памяти, т.е. и код и переменные. Чтобы процессор отличал где код, а где данные, необходимо указывать адрес с которого начинается сегмент кода, сегмент данных. Адрес формируется достаточно по сложному принципу, сейчас вам надо запомнить одну вещь, адрес может состоять всего из двух компонентов: значения одного из сегментных регистров + смещение от начала сегмента.Смещение - это номер ячейки, относительно начала секции. Что такое сегментные регистры? Эти регистры отличаются от уже хорошо знакомых нам регистров общего назначения. Ну, во-первых у них есть четкая специализация (указывают на начало определнного сегмента), во-вторых они не делимы в отличие от регистров общего назначения. Всего этих регистров шесть штук. В данном случае вы увидели явно работу только с одним - DS, с сегментным регистром данных.

   С небольшой поправкой можно сказать, что в сегментных регистрах содержаться адреса памяти, с которых начинаются соответствующие сегменты. Продолжаем разбираться, кроме DS мы видим ещё несколько незнакомых символов - BYTE PTR, PTR - это оператор переопределения типа, он обычно используется программистом в том случае если необходимо переопределить размер какой-либо переменой во время исполнения программы. Вы скажите но мы ведь этого не делали, да мы не делали, за нас это сделал транслятор, чтобы процессор точно знал, с числом какого размера он будет работать. Нетрудно догадаться, что BYTE PTR сделать переменную размером в байт, WORD PTR - размером в слово. Ну, вот в принципе все понятно. Трассируем F8, и наблюдаем за изменениями в регистрах. Ну и напоследок напишем программу, которая будет содержать секцию инициализированных и неинициализированных данных.

.386
.model flat, stdcall
option casemap:none
;====================================
; секция инициализированных данных
;====================================
.data
    A db 050h
    A1 db 064h
    A01 dw 0ABCDh
    A02 dw 0ABEFh
.data?
;====================================
; секция неинициализированных данных
;====================================
    A00 db ?
    A001 db ?
    A002 dw ?
    A003 dw ?
    .code
start:
    xor eax,eax
    xor ebx,ebx
    xor ecx,ecx
    xor edx,edx
    mov al, A
    mov bl, A1
    mov cx,A01
    mov dx,A02
  mov A00, al
; помещаем значение al в A00
  mov A002,cx ; помещаем значение cx в A002
  mov A003,dx ; помещаем значение dx в A003
  ret
end start

   Конечно, значение регистра можно присваивать и простой, инициализированной переменной, на то она и переменная ;-) , но я в данном случае продемонстрировал то, что данные независимо от их вида располагаются в одном сегменте, и работать с ними можно таким образом. Для того чтобы убедиться в том, что наша программа работает, в очередной раз загружаем её в отладчик и смотрим. А куда смотреть? Смотреть надо на левое нижнее окошечко, после команды MOV BYTE PTR DS:[402008],AL, в памяти по адресу 402008 появится число, сами посмотрите какое ;-).

    Да, не доволен я тем как объяснил данную тему. Но не переживайте, к теме организация памяти будет возвращаться почти в каждом выпуске, т.к. это одна из основных тем при изучении ассемблера. Вот вам первое домашнее задание ;-), напишите программу, в которой будут инициализированные и неинициализированные переменные, а также какие-нибудь операции с ними.

    Ну, и чтобы продвинутые подписчики не сказали, что я сказал про память полную чушь, замечу, я немного переврал (совсем немного) способ получения адреса в памяти. Это связано со сложностью даного материала, как для объяснения, так и для понимания. Поэтому к этому важнейшему вопросы ОБЯЗАТЕЛЬНО вернемя.
За сим прощаюсь, с уважением SeDoYHg[TLRH]

ЗАМЕЧАНИЕ
Хочу отметить один момент, о котором ничего не сказал во время объяснения.В моем изложение встретились такие термины как "секция кода", "секция данных", "сегмент кода", "сегмент кода" - это не тождественные понятия. Сегмент это определнная область памяти, а секция - это часть программы.

Обратная связь

   Если вы хотите что-то спросить по ассемблеру, крэку, или просто поболтать прошу на форум нашей команды, там вы сможете получить ответы от меня, и моих товарищей Mafia32, formatC Вы можете отправить письмо на мой почтовый ящик , только в том случае если вопрос имеет отношение к рассылке. Обязательно заполняйте поле "Тема", письма без темы, я не буду читать. Для вашего удобства я разместил в рассылке e-mail форму, вы можете прямо из нее отправлять свое письмо, но для этого должна быть настроена ваша почтовая программа.

Имя:
Тема:
Сообщение:



Копирайты

Вся информация, содержащаяся в рассылках, является интеллектуальной собственностью своих законных авторов.
Перепечатка и распространение материалов рассылки только с разрешения автора.

Copyright © 2004 SeDoYHg

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу


В избранное