Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

articles:elf-trj [2017/09/05 02:55] (текущий)
Строка 1: Строка 1:
 +====== ELF-trj ======
 +<​sub>​{{ELF-trj.odt|Original file}}</​sub>​
 +
 +====== секреты покорения эльфов ======
 +
 +крис касперски ака мыщъх, noe-mail
 +
 +**если загрузить исполняемый файл в ****hex****-редактор,​ мы увидим цифры. много цифр. можно нажать на ноль, наслаждаясь как машинный код стирается под натиском нашей силы, но… это слишком просто и неинтересно. лучше собраться с умом и дописать несколько осмысленных ассемблерных строк! эта статья рассказывает о том, как устроены ****ELF****-файлы,​ как они загружаются в память и как хакеры внедряют в них свои имплантанты**
 +
 +===== введение =====
 +
 +Ворвавшись в нашу жизнь, LINUX прочно обосновался на операционной арене и все чаще и чаще на компакт-дисках гламурных журналов оказываются программы для этой оси. Причем,​ в отличии от Windows, которая проектировалась непонятно (за)чем,​ большинство Линуховых приложений не требует установки и спокойно копируется с одного компьютера на другой,​ что способствует интенсивному обмену файлами. Помните MS-DOS? Какие там дистрибьютивы! Наше поколение таких слов тогда вообще не знало!
 +
 +Считается,​ будто бы обмен исполняемыми файлами в мире LINUX намного ниже, чем в Windows, что большинство пользователей качает исходники и компилирует их самостоятельно. Да как бы не так! Исходники занимают намного больше места, а модем не резиновый,​ это раз! Далеко не всегда сборка проходит гладко и тогда приходиться колдовать над компилятором и исправлять ошибки разработчиков,​ что требует квалификации,​ — это два! Наконец,​ компиляция больших проектов занимает довольно продолжительное время, зачастую намного превышающее время скачки (десятки минут или даже часы) — это три. Есть и другие причины,​ которые мы не будем перечислять здесь. Важно одно — очень многие пользователи предпочитают сливать готовые бинарники,​ скомпилированные для своей оси. Часто такие файлы лежат прямо на официальном сайте производителя. Часто, но не всегда! Есть и другая проблема. Линуховые программисты не заморачиваются с интерактивными конфигураторами и серьезно злоупотребляют "​дефайнами"​ — директивами условной компиляции. Например,​ для однопроцессорной машины создается одна сборка,​ для двух- или четырех-процессорной — другая. Таких опций может быть очень много и выложить все разновидности сборок на официальный сайт просто нереально. А компилировать самостоятельно ‑ лень. Вот и приходится рыскать по сети в поисках готовых сборок,​ откомпилированных независимыми разработчиками и качать их. При этом возникает естественная угроза нарваться на вирус, закладку или троян и такие пришествия уже случались!
 +
 +Доработать исходные тексты проще всего (здесь это даже не рассматривается),​ но что делать,​ если есть только исполняемый файл и больше ничего?​ Берем hex-редактор и в самых ответственных местах правим "​yes"​ на "​no"​ — пускай юзеры потом удивляются! А еще круче внедрить "​часовой механизм",​ который в определенный момент выведет приветствие на экран или… сделает что-нибудь типа того. Вот об этом мы сейчас и поговорим!
 +
 +{{elf-trj_Image_0.jpg?​552}}
 +
 +Рисунок 1 исполняемый файл на тропе войны
 +
 +===== >>>​ врезка что нам понадобиться?​ =====
 +
 +Для правки исполняемых файлов Линух необязателен. Достаточно иметь HIEW, запущенный из-под Windows, или даже MS-DOS, однако,​ в этом случае придется сильно попыхтеть и к тому же — как потом это отлаживать?​ Так что Линух все-таки желателен,​ хотя бы в виде эмулятора — VM Ware, BOCHS или QEMU.
 +
 +Мы будем пользоваться hex-редактором HTE, готовую сборку которого можно бесплатно скачать с http://​hte.sourceforge.net. Он "​переваривает"​ ELF-формат,​ в нем есть мощный ассемблер и прочие вкусности. Как вариант,​ можно воспользоваться редактором BIEW (http://​biew.sourceforge.net),​ но он намного слабее.
 +
 +Желательно иметь ИДУ. Линуховый порт содержит удобный интерактивный отладчик в стиле Turbo-Debugger'​а,​ плюс сам дизассемблер. Если нет ИДЫ, возьмите gdb – стандартный отладчик,​ входящий в штатный комплект поставки большинства Линухов,​ но его возможности сильно ограничены. В частности,​ от отказывается грузить файлы без sectiontable,​ спотыкается на антиотладочный приемах и т.д., и т.п. (подробный обзор отладчиков под Линухом можно найти в моей статье "​особенности национальной отладки в UNIX", опубликованной в "​нннн"​ номере Хакера)
 +
 +Из документации нам в первую очередь понадобиться спецификация на ELF-формат,​ которую можно бесплатно скачать с пристанского сервера:​ http://​www.cs.princeton.edu/​courses/​archive/​fall05/​cos318/​docs/​ELF_Format.pdf и перечень системных вызовов в разных осях — "​UNIXAssemblyCodesDevelopmentforVulnerabilitiesIllustrationPurposes"​ (http://​www.lsd-pl.net/​documents/​asmcodes-1.0.2.pdf).
 +
 +{{elf-trj_Image_1.png?​552}}
 +
 +Рисунок 2 отладка внедряемого кода с помощью интегрированного отладчика,​ встроенного в дизассемблер IDA Pro
 +
 +===== анатомия эльфов и их репродуктивные возможности =====
 +
 +Изначально UNIX (и производные от нее операционные системы) поддерживали множество исполняемых форматов,​ ожесточенно конкурирующих между собой, но теперь поле боя опустело и среди дымящихся обломков минувших сражений остался один ELF, ставший стандартом де-факто для LINUX и BSD. Кое-где еще встречается древний a.out, но на него можно не обращать внимания.
 +
 +Аббревиатура ELF расшифровывается как от //​**E**////​xecution////​ & ////​**L**////​inkable////​**F**////​ormat//​ (формат исполнения и компоновки). Он состоит в определенном родстве с win32 PE, поэтому у них много общего. В начале ELF-файла расположен служебный заголовок (ELF-header),​ описывающий основные характеристики файла — тип (исполнения или линковки),​ архитектура ЦП, виртуальный адрес точки входа, размеры и смещения остальных заголовков…
 +
 +За ELF-header'​ом следует таблица сегментов (programheadertable),​ перечисляющая имеющиеся сегменты и их атрибуты. В формате линковки она необязательно. Линкеру сегменты глубоко фиолетов и он работает исключительно на уровне секций. Напротив,​ системный загрузчик,​ загружающий исполняемый ELF-файл в память,​ игнорирует секции,​ и оперирует целыми сегментами.
 +
 +Сегменты и секции — что это такое? Сегмент — это непрерывная область адресного пространства со своими атрибутами доступа. В частности,​ сегмент кода имеет атрибут исполнения,​ а сегмент данных — атрибуты чтения и записи. Не стоит путать ELF-сегменты с сегментами x86 процессора! В защищенном режиме 386+ никаких "​сегментов"​ в изначальном смысле этого слова уже нет, а есть только селекторы и все сегменты ELF-файла загружается в единый 4 Гбайтовый x86-сегмент! В зависимости от типа сегмента,​ величина выравнивания в памяти может варьировать от 4h до 1000h байт (размер страницы на x86). В самом ELF-файле хранятся в невыровненном виде, плотно прижатые друг к другу. Так что со свободным пространством для внедрения сплошные напряги.
 +
 +Ближайший аналог ELF-сегментов — PE-секции,​ но в PE-файлах,​ секция — это наименьшая структурная единица,​ а вот в ELF-файлах сегмент может быть разбит на один или несколько фрагментов — секций. В частности,​ типичный кодовый сегмент состоит из секций .init (процедуры инициализации),​ .plt (секция связок),​ .text (основой код программы) и .finit (процедуры финализации). Секции нужны линкеру для комбинирования,​ чтобы он мог отобрать секции с похожими атрибутами и оптимальным образом растасовать их по сегментам при сборке файла, то есть "​скомбинировать"​.
 +
 +Несмотря на то, что системный загрузчик игнорирует таблицу секций,​ линкер все-таки помещает ее копию в исполняемый файл. Место тратиться совсем немного,​ зато отладчикам и дизассемблерам так приятнее. По не совсем понятным причинам gdb и многие другие программы отказываются загружать в файл с поврежденной или отсутствующей таблицей секций,​ чем часто пользуются для защиты программ от постороннего вмешательства.
 +
 +{{elf-trj_Image_2.png?​553}}
 +
 +Рисунок 3 структура ELF-формат с точки зрения линкера (слева) и системного загрузчика операционной системы (справа)
 +
 +Структуру и назначение полей служебных заголовком здесь разбирать не будем. Этим займется hex-редактор и нам эти подробности не понадобятся. Интересующиеся могут обратиться к файлу /​usr/​include/​elf.h — там все подробно расписано.
 +
 +Лучше сосредоточимся на загрузке файла в память. По умолчанию ELF-заголовок проецируется по адресу 8048000h, который прописан в его заголовке. Это и есть базовый адрес загрузки. На стадии линковки он может быть свободно изменен на другой,​ но большинство программистов оставляют его "​как есть"​. Все сегменты проецируются в память в соответствии с виртуальными адресами,​ прописанными в таблице сегментов,​ причем,​ виртуальная проекция образа всегда непрерывна,​ и между сегментами не должно быть незаполненных "​дыр"​.
 +
 +Начиная с адреса 40000000h располагаются совместно используемые библиотеки ld-linix.so,​ libm.so, libc.so и другие,​ которые связывают операционную систему с прикладной программой. Ближайший аналог из мира Windows – KERENL32.DLL,​ реализующая win32 API, что расшифровывается как Application Programming Interface, но при желании программа может вызывать функции операционной системы и напрямую. В NT за это отвечает прерывание INT 2Eh, в LINUX – как правило INT 80h (подробнее о различии в реализации системных вызовов можно прочитать в уже упомянутом документе "UNIX Assembly Codes Development forVulnerabilitiesIllustrationPurposes"​) или книге Зубкова "​Ассемблер– язык неограниченных возможностей"​.
 +
 +Для вызова функций типа открытия файла мы можем обратиться либо к библиотеке libc, либо непосредственно к самой операционной системе. Первый вариант — самый громоздкий,​ самый переносимый,​ и наименее приметный. Последний — прост в реализации,​ но при первом же взгляде на дизассемблерный листинг тут же бросается в глаза (правильные программы INT 80h не вызывают!),​ к тому же он испытывает проблемы совместимости с различными версиями LINUX'​а. Вот она — расплата за простоту!
 +
 +Последний гигабайт адресного пространства (от адреса C0000000h и выше) занимают код и данные операционной системе,​ к которым мы будем обращаться только посредством прерывания INT 80h или через разделяемые библиотеки.
 +
 +Стек находится в нижних адресах. Он начинается с базового адреса загрузки и "​растет"​ "​вверх"​ по направлению к нулевым адресам. В большинстве Лихнухов стек исполняем (то есть сюда можно скопировать машинный код и передать на него управления),​ однако,​ некоторые параноидальные администраторы устанавливают заплатки,​ отнимающие у стека атрибут исполняемости,​ но большой распространенности они не получили и ими можно пренебречь.
 +
 +{{elf-trj_Image_3.jpg?​553}}
 +
 +Рисунок 4 карта памяти загруженного образа исполняемого файла
 +
 +===== имплантация чужеродного кода в ELF-файл =====
 +
 +Для экспериментов по имплантации нам потребуется живой исполняемый файл, который при помощи компилятора и текстового редактора мы сможем изготовить и самостоятельно. Нажмем <​Shift-F4>​ в MidnightCommaner'​е,​ наберем программу следующего содержания (см. листинг 1),​ затем <​F2>/"​имя-файла.c"​ и откомпилируем ее своим любимым gcc с настойками по умолчанию (gcc имя-файла.c -o имя-файла).
 +
 +#include <​stdio.h>​
 +
 +main()
 +
 +{
 +
 +printf("​LORDI - the best group in the world!\n"​\
 +
 +"​(www.lordi.org)\nmonsters,​ bondage and sado-maso\n"​);​
 +
 +}
 +
 +Листинг 1 демонстрационная программа,​ в которую мы будем внедрять посторонний код
 +
 +{{elf-trj_Image_4.png?​552}}
 +
 +Рисунок 5 создание демонстрационной программы для внедрения
 +
 +Образовавшийся файл загрузим в hex-редактор ("#​./​ht-0.7.5-linux-i386 имя-файла"​),​ а затем нажмем <F6> (mode) и выберем "​elf/​image"​. Редактор перейдет в режим отображения образа исполняемого файла, автоматически перенося нас в окрестности точки входа, отмеченной меткой "​entrypoint"​. Если этого не произойдет,​ нажмем <F5> (goto), и введем "​entrypoint"​ (без кавычек).
 +
 +Экран должен выглядеть приблизительно так:
 +
 +{{elf-trj_Image_5.png?​552}}
 +
 +Рисунок 6 исполняемый файл в hex-редакторе hte
 +
 +Давайте для разминки просто поменяем первые две команды местами:​ xor ebp,​ebp/​pop esi на pop esi/​xor ebp,​ebp. Подведем курсор к первой машинной команде (она расположена по адресу 80482C2h) и нажмем <​Ctrl-A>​ (Assemble), вводим "​pop esi"​. Редактор предложит несколько вариантов ассемблирования на наш выбор: 5Eh и 8Fh C6h. Выбираем 5Eh, как самый короткий (8Fh C6h просто не влезет в отведенное место!),​ затем точно так ассемблируем команду xor ebp,​ebp.
 +
 +Измененные байты редактор выделят красным цветом (см. рис. 7),​ что наглядно и очень красиво,​ но при нажатии на <F2> (save) они вновь зеленеют,​ подтверждая,​ что все исправления успешно сохранены. Полей контрольной суммы в ELF-заголовке нет и потому заботиться о ее пересчете не нужно. Линух контрольную сумму файла не считает! А не считает он ее потому,​ что проектировался головой. Это же не Windows! Такое впечатление,​ что PE-файл проектировала толпа народу с трудом взаимодействующая между собой. Судите сами: и Линух, и Widows поддерживают механизм отложенной загрузки по требованию. Страницы образа проецируются в память тогда и только тогда, когда к ним происходит обращение,​ в результате чего немедленно после запуска файл уже готов к работе,​ а все недостающие страницы дозагружаются уже потом (или не загружаются вообще,​ например,​ часть программы,​ ответственная за печать,​ вообще не будет загружена,​ если ни разу не был выбран пункт "​print"​). Процесс загрузки как бы "​размазывается"​ во времени,​ не нервируя никакими песочными часами,​ которые так любит демонстрировать Windows. Но! Ведь при подсчете контрольной суммы происходит неизбежное обращение ко всем страницам и все они загружаются в память,​ даже если не нужны. Получается,​ что у нас есть два механизма — один оптимизирует загрузку,​ другой ее "​пессимизирует",​ съедая весь выигрыш. Где логика?​!
 +
 +А вот разработчики Линуха переложили подсчет контрольной суммы на устройства ввода/​вывода,​ которые ее действительно считают. Конечно,​ это не страхует от искажений. В частности,​ жесткие диски контролируют только физические дефекты,​ но не обращают внимания на логические искажения (типа вируса). Тем не менее, особого смысла в контрольной сумме, хранящейся непосредственно в самой файле, все равно нет. Если вирус может модифицировать файл, он модифицирует и контрольную сумму. По науке, контрольные суммы нужно хранить в отдельном "​защищенном хранилище"​ и их подсчетом должна заниматься файловая система или антивирусные ревизоры. Ни того, ни другого в мире Линуха не наблюдается. То есть, они как бы есть, но ни у кого реально не установлены.
 +
 +Единственную проблему представляют протекторы и упаковщики исполняемых файлов,​ контролирующее собственную целостность. С каждым годом их становится все больше и больше. UPX, протектор от shiv'​ы… В них на первых порах лучше не внедряться!
 +
 +{{elf-trj_Image_6.png?​552}}
 +
 +Рисунок 7 модификация исполняемого файла в редакторе hte, измененные байты выделяются красным цветом
 +
 +Но мы отвлеклись. Выходим из hex-редактора,​ нажав <F10> (где мой привычный выход по Escape?!), и запускаем пропатченный файл. Он запускается,​ подтверждая свою работоспособность. Значит,​ модификация прошла успешно! (Ну еще бы! Под моим чутким руководством!)
 +
 +{{elf-trj_Image_7.png?​552}}
 +
 +Рисунок 8 результат работы модифицированного файла после перестановки пары команд местами — полет нормальный
 +
 +А теперь займется более серьезными вещами,​ попытавшись внедрить в программу реальный код, который делает что-то полезное. Сразу возникает вопрос:​ куда мы будет внедряться?​ Между сегментами свободного места нет, между секциями тоже. Можно (теоретически) расширить последний сегмент и внедрится сюда, но во-первых,​ это будет слишком заметно,​ а во-вторых,​ слишком муторно и утомительно.
 +
 +Но все не так плохо, как кажется! По умолчанию gcc выравнивает стартовые адреса функций по границе 10h, а это, значит,​ что даже наш демонстрационный файл содержит просто кучу свободного пространства. В среднем 10h/2h = 8h байт на каждую функцию,​ включая служебные. Сюда и мамонта упрятать можно, если, конечно,​ его предварительно расчленить. Вот, смотрите,​ сами:
 +
 +....... ! main:;xref o80482d7
 +
 +....... !  pushebp
 +
 +8048385 !  movebp, esp
 +
 +8048387 !  subesp, 8
 +
 +804838a !  andesp, 0fffffff0h
 +
 +804838d !  moveax, 0
 +
 +8048392 !  subesp, eax
 +
 +8048394 !  movdword ptr [esp], strz_LORDI___the_best_group_in_the_80484e0
 +
 +804839b !  callwrapper_8049634_80482b0
 +
 +80483a0 !  leave
 +
 +80483a1 !  ret
 +
 +80483a2 ​ nop
 +
 +80483a3 ​ nop
 +
 +80483a4 ​ nop
 +
 +80483a5 ​ nop
 +
 +80483a6 ​ nop
 +
 +80483a7 ​ nop
 +
 +80483a8 ​ nop
 +
 +80483a9 ​ nop
 +
 +80483aa ​ nop
 +
 +80483ab ​ nop
 +
 +80483ac ​ nop
 +
 +Листинг 2 цепочка команд NOP, оставленная компилятором в конце функции main для выравнивания
 +
 +А вот еще одна лазейка — буфер ввода/​вывода,​ расположенный в сегменте данных,​ дамп которого приведен ниже. Это целых 28 байт, которые можно использовать по своему усмотрению! Даже если никаких явных файловых манипуляторов в файле нет (как, например,​ в нашей демонстрационной программе),​ такой буфер все равно создается при компиляции программы,​ что наш случай и подтверждает.
 +
 + ​80484c2db00h ; ' '
 +
 + ​80484c3db00h ; ' '
 +
 + ​80484c4
 +
 + ​.......;​********************************************************
 +
 + ​.......;​ data object _IO_stdin_used,​ size 4 (global)
 +
 + ​.......;​********************************************************
 +
 + ​......._IO_stdin_used:​
 +
 + ​.......db01h ; ' '
 +
 + ​80484c5db00h ; ' '
 +
 + ​80484c6db02h ; ' '
 +
 + ​80484c7db00h ; ' '
 +
 + ​80484c8db00h ; ' '
 +
 + ​80484c9db00h ; ' '
 +
 + ​80484cadb00h ; ' '
 +
 + ​80484cbdb00h ; ' '
 +
 + ​80484ccdb00h ; ' '
 +
 + ​80484cddb00h ; ' '
 +
 + ​80484cedb00h ; ' '
 +
 + ​80484cfdb00h ; ' '
 +
 + ​80484d0db00h ; ' '
 +
 + ​80484d1db00h ; ' '
 +
 + ​80484d2db00h ; ' '
 +
 + ​80484d3db00h ; ' '
 +
 + ​80484d4db00h ; ' '
 +
 + ​80484d5db00h ; ' '
 +
 + ​80484d6db00h ; ' '
 +
 + ​80484d7db00h ; ' '
 +
 + ​80484d8db00h ; ' '
 +
 + ​80484d9db00h ; ' '
 +
 + ​80484dadb00h ; ' '
 +
 + ​80484dbdb00h ; ' '
 +
 + ​80484dcdb00h ; ' '
 +
 + ​80484dddb00h ; ' '
 +
 + ​80484dedb00h ; ' '
 +
 +Листинг 3 stdin-буфер,​ расположенный в сегменте данных
 +
 +Остается решить как передать управление на внедренный код. Это можно сделать различными путями:​ скорректировать точку входа (HTE это умеет) или внедрить в ее окрестности специальный jmp. Вот там мы и поступим!
 +
 +Запускам редактор,​ переходим в точку входа и смотрим на нее очень внимательно:​
 +
 +....... ! entrypoint:
 +
 +....... !  popesi
 +
 +80482c1 !  xorebp, ebp
 +
 +80482c3 !  movecx, esp
 +
 +80482c5 !  andesp, 0fffffff0h
 +
 +80482c8 !  pusheax
 +
 +80482c9 !  pushesp
 +
 +80482ca !  pushedx
 +
 +80482cb !  push__libc_csu_fini
 +
 +80482d0 !  push__libc_csu_init
 +
 +80482d5 !  pushecx
 +
 +80482d6 !  pushesi
 +
 +80482d7 !  pushmain
 +
 +80482dc !  callwrapper_8049630_80482a0
 +
 +80482e1 !  hlt
 +
 +80482e2 !  nop
 +
 +80482e3 !  nop
 +
 +Листинг 4 точка входа и ее окрестности
 +
 +Почему бы нам не заменить pop esi/​xor ebp,​ebp на jmp на наш код, откуда мы сможем сделать все, что задумано,​ выполнить эти команды и вернуться обратно?​ Но для начала необходимо подготовь код, который мы будем внедрять. Для простоты выведем короткое приветствие на экран. На языке ассемблера это звучит приблизительно так:
 +
 +moveax, 4; системный вызов write
 +
 +movebx,1; идентификатор стандартного вывода
 +
 +movecx, offsetbegin_msg;​ указатель на первый символ выводимого сообщения
 +
 +movedx, offsetend_msg;​ указатель на последний символ выводимого сообщения
 +
 +int 80h; вывод на экран
 +
 +popesi; сохраненные команды
 +
 +xor ebx,ebp;
 +
 +jmp 80482C3h; возврат в программу
 +
 +Листинг 5 исходная программа,​ выводящая приветствие на экран
 +
 +Это не самый оптимальный вариант и его можно здорово оптимизировать,​ если переписать так: ​
 +
 +xor eax,eax
 +
 +add al, 4
 +
 +xor ebx,ebx
 +
 +inc ebx
 +
 +mov ecx, offset begin_msg
 +
 +mov edx,ecx
 +
 +add edx, sizeof(msg)
 +
 +int 80h
 +
 +pop esi
 +
 +xor ebp, ebp
 +
 +jmp 80482C3h
 +
 +Листинг 6 оптимизированный вариант
 +
 +Теперь прокручивая файл в hex-редакторе,​ найдем и выпишем стартовые адреса всех цепочек NOP'​ов,​ пригодных для внедрения. А какие цепочки пригодны для внедрения?​ Если две соседние цепочки расположены в пределах досягаемости короткого перехода (грубо — в пределах сотни байт), 3х NOP'​ов будет вполне достаточно (2 байта на команду перехода,​ один — на любую однобайтовую команду полезного кода, например,​ incebx или popesi). В противном случае нам необходимо иметь цепочку по крайней мере из 6ти NOP'​ов — пять на команду близкого перехода и один на полезную команду.
 +
 +В нашем случае получается:​
 +
 +8048306h 10 байт
 +
 +80483a214 байт
 +
 +804846412 байт
 +
 +Листинг 7 перечень стартовых адресов цепочек NOP'​ов пригодных для внедрения и их длина
 +
 +Итого — 36 байт. Вполне достойное место для демонстрационной программы! Начинаем заполнять цепочки NOP'​ов полезным кодом. С первой попытки у нас получается:​
 +
 +804830631 c0xoreax, eax
 +
 +804830804 04addal, 4
 +
 +804830ae9 93 00 00 00jmp80483a2h
 +
 +804830f90nop
 +
 +Листинг 8 заполняем первую цепочку NOP'​ов (предварительный вариант)
 +
 +При этом один последний NOP остается потерян,​ но по-другому не получается. Команда XOR EBX,​EBX занимает два байта и сюда не лезет. А что, если переставить команды местами?​ Перенести add al,4 в следующую цепочку NOP, а вместо нее вставить XOR EBX,​EBX/​INC EBX
 +
 +804830631c0xoreax,​ eax
 +
 +804830831dbxorebx,​ ebx
 +
 +804830a43incebx
 +
 +804830be9 92 00 00 00jmp80483a2h
 +
 +Листинг 9 заполняем первую цепочку NOP'​ов (окончательный вариант)
 +
 +Тогда следующая цепочка будет заполнена так:
 +
 +80483a20404addal,​ 4
 +
 +80483a4b9 ?? ?? ?? ??movecx, offset begin_msg
 +
 +80483a989camovedx,​ ecx
 +
 +80483abe9 b4 00 00 00jmp8048464h
 +
 +Листинг 10 заполняет вторую цепочку NOP'​ов (предварительный вариант)
 +
 +В третью,​ последнюю,​ цепочку NOP'​ов остаток кода уже не вмещается,​ не хватает одного единственного байта! Что ж, попытаемся еще немного ужать наш код. Например,​ пары инструкций mov edx,​ecx/​add edx,​sizeof(msg),​ которые занимают 5 байт,​ можно использовать lea edx,​[ecx+sizeof(msg)]. Тогда все влезает! Ну а само сообщение можно разместить в сегменте данных… Поскольку,​ свободного места там не очень много, ограничимся строкой "​hello"​. Завершающий нуль в конце ставить необязательно,​ поскольку системный вызов write выводит ровно столько символов,​ сколько ему приказано вывести и ни на какие знаки "​останова"​ не реагирует.
 +
 +Если все было сделано правильно (что маловероятно,​ в первый раз ошибки делают все), наш файл победоносно выведет строку "​hello",​ а следом за ней, ту строку,​ которая выводит сама подопытная программа и экран будет выглядеть так:
 +
 +{{elf-trj_Image_8.png?​552}}
 +
 +Рисунок 9 результат работы программы после внедрения (строка hello, которую выводит имплантированный код, для наглядности выделена красным цветом и обведена овалом)
 +
 +===== заключение =====
 +
 +Мы рассмотрели простейший случай,​ а пропыхтели над ним два часа. А сколько займет троянизация полноценной программы?​ Всю оставшуюся жизнь?​! Конечно же, нет! Долго это только с непривычки,​ потом вырабатывается навык и все идет на автопилоте! Главное — не бояться трудностей и постоянно тренироваться,​ оттачивая свое мастерство!
 +
 +{{elf-trj_Image_9.jpg?​201}}
 +
 +