Взаимодействие между процессами (IPC) - это путь, с помощью которого
процессы могут взаимодействовать между собой. У каждого процесса есть свое
собственное отдельное адресное пространство, поэтому процессы не могут
напрямую видеть память других процессов. Win32 API предоставляет несколько
разновидностей IPC. IPC может быть очень полезно для вирусов, поэтому я
объясню несколько путей, хотя я не уверен, что в вирусах применимы все из
этих путей.
В этой статье объясняются следующие виды IPC:
- атомы
- мэппинг файлов
- анонимные пайпы
- мейлслоты
Атомы
Атомы - это очень простой и доступный путь IPC. Идея состоит в том, что
процесс может поместить строку в таблицу атомов и эта строка будет видна
другим процессам. Когда процесс помещает строку в таблицу атомов, он
получает 32-х битное значение (атом), и это значение используется для
доступа к строке. Система не различает регистр строки.
Есть два типа таблиц атомов - глобальная (GAT) и локальная (LAT).
Добавление строки в GAT
push _address_of_string - строка, заканчивающаяся NULL, максимальный
размер равен 255 байтам
call GlobalAddAtom(A/W)
Возвращает в eax атом
Удаление атома из GAT
push _atom
call GlobalDeleteAtom(A/W)
Возвращает 0 в eax, если вызов функции прошел успешно
Поиск атома
push _адрес_строки - заканчивающаяся NULL'ом строка, максимальный размер
которой равен 256 байтам
call GlobalFindAtom(A/W)
Возвращает 0 в eax, если вызов функции неудался, либо атом, если вызов
прошел успешно
Получить строку атома, имея атом
push _size_of_buffer - длина буфера
push _address_of_buffer - буфер
push _atom - атом :)
call GlobalGetAtomName(A/W)
|
Если вызов прошел успешно, буфер будет заполнен строкой атома
|
LAT использует те же функции для работы с атомами, только без приставки
'Global'. Как бы то ни было, LAT виден тольк для текущего процесса, но не для
других. Количество элементов по умолчанию равно 37, но его можно изменить с
помощью функции InitAtomTable. Максимальное количество, которое можно
установить, равно 3FFF. Хм, это число указано в MSDN, но я пытался получить
сообщение об ошибке, вызывая эту функцию с более высоким значением, и у меня
ничего не вышло. Похоже, что функция работает с любым количеством элементов.
Инициализация LAT
push _размер_LAT
call InitAtomTable
Возвращает ...
Вот и все об атомах...
Мэппинг файлов
Я думаю, что мэппинг файлов - это хорошо известный путь для доступа к файлам.
Он объяснен во многих статьях, поэтому я не буду тратить время на
подробности. Идея в том, что мы можем промэппировать файл в память. Тогда с
каждым изменением в памяти, будет меняться и файл. Применительно к IPC мы
можем промэппировать файл в нескольких процессах и взаимодействовать через
него.
функции
push шаблонный хэндл файла ; по умолчанию - 0
push флаги и аттрибуты ; по умолчанию - 0
push флаги открытия и создания
push аттрибуты безопасности ; по умолчанию - 0
push флаги разделяемого доступа ; по умолчанию - 1
push доступ к файлу
push имя файла
call CreateFile(A/W)
Возвращает хэндл файла
флаги создания и открытия
CREATE_NEW equ 1
CREATE_ALWAYS equ 2
OPEN_EXISTING equ 3
OPEN_ALWAYS equ 4
TRUNCATE_EXISTING equ 5
доступ к объекту
GENERIC_READ equ 080000000h
GENERIC_WRITE equ 040000000h
GENERIC_EXECUTE equ 020000000h
GENERIC_ALL equ 010000000h
Правильный мэппинг файла начинается здесь, ...объект, который должен быть
промэппирован, не нужно открывать CreateFile(A/W). Если мы используем
значение (-1) в аргументе хэндла файла, мы промэппируем часть разделяемой
пммяти без связывания с файлом, ...идеально для IPC.
push имя промэппированного объекта ; по умолчанию - 0
push нижние 32 бита размера объекта ; по умолчанию - размер
push верхние 32 32 bits of object size ; по умолчанию - 0
push защита ; по умолчанию - 4
push аттрибуты безопасности ; по умолчанию - 0
push хэндл файла ; по умолчанию - HNDL от CreateFile
call CreateFileMapping(A/W)
Возвращает хэндл объекта
Эта функция создает объект мэппинга и возвращает хэндл его защиты
PAGE_READONLY equ 2
PAGE_READWRITE equ 4
PAGE_WRITECOPY equ 8
Аттрибуты безопасности - указатель может быть установлен в NULL, как бы
то ни было, вот структура:
SECURITY_ATTRIBUTES struc
_saSize dd ? ; размер структуры
_lpSecurityDescriptor dd ? ; указатель на дескриптор
; безопасности
_bInheritHandle dd ? ; булевое значение, истинно для
; наследования
SECURITY_ATTRIBUTES ends
push имя мэппирующегося объекта
push флаг наследования
push реим доступа
call OpenFileMapping(A/W)
Возвращает хэндл объекта
Режим доступа
FILE_MAP_WRITE equ 2 ;R/W access
FILE_MAP_READ equ 4 ;R access
теперь у нас есть map-объект
push сколько байтов мэппировать ; по умолчанию - размер
push нижняя часть смещения ; по умолчанию - 0
push верхняя часть смещения ; по умолчанию - 0
push access mode ;----------------------------------
push object handle ; по умолчанию - HNDL from Create(Open)FileMapping
call MapViewOfFile
Возвращает адрес в памяти
теперь у нас есть промэппированный в память файл
мы можем записать часть промэппированного файла на диск, используя:
push количество байтов, которое нужно записать
push начальный адрес
call FlushViewOfFile
push адрес в памяти
call UnmapViewOfFile ; демэппирует файл
push хэндл объекта ; закрывает объект мэппинга
call CloseHandle
push хэндл файла ; закрывает хэндл файла
call CloseHandle
Вот и все о мэппинге файлов.
Анонимные пайпы
Есть два типа пайпов: анонимные и именованные. Именованные пайпы 'немного'
труднее использовать, чем анонимные, поэтому я расскажу о них в другой
статье.
Ладно, анонимные пайпы однонаправленны и безымянны. Они не могут быть
использованы для удаленного взаимодействия (в отличии от именованных). Идея
состоит в том, что сервер создает пайп и получает хэндлы чтения и записи в
пайп. Тогда он может послать один из этих хэндлов процессу, с которым он
хочет взаимодействовать (мы должны послать его через какой-нибудь другой
метод IPC, я думаю, что лучше всего будет сделать это через атомы, хотя есть
и другие пути...).
создание анонимного (безымянного) пайпа
push размер пайпа
push защита
push указатель на хэндл записи в пайп
push указатель на хэндл чтения из пайпа
call CreatePipe
pipe size - size of pipe buffer
if it's NULL system uses default size
protection - pointer to SECURITY_ATTRIBUTES
if it's NULL pipe cant be inherited
размер пайпа - размер его буфера
защита - указатель на SECURITY_ATTRIBUTES, если NULL - пайп нельзя
унаследовать
Когда пайп создается, мы имеем доступ к обоим концам. После создания
пайпа, сервер должен отослать один хэндл клиентскому процессу.
чтение из пайпа синхронно - это означает, что функция не возвратится, пока
чтение не будет закончено
push указатель на структуру данных
push указатель на количество прочтенных байтов
push количество байтов, которое нужно считать
push указатель на буфер, куда будет производиться чтение
push хэндл чтения из пайпа
call ReadFile
указатель на структуру данных - эта структура используется для
асинхронного ввода и вывода; поэтому
напишем NULL
указатель на количество считанных байтов - указатель на dword, который
будет содержать количество
считанных байтов
запись в пайп синхронна - это означает, что функция не возвратится, пока
запись не будет выполнена
push указатель на структуру данных
push указатель на количество записанных байтов
push количество байтов, которое нужно записать
push указатель на буфер, содержащий записываемые данные
push хэндл записи в пайп
call WriteFile
Анонимные пайпы живут, пока открыты хэндлы чтения и записи. Мы можем
закрыть их с помощью функции CloseHandle.
Вот и все об анонимных пайпах.
Мейлслоты
Мейлслоты - это еще один простой способ взаимодействия между процессами.
Его суть заключается в том, что один процесс (сервер) может коннектиться к
мейлслоту и посылать эти сообщения. Мейлслоты используют датаграмы для
коммуникации, а именованные слоты - нет (для удаленного взаимодействия).
Уффф...
push защита
push время(ms) ожидания
push максимальный размер сообщения
push имя мейлслота
call CreateMailslot(A/W)
Возвращает имя мейлслота
имя мейлслота - имя, которое имеет следующую форму:
db "\\.\mailslot\[path]name", 0
- имя должно быть уникальным, и в нем могут быть
псевдодиректории ([path]), и (как обычно) оно не
чувствительно к регистру
максимальный размер сообщения - мы можем указать максимальную длину
записанного сообщения в мейлслот, как бы то
ни было, если это поле равно NULL, сообщение
может быть любого размера
время ожидания - это время, которое ReadFile будет ждать, если сообщение
еще не находится в мейлслоте. Есть несколько специальных
значений:
0 - если нет сообщения в мейлслоте, функция не будет
работать :)
-1 - функция будет ждать, пока в мейлслоте не появится
сообщение - (MAILSLOT_WAIT_FOREVER)
Ладно, мы создаем мейлслот и у нас есть его хэндл. Мы можем закрыть его с
помощью CloseHandle...
Теперь мейлслот создан и к нему можено получить доступ по имени. Клиенты
могут подсоединяться с помощью функции CreateFile(A/W), используя имя
мейлслота в качестве имени файла. После подсоединения клиент получает хэндл
мейлслота и может записывать сообщения в него. Это делается с помощью функции
WriteFile. Сервер считывает сообщения с помощью ReadFile. Как CreateFile, и
ReadFile уже затрагивались в этом тексте, поэтому я не буду тратить время на
повторное объяснения. как бы то ни было, есть еще несколько функций для
работы с мейлслотами.
функции сервера мейлслота:
push указатель на время ожидания чтения
push указатель на количество соощений
push указатель на размер следующего сообщения
push указатель на максимальный размер сообщения
push хэндл мейлслота ; возвращается функцией CreateMailslot(A/W)
call GetMailslotInfo
Возвращает TRUE(1), если вызов прошел успешно
указатель на размер следующего сообщения - этот параметр может быть равен
NULL, как бы то ни было, мы получим размер следующего сообщения в
мейлслоте. Мы можем получить MAILSLOT_NO_MESSAGE(-1) - это значит,
что в мейлслоте больше нет сообщений.
указатель на количество сообщений - этот параметр может быть равен NULL,
если нет, то мы получим общее количество сообщений, которое нужно
прочитать.
Чтобы установить время ожидания для операции чтения из мейлслота, мы можем
использовать:
push время ожидания для операции чтения
push хэндл мейлслота
call SetMailslotInfo
Когда у нас есть хэндл мейлслота, мы можем использовать стандартные функции,
чтобы получить информацию о нем. Мы можем использовать:
DuplicateHandle
GetFileTime
SetFileTime
GetHandleInformation
SetHandleInformation
Вот и все.