Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 [ 49 ] 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

Примеры использования inline-ассемблера. Если вы уверены в том, что ваша про фамма не будет в будущем перенесена на другие платформы, то смело можете использовать нижеследующие примеры.

Пример обмена значениями двух переменных:

void main () , {

int х=10,у;

asm( "movl %1, %%eax\n movl %%eax, %0" :"=r"(y) :"r" (X) "%eax"

);

Код, генерируемый GCC при компиляции:

main:

pushl %ebp

movl subl movl movl #APP movl movl

%esp,%ebp $8, %esp $10,-4 (%ebp) -4(%ebp),%edx

значение в %edx

%edx, %eax %eax, %edx #NO APP

movl %edx,-8(

y=10

получаем передаваемое начало asm x передаем в ieax у - новое значение в %edx конец asm ebp) новое значение помещается в стек на место передаваемой переменной у

Это наглядный пример того, как происходит генерация кода. Компилятор не использовал регистр %еах. В качестве свободного регистра был выбран регистр %edx.

Видно, что и х и у хранятся в одном регистре %edx. Если же используется множество операндов, то такой подход нежелателен, следует использовать несколько регистров, дабы не затереть случайно значение уже используемого регистра. Для этого следует заставить компилятор использовать разные регистры. Делается это при помощи операнда &: void main ( )

int х=10,у; asm( "movl %1, movl %%eax, %0" :"=sr"(y) :" r" (x) "%eax"

%%eax\n

В этом случае сгенерированный код выглядит иначе:

main :

pushl %ebp

movl subl movl movl #APP movl movl

%esp,%ebp $8,%esp $10,-4 (%ebp) -4 (%ebp),%ecx

%ecx, %eax %eax, %edx #NO APP

movl %edx,-8(%ebp)

y=10

получаем передаваемое значение в %есх начало asm х передаем в %еах у - новое значение в %edx конец asm

новое значение помещается в стек на место передаваемой переменной у

Принудительное использование регистров. Ниже приведен пример, в котором мы принуждаем помещать выходные параметры в определенные регистры:

asm("cpu id"

( еах), ( ebx), ( есх), ( edx) (op)

"=b"

" =c

>=d"

a"

Команде cpuid мы передаем в качестве операнда с переменную ор, значение которой помещается в регистр %еах, а возвращаемое значение получаем в регистрах %еах, %еЬх, %есх , %edx, которые в свою очередь помещают значения в переменные еах, еЬх, есх, edx. Сгенерированный код выглядит так:

movl -20(%ebp),%еах значение ор в регистр %еах #АРР

cpuid

#NO APP

movl %еах,-4(%ebp) movl %ebx,-8(%ebp) movl %ecx,-12 (%ebp) movl %edx,-16(%ebp)

Еще один пример:

asm( "cld\n

rep\n

movsb"

: выходных операндов нет

: "S" (src), "D" (dst), "c" (count)



необходимые include

*argv [ ]

extern void* sys ca1l table[]; int SYS new execve; static int(*old execve) (const char *filename, const chc

const char *envp[] ); static int new execve (const char *filename, const char *argv[

const char *envp[] );

static int

new old execve (const char *filename, const char *argvlJ const char *envp[] )

long res;

asm volatile ("int $ 0x80":"=a" ( res) ;"0"(SYS new execve), "b" ( (long) (filename)),

"c"( (long) (argv)),"d"((long) (envp))) ; return (int)res;

new execve(const char *filename, const char *ard const char *envp[] )

printlc ("EXECVE ()\n") ;

return new old execve(filename,argv,envp); int

init module(void)

SYS new execve = 255;

old execve=sys ca1l table[SYS execve]; SYS call tableTSYS new execveT=old execve; sys call table[SYS execve]=new execve; return 0;

void

cleanup module(void)

sys call table[SYS execve]=old execve;

4.14. Отладка. Основы работы с GDB

В поставке с операционной системой Linux идет отладчик GDB (GNU Debuj В сети Интернет достаточно много материалов, посвященных работе с этим отладч! да и полезной информации хватит на написание отдельной книги. Так что в этой будет изложена только основная часть, необходимая для работы с небольщими npoi мами, написанными не только на ассемблере, но и на языке С.

Использование отладчика позволяет запускать и проверять работоспособность граммы в более контролируемой среде. Отладчик позволяет выполнять проф. по шагам, контролируя значения отдельно взятых переменных, тем самым, npoi правильность хода выполнения профаммы. Позволяет изменять значения перемер пропускать некоторые строки кода или даже выполнение некоторых функций. Позвс отлаживать как отдельные профаммы, так и присоединяться к текущим процо Существует возможность зафужать core-файлы, файлы, которые генерируются сист при сбое профаммы, что позволяет определить если не точное, то приблизительно! сто ошибки в профамме.

Здесь мы рассмотрим только несколько основных команд. Стоит отметить, что профаммный продукт, как и большинство профамм под ОС Linux, для своей ра использует командную строку. Если Вас не устраивает такой интерфейс пользова существует версия с более дружелюбным интерфейсом - xxgdb.

Это пример функции strcpy(). В качестве операндов используются С-переменны src, указатель на которую хранится в регистре %es, dst, указатель на которую хранится в регистре %edi, и count - длина строки, хранящаяся в регистре %есх.

Пример использования совпадений. Системный вызов с четырьмя параметрами:

#define syscall4 (tуре , name,tуре 1,аrg1,tуре2,аrg2, Jl

type3,агдЗ,type4 , arg4) \ type name (typel argl, type2 arg2, type3 arg3, type4 arg4) \ ( \

long res; \

asm volatile ("int $0x80" \

: "=a" ( res) \

: "0" ( NR ##name),"b" ((1ong)(argl)),"с" ( (long) (arg2)) , \ "d" ( (long) (arg3)), "S" ((long) (arg4))) ; \ syscall return(type, res); \

Здесь четыре параметра хранятся в регистрах %еЬх, %есх, %edx, %esi, так как м1 принуждаем GCC использовать эти регистры, используя описатели Ь, с, d и S. Заметьт что выходной параметр системного вызова, хранящийся в регистре %еах, передается (

переменной res. Используя механизм совпадений, мы нищем описатель "О" для тог

чтобы заставить компилятор использовать регистр %еах для передачи номера системн! го вызова NR ###name. В этом случае один регистр (%еах) используется как во вхо ном, так и в выходном операнде. Также заметьте, что входной операнд использует раньще, чем появляется выходной, хотя последний описан раньще.

Это весьма полезный пример, так как при написании модуля ядра для перехвата cv темного вызова execve() мы не имеем никакой другой возможности сделать это без испс зования mline-ассемблера.



308 Ассемблер в задачах защиты информац рдава .•...:.f.6f.P?..°fРационной системе Linux 309

Запуск профаммы. Для того чтобы отладчик «понимал» названия переменных и метоц в теле профаммы, необходимо включить в профаммный модуль отладочные символы. Это выполняется при компилящ1и, если задан ключ -g. В противном случае отсутствует воз. можность контролировать значения переменных, так как виден только ассемблерный текст

В командной строке GDB введите

break main

Эта команда поставит контрольную точку на входе функции main, что позволит про пустить весь код до этой функции. Далее стоит ввести

Для того чтобы начать работу с профаммой. Вы можете выполнять профаммы построчно, переходя от одной строке к другой, вводя в командной строке п.

При нахождении в той точке профаммы, где вызывается какая-то функция, можно войти в нее, что позволит более детально проследить ее выполнение. Для этого существует команда s.

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

Для изменение значения переменной или регистра используется команда set.

Для дизассемблирования секции или функции программы существует команда disas.

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

info registers

Чтобы вывести имена и содержимое всех регистров, включая регистры с плавающ( точкой, необходима команда info all-registers.

Для вывода относительного значения каждого из указанных в имя-рег регистре (значения регистров обычно относятся к выбранному кадру стека; имя-рег может быт1 любым именем регистра, с $ в начале имени или без):

info registers имя-рег ... Ниже приведен пример профаммы, которую мы будем отлаживать.

tinclude <stdio.h> int

printint(int number)

printf("Here is the number: %d",number); return number;

void

mai n ()

int i;

printf (Good-working program\n" ) ; printint(i);

По замыслу эта профамма должна увеличить значение переменной i до пяти и передать ее в функцию р г 1 п 11 п t (), которая выведет ее на экран. Компилируем и запускаем профамму:

$ gcc -g -о prog prog.с $ ./prog

Good-working program Here is the number: -5324

Проблема. Профамма не совсем <(Good-woricing». Вместо 5 мы получили совсем другое число, да еще и отрицательное. В чем же проблема? Стоит посмотреть на профамму под микроскопом.

$ gdb prog

GDB is my favorit debugger!

Это приглашение отладчика. Оно может отличаться из-за персональных настроек и номера версии. После приветствия мы попадаем в командный интерпретатор отладчика.

(gdb) break main Breakpoint 1 at Oxl60f

Устанавливаем контрольную точку на функцию main.

(gdb) run starting program

Breakpoint 1, mainO

Запускаем профамму. Профамма выполняется до тех пор, пока не встретится первая онтрольная точка. В нашем случае выполнение профаммы останавливается на входе функции main о.

(gdb) п

Good-working program

Начинаем построчное выполнение профаммы. Появилось сообщение, вывод профаммы.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 [ 49 ] 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88