П О Р Т А Л                            
С Е Т Е В Ы Х                          
П Р О Е К Т О В                        
  
Поиск по сайту:
                                                 
Главная

О проекте

Web-мастеру
     HTML & JavaScript
     SSI
     Perl
     PHP
     XML & XSLT
     Unix Shell

MySQL

Безопасность

Хостинг

Другое








Самое читаемое:

Учебник PHP - "Для Чайника".
Просмотров 3524 раз(а).

Иллюстрированный самоучитель по созданию сайтов.
Просмотров 6122 раз(а).

Учебник HTML.
Просмотров 3276 раз(а).

Руководство по PHP5.
Просмотров 5499 раз(а).

Хостинг через призму DNS.
Просмотров 4148 раз(а).

Подборка текстов стандартных документов.
Просмотров 55773 раз(а).

Учебник PHP - Самоучитель
Просмотров 3093 раз(а).

Документация на MySQL (учебник & справочное руководство)
Просмотров 5758 раз(а).

Внешние атаки...
Просмотров 3843 раз(а).

Учебник PHP.
Просмотров 2826 раз(а).

SSI в примерах.
Просмотров 37463 раз(а).






 
 
| Добавить в избранное | Сделать стартовой Project.Net.Ru | Помощь





Вызовы на ассемблере

Мы будем использовать gcc и gdb, чтобы получить инструкции ассемблера соответствующие нашей маленькой программе. Скомпилируем shellcode3.c с опцией отладки (-g) и встроим функции, обычно находящиеся в разделяемых библиотеках, в саму программу при помощи опции --static. Теперь у нас есть необходимая информация, чтобы понять способ работы системных вызовов _exexve() и _exit().
$ gcc -o shellcode3 shellcode3.c -O2 -g --static
Дальше при помощи gdb мы посмотрим на ассемблерный эквивалент наших функций. Все это относится к Linux на платформе Intel (i386 и выше).
$ gdb shellcode3
GNU gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public
License, and you are welcome to change it and/or distribute
copies of it under certain conditions.  Type "show copying"
to see the conditions.  There is absolutely no warranty
for GDB.  Type "show warranty" for details.  This GDB was
configured as "i386-redhat-linux"...
Мы просим gdb отобразить ассемблерный код для функции main().
(gdb) disassemble main
Dump of assembler code for function main:
0x8048168 <main>:       push   %ebp
0x8048169 <main+1>:     mov    %esp,%ebp
0x804816b <main+3>:     sub    $0x8,%esp
0x804816e <main+6>:     movl   $0x0,0xfffffff8(%ebp)
0x8048175 <main+13>:    movl   $0x0,0xfffffffc(%ebp)
0x804817c <main+20>:    mov    $0x8071ea8,%edx
0x8048181 <main+25>:    mov    %edx,0xfffffff8(%ebp)
0x8048184 <main+28>:    push   $0x0
0x8048186 <main+30>:    lea    0xfffffff8(%ebp),%eax
0x8048189 <main+33>:    push   %eax
0x804818a <main+34>:    push   %edx
0x804818b <main+35>:    call   0x804d9ac <__execve>
0x8048190 <main+40>:    push   $0x0
0x8048192 <main+42>:    call   0x804d990 <_exit>
0x8048197 <main+47>:    nop
End of assembler dump.
(gdb)
Вызовы функций по адресам 0x804818b и 0x8048192 запускают подпрограммы библиотеки Си, содержащие настоящие системные вызовы. Заметьте, что инструкция 0x804817c : mov $0x8071ea8,%edx заполняет регистр %edx значением, похожим на адрес. Посмотрим на содержимое памяти по этому адресу, отображая его как строку:
(gdb) printf "%s\n", 0x8071ea8
/bin/sh
(gdb)
Теперь мы знаем, где находится строка. Давайте посмотрим на дизассемблированный код функций execve() и _exit():
(gdb) disassemble __execve
Dump of assembler code for function __execve:
0x804d9ac <__execve>:    push   %ebp
0x804d9ad <__execve+1>:  mov    %esp,%ebp
0x804d9af <__execve+3>:  push   %edi
0x804d9b0 <__execve+4>:  push   %ebx
0x804d9b1 <__execve+5>:  mov    0x8(%ebp),%edi
0x804d9b4 <__execve+8>:  mov    $0x0,%eax
0x804d9b9 <__execve+13>: test   %eax,%eax
0x804d9bb <__execve+15>: je     0x804d9c2 <__execve+22>
0x804d9bd <__execve+17>: call   0x0
0x804d9c2 <__execve+22>: mov    0xc(%ebp),%ecx
0x804d9c5 <__execve+25>: mov    0x10(%ebp),%edx
0x804d9c8 <__execve+28>: push   %ebx
0x804d9c9 <__execve+29>: mov    %edi,%ebx
0x804d9cb <__execve+31>: mov    $0xb,%eax
0x804d9d0 <__execve+36>: int    $0x80
0x804d9d2 <__execve+38>: pop    %ebx
0x804d9d3 <__execve+39>: mov    %eax,%ebx
0x804d9d5 <__execve+41>: cmp    $0xfffff000,%ebx
0x804d9db <__execve+47>: jbe    0x804d9eb <__execve+63>
0x804d9dd <__execve+49>: call   0x8048c84 <__errno_location>
0x804d9e2 <__execve+54>: neg    %ebx
0x804d9e4 <__execve+56>: mov    %ebx,(%eax)
0x804d9e6 <__execve+58>: mov    $0xffffffff,%ebx
0x804d9eb <__execve+63>: mov    %ebx,%eax
0x804d9ed <__execve+65>: lea    0xfffffff8(%ebp),%esp
0x804d9f0 <__execve+68>: pop    %ebx
0x804d9f1 <__execve+69>: pop    %edi
0x804d9f2 <__execve+70>: leave
0x804d9f3 <__execve+71>: ret
End of assembler dump.
(gdb) disassemble _exit
Dump of assembler code for function _exit:
0x804d990 <_exit>:      mov    %ebx,%edx
0x804d992 <_exit+2>:    mov    0x4(%esp,1),%ebx
0x804d996 <_exit+6>:    mov    $0x1,%eax
0x804d99b <_exit+11>:   int    $0x80
0x804d99d <_exit+13>:   mov    %edx,%ebx
0x804d99f <_exit+15>:   cmp    $0xfffff001,%eax
0x804d9a4 <_exit+20>:   jae    0x804dd90 <__syscall_error>
End of assembler dump.
(gdb) quit
Настоящий вызов ядра происходит через прерывание 0x80 по адресам 0x804d9d0 для execve() и 0x804d99b для _exit(). Эта точка входа общая для различных системных вызовов, поэтому различие производится по содержимому регистра %eax. Для execve(), он имеет значение 0x0B , тогда как для _exit() - 0x01.

