Security Teams



» ДРУЗЬЯ: ::: Protocols.Ru ::: Grudina.INFO ::: Damage Lab ::: Securityvulns.RU ::: .:[KZ TeaM]:. ::: X-Hack Team ::: Hack-Lab ::: ALP.org.ua :::
  Ответ в темуСоздание новой темы

> Ассемблер - первые шаги. Часть 2: Основы, (c) drmist
drmist
Дата 8.09.2007 - 09:43
Цитировать сообщение
Offline



Professional
*****

Профиль
Группа: -users-
Сообщений: 1165
Пользователь №: 222
Регистрация: 14.04.2005



Рейтинг:
(0%) -----


Ассемблер - первые шаги. Часть 2: ОсновыВведениеВ предыдущей статьемы написали и скомпилировали нашу первую программу. Данный процесс происходил без особого пониманияисходного кода, однако позволил научится использовать компилятор и линкер, что пригодится нам в будущем. Теперь пришла пораознакомиться с моделью процесса и ресурсами, которыми пользуется программный код во время исполнения, а также некоторымипростыми ассемблерными командами.Модель процессаПонятие "процесс" является абстрактным. Часто под процессом понимается код программы, потоки, хендлы и тпвместе взятые. Таким образом процесс является сложным объектом для изучения. Чтобы как-то структурировать его и упроститьизучение, я предлагаю рассмотреть модель процесса, схематично изображенную на следующем рисунке:Рис 2.1 - Модель процессаСогласно этой модели, основным элементом процесса является код программы, то есть последовательность ассемблерных команд,описывающая алгоритм работы программы. Алгоритмом называется последовательность действий, направленных на решение определеннойзадачи (в будущем мы рассмотрим алгоритм записи строки в файл). Другими словами, код программы описывает ее поведение взависимости от тех или иных условий. Также модель включает в себя три элемента, используемых кодом программы - память,регистры и стек.Надо отметить, что мы рассматриваем однопоточную модель процесса. Потоком называется процесс выполнения машинных команд, длякоторого выделены отдельные регистры и стек. Иногда поток называют нитью или thread'ом. Каждому процессу может принадлежать несколькопотоков, выполняемых одновременно. Как уже было сказанно, каждый из таких потоков имеет собственный набор регистров и стек, нопри этом память и хендлы являются общими для всех потоков в пределах одного процесса.ПамятьПамять служит для временного хранения данных - строк, дескрипторов открытых файлов и тп. Память состоит из байт(а ассемблере обозначается BYTE или DB, иногда байт называют октетом), то есть элементов, способных принимать значенияот 0 до 255 или от 0 до 11111111 в двоичной системе счисления. Если среди читателей имеются те, для кого последнее понятиеявляется незнакомым, я хотел бы посоветовать им обратиться к популярному математическому справочникуwww.google.com.Один разряд двоичного кода называется битом, и может принимать значения 0 или 1. Байт состоит из восьми бит, что влияет надиапазон принимаемых им значений. Как известно читателю из курса комбинаторики, число размещений из двух элементов (0, 1)по 8 с повторениями равно 2 в 8-ой степени, то есть 256(подробности).Часто старший разряд в байте служит для обозначения знака числа, в этом случае в байте может быть записано число от-127 до 128. Никаких признаков того, что байт хранит знаковое или беззнаковое число, нигде не храниться, так байт10010110 может считаться одновременно как числом 150, так и -106. Два байта образуют слово (обозначается WORD или DW),последнее может принимать значения от 0 до 65535 или от -32768 до 32767. Два слова образуют двойное слово (обозначаетсяDWORD или DD). Двойное слово может принимать значения от 0 до 4294967295 или от -2147483648 до 2147483647.Надо обратить внимание на то, что вышесказанное верно только для асма, так как, например, в языке Си есть четкиеразличия между знаковыми и беззнаковыми переменнами.Итак, память можно представить в виде массива (ограниченной упорядоченной последовательности) байт:Рис 2.2 - Байт, слово и двойное слово в памятиЗеленым цветом обозначен байт, для определенности он хранит число 12. Желтым цветом обозначено слово. Как уже былосказано, слово состоит из двух байт. В данном примере его значение равно 12345. Понятно, что в один байт такое большоечисло записать нельзя. Красным цветом обозначено двойное слово, состоящее из четырех байт или двух слов. Значениедвойного слова - 20. Каждому байту в памяти можно присвоить номер. Например, самому первому байту в памяти присвоитьномер 0, следующим за ним - 1, следующему - 2 и тд. Так, байт, обозначеный зеленым цветом, имеет номер 20. Этот номерназывается адресом байта. Адресом слова или двойного слова называется номер первого байта, входящего в это словоили двойное слово. В нашем примере адрес слова равен 1, а адрес двойного слова - 6. Как Вы уже могли заметить,в нашем примере двойное слово, обозначенное красным цветом, хранит адрес "зеленого" байта. В таком случае говорят,что "красный" dword является указателем на "зеленый" байт.Строки в памяти хранятся следующим образом:Рис 2.3 - Строка в памятиНа рисунке изображена строка "Security-Teams.Net" длиной 18 символов, хранящаяся по адресу 32. Каждый символ строкикодируется одним байтом. Соответствие "символ - код" называется кодировкой, в руссифицированных версиях Windowsиспользуется кодировка cp1251. В байте, который кодирует символ "S" записан соответствующий этому символу код - 83,в байте, кодирующем символ "e" - 101 и тд. Завершается строка символом с кодом 0. Он обозначает конец строки.Объем памяти в 1024 байт называется Килобайт, в 1024 Килобайт - Мегабайт, 1024 Мегабайт - Гигабайт, 1024 Гигабайт- Терабайт. Некоторые хитрозадые производители различных носителей информации (жестких дисков, CD/DVD-дисокв, флешек)считают, что в килобайте 1000 байт, в мегабайте 1000 килобайт и так далее, так флэшка объемом 8 Гб может на самом делеиметь только 7.45 Гб.РегистрыРегистром называется память, отличающаяся чрезвычайно большой скоростью доступа к ней. Регистры предназначены дляхранения промежуточных результатов вычислений. Как изображено на рисунке 2.1, чаще всего используются так называемыерегистры общего назначения: EAX, EBX, ECX, EDX, ESP, EBP, ESI и EDI (правильный порядок немного другой, но для нас сейчасэто не имеет значения). Каждый из этих регистров является двойным словом и программист волен использовать их,как ему заблагорассудится. Однако существуют неформальные правила, согласно которым использовать регистры нужно следующимобразом.Регистр EAX используется для хранения результата выполнения предыдущей операции, например, в регистр EAXпомещается результат умножения, а также результат работы функции после ее вызова (функцией называется часть кода,отвечающая за выполнение определенной задачи внутри программы, например рисование окна или открытие файла). Регистр EBXобычно используется в качестве глобальной переменной. Например, в нем может храниться дескриптор открытого файла. РегистрEBX должен оставаться неизменным после вызова функций. ECX обычно используется в качестве счетчика, например при выполнениицикла (часть кода, повторно выполняющаяся несколько раз, перед тем, как перейти к следующей части кода). EDX, как правило,используется для хранения каких-то промежуточных результатов вычислений, но в некоторых случаях используется аналогичноEAX. Регистр ESP используется в качестве указателя на вершину стека (о стеке ниже), а регистр EBP - для хранения адресалокальных переменных при вызове функций. Регистры ESI и EDI используются в качестве указателей, притом ESI обычно указываетна входные данные (например, хранит адрес обрабатываемой строки), а EDI - на выходной буфер (то есть ту часть памяти, куданужно поместить результат какой-то работы). Значения регистров ESI и EDI не должны меняться при вызове функций.Помимо перечисленных регистров, существуют другие, что отображено на следующем рисунке:Рис 2.4 - Регистр EAXКак видно, регистр EAX, как и любое другое двойное слово, состоит из четырех байт, притом мы можем обращаться напрямуюкак к двум младшим байтам (регистры AL и AH), так и к младшему слову (регистр AX). Аналогично, существуют регистры BX, CX иDX, а также BL, BH, CL, CH, DL, DH. Также мы можем обращаться напрямую к младшему слову регистров ESP, EBP, ESI и EDI(регистры SP, BP, SI и DI соответственно). Чтобы обратиться, например, к старшему байту регистра EAX или младшему байтурегистра ESI нужно использовать команды типа побитового сдвига.СтекПредставте себе стопку книг и что Вы пытаетесь достать из нее вторую книгу сверху. Чтобы сделать это, Вампридется сначала убрать из стопки верхнюю книгу. Вот примерно такой "стопкой книг" является стек и примерно по такимправилам приходится с ним работать, только "книгами" в данном случае являются двойные слова:Рис 2.5 - СтекКак видно из картинки, двойные слова следуют одно за другим, а на вершину стека указывает регистр ESP, притом словам,которые находящимся ближе к вершине, соответствуют меньшие адреса, а словам, что находятся ближе ко дну стека -большие. Синим цветом обозначены дынные, несущие какую-то смысловую нагрузку, а голубым - часть памяти, зарезервированнаядля данных, которые в скором времени могут быть помещены в стек.Для помещения двойного слова в стек используется ассемблерная команда push (о командах будет рассказано ниже). Приобработке этой команды значение регистра ESP уменьшается на 4 (размер dword'а в байтах), и в двойное слово, на котороеуказывает ESP (обозначается [ESP]) записываются данные. При извлечении данных из стека (команда pop) производятся обратныедействия - сначала из стека извлекаются данные, на которые указывает ESP, а затем ESP увеличивается на 4.Стек используется для хранения локальных переменных, передачи параметров функциям, а также для хранения адресавозврата (см. ниже).Метод доступа к элементам стека называется FILO, first in - last out, первый вошел - последний вышел. Поанологии иногда используют аббревиатуру LIFO. Понять, откуда взялось такое название, не сложно. Допустим, мы кладем встек по очереди элементы A, B и C. Чтобы достать первый из них, элемент A, сначала нужно достать элементы С и B. То естьэлемент, положенный в стек первым, достается из него последним. Неопытному программисту такой метод доступа можетпоказаться совершенно неудобным, однако это чувство проходит в процессе приобретения опыта создания программ.Важно помнить, что стек не безразмерный. Объем памяти, выделяемой под стек, зависит от ОС и флагов,c которыми была скомпилирована программа. Как правило, размер стека не превосходит десятка килобайт. Таким образом, еслипытаться записать в стек (или считать из стека) слишком много данных, велика вероятность выйти за его пределы, что в 99%случаев гарантирует аварийное завершение приложения, или по крайней мере некорректное его поведение.Код программыКод программы хранится в памяти аналогично строкам или дескрипторам открытых файлов и представляет собойпоследовательность ассемблерный команд, закодированных группами байт переменной длины. Так, команде push eaxсоответствует всего лишь один байт 50h (50 в 16-и разрядной системе счисления, также обозначается 0x50), а командеtest eax, eax последовательность из двух байт: 85h, C0h. Более подробно об ассемблерных командах будет рассказанов следующей части статьи, а пока что мы разберем самые элементарные из них.Команды push и pop Вы уже знаете, они служат для работы со стеком. Пример:push eaxpop ecxpush dword ptr [esi]Первая команда помещает в стек значение регистра eax, вторая считывает только что помещенное в стек двойное слово врегистр ecx. После выполнения этих двух команд в регистрах eax и ecx будут записаны одинаковые значения. Последняя командакладет в стек значение двойного слова, на которое указывает регистр esi. Как уже было сказано выше, квадратными скобкамимы обозначаем, что регистр используется в качестве указателя. Запись "dword ptr" означает, что esi указывает именно надвойное слово. Так как процессор не знает, на что указывает esi - на байт, слово или двойное слово, мы должны указатьэто явно.Команды mov и xchg используются для присовения и обмена значениями соответственно. Пример:mov eax, eсxxchg si, dimov ax, word ptr [eax]Первой командой мы присваиваем регистру eax значение регистра ecx, то есть делаем то же самое, что делают первые двекоманды из предыдущего примера. Вторая команда меняет местами значения регистров si и di. Последняя команда берет значениеслова, на которое указывает регистр eax, а затем кладет это значение в регистр ax. Такая команда имеет право на жизньнесмотря на то, что регистр ax является младшим словом регистра eax.Команды add и sub - команды сложения и вычитания соответственно. Пример:mov ebx, 123456add eax, ebxsub ecx, 12Первой командой мы присваиваем регистру ebx значение 123456, второй командой прибавляем к значению регистра eaxзначение регистра ebx, сумма записывается в регистр eax, таким образом значение регистра eax увеличивается на 123456.Последней командой мы уменьшаем значение регистра ecx на 12.Команда jmp используется для смены адреса обрабатываемого в данный момент кода: jmp address2address1: add eax, 12 jmp address3address2: sub eax, eax jmp address1 address3: push eaxПервой командой мы переходим к метке address2. Выполняем команду sub eax, eax, в результате чего обнуляемрегистр eax. Такой прием часто используется, поскольку команда mov eax, 0 имеет размер 5 байт, а sub eax, eaxвсего лишь 2 байта. Затем переходим к метке address1, прибавляем к eax двенадцать и переходим к метке address3. Врезультате всех этих телодвижений в стек будет помещено значение 12.Команда call используется для вызова функции, а команда ret для возврата из функции: jmp entry_pointsome_proc: mov eax, 0FFh ret entry_point: call some_proc inc eaxПервой командой мы переходим на метку entry_point. Команда call помещает в стек адрес следующей за ней команды ипередает управление по адресу some_proc. Затем в регистр eax кладется значение 0FFh. Тут надо отметить, что если в началечисла в шестнадцатиричной системе счисления не поставить ноль, компилятор решит, что мы хотим поместить в регистр eaxне число 255, а адрес метки "FFh", которую мы не объявили, на что справедливо скажет "udefined symbol : FFh". Однако этоне значит, что все числа нужно записывать с нулем в начале, тк запись вида 0123 означает 123 в восьмиричной кодировке.При вызове ret из вершины стека будет взят так называемый адрес возврата, то есть адрес, помещенный туда командой call,после чего будет произведен переход по этому адресу, то есть к команде inc eax. Последняя увеличивает значениерегистра на единицу. Для уменьшения на единицу служит команда dec.Теперь, когда мы ознакомились с моделью процесса и некоторыми ассемблерными командами, я хотел бы вернуться к рисунку2.1 и обратить внимание на то, что на нем изображена программа во время выполнения. Изначально в регистре eaxбыло записано число 1234, а регистр ebx содержал указатель на строку "Some_proc is called". Затем были выполнены командыpush eax, push ebx и вызвана функция some_proc, в результате чего в стек были помещены сначала значениярегистров eax и ebx, а затем адрес возврата:Рис 2.6 - Программа во время выполненияЗаключениеВ данной статье мы рассмотрели модель процесса, а также некоторые простые ассемблерные команды. В следующей частимы рассмотрим больше ассемблерных команд и напишем еще одну простую программку. В качестве домашнего задания я хотел быпредложить читателю попробовать определить значение регистра eax после выполнения следующих инструкций: mov eax, esp push eax push eax sub eax, esp add esp, eax call some_prog sub ecx, ecx pointer: add ecx, pointer sub ecx, ebx add eax, 039h jmp exit some_prog: mov ebx, dword ptr [esp] retexit: dec ecx add eax, ecxТакже я хотел бы поблагодарить gr8, 500mhz и 0x0c0de за их ценные замечания и указания на неточности в данной статье.12/08/07 © drmist[STNC]Web: www.security-teams.net


--------------------
Когда нельзя еще больше хочется...
PMСайт пользователя
Top
jamall
Дата 16.05.2009 - 11:48
Цитировать сообщение




Unregistered












есть ли продолжение этого цикла статей?
спасибо
Top
nameless
Дата 19.05.2009 - 23:40
Цитировать сообщение
Offline



.:Тень:.
******

Профиль
Группа: -experts-
Сообщений: 1745
Пользователь №: 18
Регистрация: 19.02.2005



Рейтинг:
(90%) XXXXX


Нет, продолжения нету и к сожалению не предвидится.


--------------------
Мы расправим крылья
PM
Top

Опции темы Ответ в темуСоздание новой темы

 





выездной тир на праздник в Киеве