Различия

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

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

articles:dllmain [2017/09/05 02:55] (текущий)
Строка 1: Строка 1:
 +====== dllmain ======
 +<​sub>​{{dllmain.odt|Original file}}</​sub>​
 +
 +====== троянизация приложений — технологии XXI века ======
 +
 +крис касперски ака мыщъх, a.k.a nezumi, no-email
 +
 +**в хакерской практике достаточно часто возникает потребность внедрить в готовый ****exe-****файл свой код (не обязательно вредоносный ;-) так, чтобы он получал управление первым,​ не вызывал ругательств со стороны антивирусов и вообще по возможности вел себя максимально скрытно,​ не бросаясь в глаза. как это сделать?​ мыщъх знает как. и сейчас обстоятельно расскажет. но сначала слегка покурит. итак…**
 +
 +===== введение =====
 +
 +Существует целое семейство утилит,​ предназначенных для "​склейки"​ нескольких программ в один файл, с общим названием "​**джойеры**"​ (от англ. joiner — соединитель):​ Joiner by Blade, SuperGlue, MicroJoiner,​ Juntador и множество других,​ подробный обзор которых можно отрыть в статье "​Клейкий софт"​ из #65 Хакера (http://​www.xakep.ru/​magazine/​xa/​065/​044/​1.asp),​ однако,​ качество склейки оставляет желать лучшего:​ большинство джойнеров просто помещают внедряемый файл в оверлей основного exe файла и при запуске копируют его на диск во временную папку или (еще чаще — в системный каталог Windows, прав записи в который у простого пользователя,​ не сидящего под администратором,​ разумеется,​ нет, и операция накрывается медным Тазом),​ но в любом случае,​ копирование занимает какое-то время, замедляя загрузку программ,​ что рождает в голове пользователе смутные подозрения.
 +
 +Более совершенные джойнеры работают по принципу упаковщиков исполняемых файлов и проецируют внедренный exe непосредственно в оперативную память,​ что не только ускоряет загрузку,​ но и с точки зрения PE-формата выглядит намного более полит. корректно ("​честный"​ PE-файл с оверлеем — это вообще-то редкость).
 +
 +Однако,​ все джойнеры без исключения рано или поздно попадают в антивирусные базы, поскольку представляют собой _готовые_ утилиты,​ в которых легко выделить постоянную сигнатуру (даже если они выполнены на полиморфной основе).
 +
 +Правильные хакеры ведут себя не так и склеивают программы самостоятельно. Это совсем нетрудно сделать! Понадобиться лишь верный друг hiew и минимальные навыки программирования на Си. Ну и, конечно,​ трава. Без травы сознание коллапсирует в черную дыру, хвост висит и мозг ни хрена не работает. Впрочем,​ мы увлеклись. Вернемся к технической стороне проблемы,​ но прежде сделаем одно важное техническое замечание.
 +
 +//​**Внедряемый код необязательно должен быть вирусом,​ червем,​ **////​**rootkit'​**////​**ом или любой другой _вредоносной_ заразой (тем более, что для одного вред, для другого благо, взять хотя бы свинью и хохла),​ поэтому,​ во избежание наездов на мыщъх**////​**'​**////​**а и прочих недоразумений,​ условимся называть внедряемый код **////​**X-**////​**кодом.**//​
 +
 +Примерыисполняемых файлов,​ прилагаемые к настоящей статье,​ совершенно безвредные и могут запускаться без опаски,​ как с правами администратора,​ так и без таковых. Хорошая идея — прогнать их через всех имеющихся у читателя антивирусов и пронаблюдать за их реакцией.
 +
 +===== как мы будем действовать =====
 +
 +Всякий exe-файл,​ импортирует одну или несколько динамических библиотек (Dynamic Link Library или, сокращенно,​ DLL), прописанных в таблице импорта. При запуске exe-файла,​ системный загрузчик анализирует таблицу импорта,​ загружает все перечисленные в ней динамические библиотеки,​ вызывая функцию DllMain для инициализации каждой DLL, и только после этого передает управление на оригинальную точку входа (Original Entry Point или, сокращенно OEP) запускаемого exe-файла.
 +
 +Таким образом,​ если мы добавим в таблицу импорта подопытного exe-файла "​свою"​ DLL, проблема внедрения X-кода будет успешно решена. Самое замечательное,​ что DLL может быть написана на любом языке (хоть на ассемблере,​ хоть на DELPHI) и совершенно неподвластна антивирусам,​ поскольку они органически неспособны распознавать неизвестную заразу. Ах, да… Эвристические анализаторы. Но эти штуки очень легко обойти (чему посвящено множество статей,​ в том числе и моих, которые можно найти на nezumi.org.ru).
 +
 +===== подготовка к экспериментам =====
 +
 +Прежде чем вторгаться в базовые структуры PE-файла,​ неплохо бы получить хотя бы общее представление о его устройстве. В MSDN входит спецификация на PE-формат ("​Microsoft Portable Executable and Common Object File Format Specification"​),​ написанная на враждебном для нас английском языке и ориентированная главным образом на честных программистов. Мыщъх исправил этот недостаток,​ переписав спецификацию на русский язык и переориентировав ее на хакеров. Электронная копия доступа для бесплатной скачки по адресу http://​nezumi.org.ru/​souriz/​PE-desc-n-inject.zip.
 +
 +ОК, теперь… нет, курить мы пока не будем, а подготовим "​дрозофилу",​ предназначенную для внедрения X-кода, пример которой,​ написанный на языке Си, приведен ниже:
 +
 +#include <​stdio.h>​
 +
 +main()
 +
 +{
 +
 +printf("​I'​m nezumi\n"​);//​ выводим что-нибудь на экран
 +
 +}
 +
 +Листинг 1 файл-дрозофила "​inject.c",​ предназначенный для внедрения X-кода
 +
 +Компилируем ее любым подходящим компилятором (например,​ в случае Microsoft Visual C++ командная строка будет выглядеть так: "​$cl.exe inject.c"​),​ после чего на диске образуется файл inject.exe, выдающий при запуске следующее приветствие на экран (мыщъх обращает внимание,​ что это _консольная_ программа,​ которая должна запускаться из-под FAR'а или штатного командного интерпретатора cmd.exe, при запуске из "​Проводника"​ окно консоли автоматически закроется сразу же после завершения программы и мы ни хвоста не увидим):​
 +
 +$inject.exe
 +
 +I'm nezumi
 +
 +Листинг 2 результат работы программы-дрозофилы до внедрения X-кода
 +
 +Разобравшись с дрозофилой,​ займемся подготовкой динамической библиотеки,​ то есть, того самого X-кода, который мы будем внедрять внутрь inject.exe. В простейшем случае,​ исходный код будет выглядеть так:
 +
 +#include <​stdio.h>​
 +
 +#include <​windows.h>​
 +
 +// создаем фиктивную экспортируемую функцию,​
 +
 +// которую потом будет импортировать дрозофила
 +
 +__declspec(dllexport) int dummy(){ return 0;}
 +
 +/* точка входа в dll, получающая управление при различных обстоятельствах */
 +
 +BOOL WINAPI DllMain(HINSTANCE hinstDLL,​DWORD fdwReason,​LPVOID lpvReserved)
 +
 +{
 +
 +// приветствие,​ выводимое до запуска дрозофилы
 +
 +if (fdwReason==DLL_PROCESS_ATTACH) printf("​hello,​world!\n"​);​
 +
 +
 +
 +// приветствие,​ выводимое перед завершением работы дрозофилы
 +
 +if (fdwReason==DLL_PROCESS_DETACH) printf("​good-bye,​world!\n"​);​
 +
 +}
 +
 +Листинг 3 исходный код динамической библиотеки injected_dll.c,​ подготовленной к внедрению
 +
 +Обратите внимание,​ что DllMain в отличии от EntryPoint вызывается _многократно_ а) при подключении к процессу;​ б) при завершении процесса или выгрузки динамической библиотеки API-функцией FreeLibrary;​ в) при создании процессом нового потока;​ г) при завершении одного из существующих потоков процесса. Другими словами,​ DllMain позволяет отслеживать определенные системные события и адекватным способном реагировать на них. В данном случае,​ мы выводим "​hello,​world!"​ перед запуском дрозофилы и "​good-bye,​world!"​ перед завершением ее работы.
 +
 +Компиляция динамической библиотеки осуществляется следующим образом:​ "​$cl.exe injected_dll.c /​LD",​ где ключ "/​LD"​ сообщает компилятору,​ что необходимо сгенерировать именно DLL, а не EXE (как он делает это по умолчанию).
 +
 +===== внедрение X-DLL в таблицу импорта дрозофилы =====
 +
 +Берем, значит,​ hiew в свои загребущие лапы, открываем файл "​inject.exe",​ переходим в hex-режим по <​ENTER>,​ давим <F8> для отображения PE-заголовка,​ нажимаем <F10> (Dir) и среди прочих элементов IMAGE_DATA_DIRECTORYвыбираем секцию импорта (Import), расположенную в данном случае по RVA-адресу равному 5484h и раскинувшуюся в ширину на целых 28h байт (см. рис. 1). Клавиша <​ENTER>​ переносит нас к структуре Import Directory Table,​ о которой мы поговорим чуть попозже,​ а пока обсудим,​ как найти указатель на Import Directory Table в отсутствии hiew'​а.
 +
 +Двойное слово, лежащее по смещению 80h от начала PE-заголовка (легко опознаваемого визуально по сигнатуре "​PE"​),​и будет RVA-адресом,​ указывающим на Import Directory Table,​ а следующее двойное слово хранит ее размер. Так что для поиска таблицы импорта,​ hiew совсем необязателен.
 +
 +{{dllmain_Image_0.png?​553}}
 +
 +Рисунок 1 поиск указателя на таблицу импорта в hiew'​е и в PE-файле
 +
 +Таблица импорта представляет собой достаточно сложное сооружение иерархического типа. Вершину иерархии занимает структура **Import Directory Table**,​ фактически являющаяся массивом подчиненных структур типа IMAGE_IMPORT_DESCRIPTOR,​ каждая из которых содержит RVA-указатель на имя загружаемой динамической библиотеки,​ ссылки на OriginalFirstThunk и FirstThunk с именами/​ординалами импортируемых функций (причем,​ поле OriginalFirstThunk не является обязательным и может быть равно нулю). Два других поля — TimeDateStamp (временная отметка) и ForwarderChain (форвардинг) так же необязательны и потому для подключения своей собственной DLL нам необходимо заполнить всего лишь два поля структуры _IMAGE_IMPORT_DESCRIPTOR:​ Name и FirstThunk, а так же создать таблицу Import Address Table (сокращено,​ IAT), импортирующую по меньшей мере одно имя (в данном случае "​dummy"​).
 +
 +typedef struct _IMAGE_IMPORT_DESCRIPTOR {
 +
 +union {
 +
 +DWORDCharacteristics;//​ 0 for terminating null import descriptor
 +
 +DWORDOriginalFirstThunk;//​ RVA to original unbound IAT
 +
 +};
 +
 +DWORDTimeDateStamp;//​ TimeDateStamp
 +
 +DWORDForwarderChain;//​ -1 if no forwarders
 +
 +DWORDName;//​ name of the dll
 +
 +DWORDFirstThunk;//​ RVA to IAT
 +
 +} IMAGE_IMPORT_DESCRIPTOR;​
 +
 +Листинг 4 прототипструктуры IMAGE_IMPORT_DESCRIPTOR
 +
 +Если вместе стройной иерархии структур в нашей голове возникла каша, то не стоит волноваться — это нормально! По ходу дела она "​утрясется"​ и все структуры усядутся по своим местам,​ так что оставим их "​дозревать"​ и сосредоточимся на текущих проблемах. Чтобы внедрить X-DLL, в Import Directory Table необходимо добавить еще один экземпляр структуры IMAGE_IMPORT_DESCRIPTOR,​ но просто так сделать это не получится,​ поскольку сразу же за концом Import Directory Table начинается IAT первой динамической библиотеки и нам просто _некуда_ втиснуться,​ если, конечно… не перенести Import Directory Table в какое-нибудь другое место! А что?! И перенесем! Вот только покурим сначала или заточим хороший отбивной.
 +
 +Повторяем описанную последовательность действий с hiew'​ом еще раз, перемещаясь в начало таблицы импорта (а точнее,​ как мы уже знаем, на первый элемент Import Directory Table),​ давим <​Gray-*>​ ("​звездочку"​ на цифровой клавиатуре) и перемещаясь курсорными клавишами,​ выделяем бардовым цветом 28h байт (размер Import Directory Table),​ после чего давим <​Gray-*>​ еще раз и нажав <F2> сохраняем блок в файл, для определенности назвав его idt-org (см. рис. 2).
 +
 +{{dllmain_Image_1.png?​553}}
 +
 +Рисунок 2 сохранение оригинальной Import Directory Table в файл idt-org
 +
 +Теперь прокручиваем файл, клацая <​PageDown>​ до тех пор, пока не выйдем на оперативный простор свободного места, оккупированного длинной вереницей нулей. В нашем случае это место располагается по адресу 405810h, непосредственно за концом таблицы импорта (см. рис. 3).
 +
 +{{dllmain_Image_2.png?​553}}
 +
 +Рисунок 3 свободное место за концом таблицы импорта,​ пригодное для перемещения Import Directory Table
 +
 +Теперь нам необходимо скопировать оригинальную Import Directory Table на новое место, не забыв при этом зарезервировать место для одного элемента структуры типа IMAGE_IMPORT_DESCRIPTOR,​ в который мы чуть позже поместим "​нашу"​ динамическую библиотеку,​ которая будет проинициализирована самой первой (что очень полезно для борьбы с антивирусами,​ "​иммунизирующими"​ exe-файлы путем "​прививки"​ им специальной dll-вакцины,​ выполняющий проверку целостности содержимого образа исполняемого файла).
 +
 +Поскольку,​ как нетрудно подсчитать (см. листинг 4),​ размер структуры IMAGE_IMPORT_DESCRIPTOR составляет 14h байт, а незанятая область начинается с адреса 405810h мы должны передвинуть курсор по адресу 405824h, задавить <​Gray-*>,​ выделить 28h байт (размер оригинальной Import Directory Table), задавать <​Gray-*>​ еще раз, _обязательно_ переместить курсор в начало выделенного блока, нажать <​Ctrl-F2>​ (Get Block), ввести имя файла, в который мы только что сохранили блок — "​idt-org"​и считать его с диска (см. рис. 4).
 +
 +{{dllmain_Image_3.png?​552}}
 +
 +Рисунок 4 оригинальная Import Directory Table,​ перемещенная на новое место
 +
 +Теперь возвращаемся в начало файла и корректируем RVA-адрес таблицы импорта,​ который в данном случае составит 5824h. Почему 5824h, а не 405824h?! Да потому,​ что RVA адреса получаются путем вычитания базового адреса (прописанного в заголовке PE-файла и равного в данном случае 400000h) от виртуального адреса (равного в данном случае 405824h). Причем,​ с учетом порядка байт старшинства байт, принятого на процессорах x86 (младшие биты располагаются по меньшим адресам),​ мы должны записать 24 58 (см. рис. 5),​ а не 58 24 как поступают многие начинающие хакеры,​ а потом удивляются почему оно не работает.
 +
 +Значит,​ открываем файл "​inject.exe"​в hiew'​e,​ находим "​PE"​ сигнатуру,​ опускаем курсор вниз на 80h байт, видим там 84 54 (см. рис. 1),​ нажимаем <F3> для разрешения редактирования,​ меняем адрес на 24 58, сохраняем изменения по <F9> и выходим… за пивом. Пиво для хакеров — это святое!
 +
 +{{dllmain_Image_4.png?​553}}
 +
 +Рисунок 5 корректируем указатель на новую таблицу импорта
 +
 +Проверяем работоспособность файла — вдруг она пострадала?​! Запускаем "​inject.exe"​и (если все операции были проделаны правильно) на экране появится триумфальное приветствие. В противном же случае — система откажется загружать файл или выбросит исключение.
 +
 +$inject.exe
 +
 +I'm nezumi
 +
 +Листинг 5 результат работы программы-дрозофилы после переноса таблицы импорта,​ как видно, ни одного тушканчика в результате экспериментов не пострадало
 +
 +Смочив пересохшее горло пивом, приступаем к самой сложной и самой ответственной части — заполнению структуры IMAGE_IMPORT_DESCRIPTOR. Начнем с того, что переместим курсор в конец Import Directory Table, подогнав его к адресу 405850h и запишем имя функции-пустышки ("​dummy"​),​ оканчивающееся нулем и предваренной двумя нулями,​ а следом за ним – имя внедряемой динамической библиотеки "​injected_dll.dll"​ (впрочем,​ порядок их расположения может быть и другим,​ системному загрузчику на такие мелочи уже давно положил).
 +
 +Сделав это, перемещаемся на первый байт, ранее зарезервированный нами для структуры IMAGE_IMPORT_DESCRIPTOR,​ и начинаем колдовать. Первое двойное слово оставляем равным нулю. За ним идут 4 байта,​ отведенные для TimeDataStamp и мы, желая слегка извратиться,​ занесем сюда IAT, т. е. двойное слово, содержащее RVA-адрес импортируемой функции. В нашем случае эта функция зовется "​dummy",​ а ее имя (предваренное двумя нулями!) начинается с RVA-адреса 5850h. Учитывая обратный порядок байт x86,​пишем:​ 50 58. Пропустив следующее двойное слово (Forwarder Chain), в поле "​Name"​ записываем RVA-адрес имени внедряемой динамической библиотеки "​injected_dll.dll",​ равный в нашем случае 5858h. Остается заполнить последнее поле — Import Address Table, содержащее RVA-адрес таблицы IAT, размещенной нами поверх поля TimeDateStamp с RVA адресом равным 5814h.
 +
 +Вот, собственно говоря,​ и все… После добавления новой структуры IMAGE_IMPORT_DESCRIPTOR в массив Import Directory Table последний будет выглядеть так (см. листинг 6).
 +
 +.00405810:​00 00 00 00-50 58 00 00-00 00 00 00-58 58 00 00  PX  XX
 +
 +.00405820:​14 58 00 00-AC 54 00 00-00 00 00 00-00 00 00 00  ¶X  мT
 +
 +.00405830:​00 58 00 00-00 50 00 00-00 00 00 00-00 00 00 00  X  P
 +
 +.00405840:​00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
 +
 +.00405850:​00 00 64 75-6D 6D 79 00-69 6E 6A 65-63 74 65 64  dummy injected
 +
 +.00405860:​5F 64 6C 6C-2E 64 6C 6C-00 00 00 00-00 00 00 00  _dll.dll
 +
 +Листинг 6 Import Directory Table с внедренной X-DLL
 +
 +Остается сущая мелочь. Вернуться в начало файла, отсчитать от "​PE"​ заголовка 80h байт, исправив указатель на таблицу импорта с 5824h на 5810h и увеличив ее размер до 3Сh. Сохраняем проделанные изменения и, набрав побольше воздуха в грудь, залпом выпиваем оставшееся пиво и запускаем файл "​inject.exe":​
 +
 +$inject.exe
 +
 +**hello,​world!**
 +
 +I'm nezumi
 +
 +**good-bye,​world!**
 +
 +Листинг 7 демонстрация работы внедренной динамической библиотеки
 +
 +Оторвать мыщъху хвост!!! Это работает!!! Причем,​ не просто работает,​ а очень даже хорошо работает и внедренная динамическая библиотека послушно выводит "​hello,​world!"​ еще до запуска файла-дрозофилы и "​good-bye,​world!"​ непосредственно перед ее завершением! Красота!!! Вот только… посторонняя dll нам очень сильно мешает,​ вызывая естественное желание задвинуть ее куда-нибудь подальше. И тут мы плавно переходим ко второй части нашего рассказа.
 +
 +===== копирование X-DLL в NTFS-stream =====
 +
 +Файловая система NTFS выгодно отличается от FAT'а тем, что поддерживает //​**потоки**//​ (//​**streams**//​),​ так же называемые атрибутами (attributes),​ но чтобы не путать их с атрибутами типа "​только на чтение",​ мы будем придерживаться первого термина.
 +
 +Каждый файл имеет по меньшей мере один безымянный поток, хранящий актуальные данные файла. Именно его размер высвечивает "​Проводник"​ Windows и продвинутые файловые менеджеры типа FAR'​а. Однако,​ мы можем создавать и дополнительные потоки,​ отделяя их имя от имени файла знаком двоеточия (":"​),​ например,​ my_file:​my_stream1,​ my_file:​my_stream2. Штатные средства Windows не поддерживают работы с именованными потоками и потому добраться до их содержимого не так-то просто. Не существует никакой (стандартной) возможности определить — имеет ли данный файл именованные потоки или нет.
 +
 +Чувствуете,​ куда я клоню? Давайте спрячем X-DLL внутрь "​inject.exe",​ поместив ее в именованный поток. Внимание! Это совсем не тоже самое, что в "​тупую"​ склеить два файла как и поступает большинство джойнеров. При копировании X-DLL в NTFS-поток видимый размер "​дрозофилы"​ _не_ увеличивается и при открытии файла "​inject.exe"​функцией fopen("​inject.exe",​ "​rb"​) _никаких_ следов присутствия X-DLL в ней _не_ окажется!!! Более того, при передачи файла через http для проверки антивирусной службой в on-line, передается только безымянный поток (содержащий полезную программу,​ _без_ X-DLL) и, естественно,​ антивирусы в ней ничего не обнаружат. Кстати говоря,​ большинство антивирусов сканируют только безымянный поток! Так что X-DLL может чувствовать себя в относительной безопасности,​ тепле, сухости и комфорте.
 +
 +Открываем "​inject.exe"​в hiew'​е,​ привычным движением переходим к таблице импорта:​ <​ENTER>,​ <F8>, <​F10>,​ <​стрелка вниз>,​ <​ENTER>​ и заменяем имя динамической библиотеки "​injected_dll.dll"​ на "​**inject.exe:​x.dll****"​**,​ где "​inject.exe"​ —имя подопытного файла, в который мы собираемся внедрить X-DLL, а "​x.dll"​ – имя самой внедряемой динамической библиотеки
 +
 +{{dllmain_Image_5.png?​552}}
 +
 +Рисунок 6 переименование внедренной динамической библиотеки для вызова ее из именованного NTFS-потока
 +
 +Теперь необходимо _скопировать_ "​injected_dll.dll"​ в "​inject.exe:​x.dll",​ что легко осуществить с помощью FAR'​а. Подогнав курсор к динамической библиотеке "​injected_dll.dll",​ нажимаем <​Shift-F5>​ и пишем "​inject.exe:​x.dll"​. Все! По завершению копирования исходную динамическую библиотеку "​injected_dll.dll"​ можно удалить. Больше она нам не понадобиться! Кстати говоря,​ размер файла "​inject.exe"​ после создания нового именованного потока не увеличился ни на байт!!! Дисковые ревизоры (вместе с прочими системами контроля) — отдыхают!!!
 +
 +{{dllmain_Image_6.png?​552}}
 +
 +Рисунок 7 копирование внедряемой библиотеки в именованный NTFS-поток
 +
 +Запускаем файл "​inject.exe"​ и убеждаемся,​ что его работоспособность в результате последних махинаций ничуть не пострадала:​
 +
 +$inject.exe
 +
 +hello,​world!
 +
 +I'm nezumi
 +
 +good-bye,​world!
 +
 +Листинг 8 результат работы дрозофилы,​ несущий внутри себя именованный поток с внедренной динамической библиотекой
 +
 +===== переход от теории к практики =====
 +
 +Внедрение своей собственной динамической библиотеки это, конечно,​ очень хорошо,​ однако,​ на практике гораздо чаще приходится сталкиваться с тем, что требуется внедрить _чужой_ исполняемый файл. Что делать?​! Преобразовывать его в DLL?! Конечно же нет! Достаточно просто слегка доработать нашу X-DLL, научив ее запускать exe-файлы посредством API-функции CreateFile, при этом сами исполняемые файлы можно (и нужно) поместить в именованные NTFS-потоки,​ число которых фактически неограниченно. Причем,​ если внедряемый exe тащит за собой динамические библиотеки или другие компоненты,​ они так же могут быть внедрены в NTFS-потоки (естественно,​ в текущем каталоге их уже не окажется и потому исполняемый файл придется подвергнуть правке на предмет изменения всех путей). Если же этот файл упакован (а большинство "​боевых"​ утилит типа систем удаленного администрирования редко поставляются в открытом виде), наша X-DLL может перехватить API-функции CreateFile/​LoadLibrary,​ автоматически отслеживания обращения к отсутствующим файлам и подсовывая вместо них соответствующие им именованные NTFS-потоки.
 +
 +Другой немаловажный момент. Отправляя exe-файл с внедренной в него X-DLL по почте, записывая его на лазерный диск или любой другой не-NTFS носитель,​ мы теряем _все_ именованные потоки и программа тут же отказывает в работе,​ ругаясь,​ что не может найти dll (см. рис. 8):​
 +
 +{{dllmain_Image_7.png?​553}}
 +
 +Рисунок 8 результат работы inject.exe, после пересылки по электронной почте
 +
 +Ситуация кажется критической,​ можно даже сказать драматической,​ но на помощь приходит благородный архиватор RAR, обладающий уникальной способностью сохранять все NTFS-потоки,​ какие только есть в файле.
 +
 +Запускаем RAR (см. рис. 9),​ выбираем "​inject.exe",​ нажимаем кнопку "​добавить"​ (или давим <​CTRL-A>​),​ после чего в свойствах архива взводим галочку "​сохранять файловые потоки"​ (вкладка "​дополнительно"​). Так же, при желании можно создать SFX-архив на тот случай,​ если у получателя не окажется RAR'​а,​ но это уже технические детали.
 +
 +{{dllmain_Image_8.png?​553}}
 +
 +Рисунок 9 использование RAR'а для сохранения именованных потоков
 +
 +Повторяем процедуру пересылки файла по электронной почте еще раз, распаковываем полученный архив, запускаем "​inject.exe"​ и… о чудо! Он работает (см. листинг 9),​ как самый настоящий хомяк!
 +
 +$inject.exe
 +
 +hello,​world!
 +
 +I'm nezumi
 +
 +good-bye,​world!
 +
 +Листинг 9 результат работы "​inject.exe"​ после пересылки по электронной почте в упакованном rar-архиве
 +
 +===== заключение =====
 +
 +Технологии внедрения в исполняемые файлы не стоят на месте и развиваются вместе с защитными механизмами и операционными системами. Извечная проблема меча и щита — кто усовершенствуется первым.
 +
 +Использование готовых утилит,​ работающих в полностью автоматическом режиме,​ это во-первых,​ не престижно,​ а, во-вторых,​ слишком ненадежно. Разработчики антивирусов даром свой хлеб не едят! Чтобы не погореть на мелочах,​ весь X-код следует писать самостоятельно — своими лапами и хвостом. До тех пор, пока он существует в единственном экземпляре,​ у защитных систем не остается никакого шанса предотвратить атаку!
 +
 +===== >>>​ врезка всегда заметай за собой следы =====
 +
 +Некоторые файлы (особенно упакованные протекторами) скрупулезно следят за своей целостностью и на попытку внедрения реагируют прямо так скажем не совсем адекватно,​ однако… поскольку X-DLL получает управление поперед остальных,​ она может восстановить таблицу импорта в памяти,​ как будто все так и было. Словно,​ к ней никто и не прикасался. Для этого достаточно вызывать API-функцию VirtualProtect,​ присвоив соответствующим регионами памяти атрибут PAGE_READWRITE,​ восстановить таблицу импорта (оригинал которой легко сохранить в X-DLL), а затем восстановить атрибут PAGE_READONLY с помощью все той же VirtualProtect. Более того, X-DLL может выделить блок памяти из кучи с помощью API-функции VirtualAlloc,​ скопировать туда свое тело (которое,​ естественно должно быть полностью перемещаемо,​ то есть сохранять работоспособность независимо от базового адреса загрузки) и… выгрузить ставшей ненужную X-DLL вызовом FreeLibrary (на тот случай,​ если какой-то хитрый механизм проверки целостности решит перечислить список загруженных модулей).
 +
 +Маленький технический нюанс: на процессорах с поддержкой битов NX/XD (запрещающий исполнение кода в страницах памяти,​ не имеющих соответствующих атрибутов),​ выделяемому блоку памяти следует присвоить атрибут PAGE_EXECUTE_READWRITE,​ в противном случае,​ если у пользователя задействован аппаратный DEP для всех приложений (а не только системных компонентов,​ как это происходит по умолчанию),​ вместо выполнения машинного кода система выбросит исключение и… выполнение троянизированной программы завершится в аварийном режиме.
 +
 +===== >>>​ врезка пересчет CRC =====
 +
 +В заголовке PE-файла имеется специальное поле, содержащее контрольную сумму. В подавляющем большинстве случаев оно равно нулю, но если это не так, то после вмешательства в таблицу импорта,​ контрольную сумму необходимо пересчитать. Этим занимается утилита EDITBIT.EXE,​ запущенная с ключом /RELEASE. Она входит как в штатную поставку компилятора Microsoft Visual Studio, так и в Platform SDK,​ так что проблем с ее поиском возникнуть не должно. Но если же они все-таки возникнут,​ можно попробовать просто обнулить контрольную сумму. В некоторых случаях это прокатывает,​ но… не всегда.
 +
 +