Диаграмма 4 : параметры функции execve()
параметры функции execve()

Анализ этих ассемблерных инструкций функций дает нам параметры, которые они используют:

  • execve() нужны различные параметры (сравни с диаграммой 4) :
    • регистр %ebx содержит адрес строки, представляющей команду для запуска, в нашем примере "/bin/sh" (0x804d9b1 : mov 0x8(%ebp),%edi а затем 0x804d9c9 : mov %edi,%ebx) ;
    • регистр %ecx содержит адрес массива аргументов (0x804d9c2 : mov 0xc(%ebp),%ecx). Первый аргумент должен быть именем программы, и больше нам ничего не нужно: массива, содержащего адрес строки "/bin/sh" и указатель NULL, будет достаточно;
    • Регистр %edx содержит адрес массива, представляющего собой окружение для запускаемой программы (0x804d9c5 : mov 0x10(%ebp),%edx). Чтобы оставить нашу программу простой, мы будем использовать пустое окружение: указатель NULL сделает это для нас.
  • функция _exit() завершает процесс и возвращает код выполнения родительскому процессу (обычно оболочке), который содержится в регистре %ebx  ;

Поэтому нам нужны строка "/bin/sh", указатель на эту строку и NULL указатель (для аргументов, так как у нас их нет, и для окружения, так как оно у нас пустое). Мы можем увидеть возможное представление данных перед вызовом execve(). Построим массив из указателя на /bin/sh и NULL указателя, %ebx будет указывать на строку, %ecx на весь массив, а %edx на второй элемент массива (NULL). Это показано на диаграмме 5.

Диаграмма 5 : представление данных по отношению к регистрам
данные

Назад | Содержание | Вперед



Если Вы не нашли что искали, то рекомендую воспользоваться поиском по сайту:
 





Copyright © 2005-2016 Project.Net.Ru