Каждая ячейка памяти имеет свой адрес - 32 битное смещение (в байтах) от начала сегмента. Сегмент определяется сегментным регистром (cs, ds, ss, es, fs, gs) и может указываться в командах при работе с памятью. Некоторые команды не поддерживают префикс замены сегмента. Рассмотрим пример на основе команды mov.
Пример:
mov eax,fs:[0] - поместить в регистр eax число из сегмента, селектор которого находится в регистре fs, со смещением от начала сегмента - 0.
Т.к eax - 32 битный регистр, то из памяти считывается 32 бита. Нельзя использовать более одного операнда "память" в команде.
-------------------------------
Смещение указывается в квадратных скобках, и может быть следующего формата: [Base+Index*{1,2,4,8}+число.] где Base - любой 32 битный регистр общего назначения, а Index - любой 32 битный регистр общего назначения кроме esp. Каждое из слагаемых может отсутствовать.
Пример:
mov ecx,dword ptr [eax+ecx*4-10] ;поместить в ecx двойное слово по адресу eax+ecx*4-10 mov cx,word ptr [edx+43] mov byte ptr [ecx+edx],al ;записать в память по адресу ecx+edx значение регистра al
В документации смещение в памяти часто называют указателем (pointer) или адресом. Разные команды работают с разными сегментами памяти по умолчанию. Windows устроен так, что вы можете использовать все сегменты как один и тот же (кроме FS, используемый для SEH). Пока не указывайте префиксы замены сегмента, при адресации ячейки памяти обращайте внимание только на смещение.
-------------------------------
Обозначение памяти в программе.
Для обозначения строки (массива) байт, слов или двойных слов используются операторы db, dw и dd соответственно.Текстовые строки заключаются в двойные или одинарные кавычки.
Пример:
AnyLabel db 10 ;Определить 1 байт, равный 10
MsgString db ′Текстовая строка′,0 ;определить текстовую строку, с последним байтом равным нолю (ASCIIZ формат. Часто используется функциями Windows. Ноль обозначает конец строки. В документации упоминается как "null-terminated string")
TenX db 10 dup (′X′),′YZ′ ;определить 10 байт, равных символу "X", при помощи оператора dup и два последних байта "YZ"
UnDefWord dw ? ;определить слово, изначальное значение которого нам неважно.
-------------------------------
Типы данных word и dword хранятся в памяти в перевернутом виде. При считывании двойного слова из строки байт MsgString получим значение "скеТ" Для обозначения меток в программе используют следующую форму записи: AnyLabel: Ими обычно обозначаются участки программы, на которые передается управление при помощи команд перехода. Метки и названия переменных не могут начинаться с цифры, состоят из любой комбинации английских букв и символов "_", "$", "@", "?". Последние три символа лучше не использовать.
Для получения адреса в памяти метки используется оператор offset. Чтобы узнать размер массива данных используйте оператор size Для переопределения типа данных используется оператор ptr. Оператор $ используется для получения смещения текущего байта.
Пример:
mov ecx,offset SomeDwords+4 ;поместить в регистр ecx адрес в памяти строки SomeDwords +4. mov cl,size TenX ;поместить в cl размер массива TenX (12). mov edx,dword ptr [MsgString] ;поместить в edx двойное слово (4 байта - 4 символа) из строки MsgString. Т.к edx - 32 битный регистр, а строка MsgString определена как строка байт, то нужно использовать оператор переопределения типа.
Символы - это 8 битные числа. Для определения числового значения символа можно использовать таблицу символов из Windows.
-------------------------------
Сегменты в программе.
Данные указываются в сегменте данных, обозначаемым как .data Соответсвтенно код (команды процессора) надо писать в сегменте кода, т.е после .code Каждая страница памяти (в Windows для пользовательских программ как правило ее размер 4 кб) может иметь свои атрибуты. Попытка записи в сегмент кода приведет к ошибке программы.
-------------------------------
Стэк (Stack)
Стэк - это область оперативной памяти, начало которой находится по адресу ss:esp Значение регистра esp называется адресом вершины стэка. Для помещения числа в стэк используется команда push, для извлечения - pop. При помещении 32 битного числа в стэк (push), происходит следующее:
1)уменьшение значения регистра esp на 4 ( размер dword′а). 2)запись числа по адресу ss:esp Таким образом стэк "растет" вниз, от больших адресов памяти к меньшим.
Пример:
push eax ;поместить значение регистра eax в стэк В стэк желательно помещать только 32 битные значения. pop ecx ;извлечь из стэка число в регистр ecx Команда pop ecx работает следующим образом: mov ecx,dword ptr ss:[esp] add esp,4
После выполнения этих команд (или команды pop ecx) значение ячейки памяти по адресу [esp-4] не изменится, но считается недействительным т.к. следующая команда push, другие команды работы со стэком, или возникшее прерывание перезапишут это значение, а также другие, с еще меньшим смещением.
-------------------------------
Строчкой .model flat,stdcall задается модель памяти (flat для программ под Windows), и алгоритм работы макроса вызова процедур (stdcall для функций Windows).
-------------------------------
.386 ;используем команды 386 процессора ;все последующие процессоры их также поддерживают. .model flat,stdcall ;программа под Windows (см выше). MAX_COMPUTERNAME_LENGTH equ 15 UNLEN equ 256 extrn GetComputerNameA:proc ;используемые внешние функции extrn GetUserNameA:proc extrn MessageBoxA:proc extrn ExitProcess:proc .data ;сегмент данных MTitle db ′Computer / User name′,0 ;ASCIIZ строка - заголовок сообщения CompNameSize dd MAX_COMPUTERNAME_LENGTH+1 UserNameSize dd UNLEN+1 MBuffer db MAX_COMPUTERNAME_LENGTH+UNLEN+1+3 dup (?) ;неопределенный массив. ;В нем будет формироваться строка с именем компьютера и пользователя .code ;сегмент кода start: ;метка начала программы ;функция GetComputerNameA возвращает имя компьютера по адресу из первого параметра, ;и количество символов в имени по адресу из второго параметра. call GetComputerNameA,offset MBuffer,offset CompNameSize mov eax,offset MBuffer ;eax= адресу массива MBuffer ;(в котором уже находится имя компьютера) add eax,dword ptr [CompNameSize] ;добавим к eax количество символов в ;имени компьютера. Получим адрес конца текстовай строки. mov dword ptr [eax],′X / ′ ;добавим к концу строки 4 символа (X для теста). add eax,3 ;прибавим к смещению конца строки 3 ;(по адресу eax будет символ X) call GetUserNameA,eax,offset UserNameSize ;передадим в качестве адреса ;для возврата имени пользователя конец строки, по которому ;находится символ X, он будет перезаписан) call MessageBoxA,0,offset MBuffer,offset MTitle,0 ;отобразим строку, ;состоящую из имени компьютера, разделителя и имени пользователя. call ExitProcess,0 ;вызовем функцию завершения программы. ;если этого не сделать, выполнение пойдет дальше ;и Windows выведет сообщение об ошибке. ends ;конец сегментов (можно и не писать) end start ;конец программы, точка входа