Системные вызовы и библиотечные функции
Бацилла (если это только не вещь в себе по типу океана Соляриса), должна как-то взаимодействовать с внешним миром: открывать файлы, устанавливать сетевые соединения, выводить ругательные сообщения и т.д. Обычно для этого используется прямой вызов API-функций. Но это не лучший вариант. Намного удобнее использовать высокоуровневые библиотечные функции, доставшиеся бацилле в наследство от дрозофилы. Согласитесь, намного удобнее открывать файл с помощью _fopen, чем CreateFile. Одних только аргументов в последнем случае потребуется миллион!
Библиотечные функции легко распознаются при помощи IDA Pro. Вот, например:
Рисунок 5 библиотечные функции, автоматически распознанные IDA Pro
Мы видим, что адрес функции _fopen равен 401988h, значит, ее вызов будет выглядеть приблизительно так:
push aRb ; "rb" указатель на строку с режимом открытия файла
push aName ; указатель на имя файла (аргументы заносятся справа налево)
call 401988h l вызываем функции _fopen
add esp, 8 ; удаляем аргументы из стека
Листинг 1 пример вызова стандартной библиотечной функции языка Си
Однако, использовать библиотечные функции можно только после того, как отработает Start-Up (стартовый код), иначе у нас ничего не получается. В этом нам вновь поможет IDA Pro, распознающая функцию main (WinMain) языков Си/Си++ и процедуру Begin на Паскаль. В notepad'е ее вызов расположен по адресу 1006571h. Здесь находится call 100299Eh, где 100299Eh – адрес WinMain. Для внедрения дрозофилы, достаточно изменить call 100299Eh на свой адрес, в смысле адрес свой бациллы.
Рисунок 6 функция WinMain в стартовом коде
А что делать, если библиотечные функции не распознаны или среди них нет той, что нужна нам? Тогда можно обратиться к импорту дрозофилы и поискать там. В IDA Pro это делается так: View à Open Subview à Imports:
Рисунок 7 функции, импортируемые из USER32.DLL
Вот, например, адрес функции MessageBoxW равен 01001204h. На самом деле, это еще не сам адрес, а только указатель на него, инициализируемый на стадии загрузки, а потому вызов функции будет выглядеть так:
xor eax,eax ; обнуляем eax
push 30h ; uType
push lpCaption ; lpCaption
push lpText ; lpText
push eax ; hWnd
call dword [1001204h] ; вызываем
функции MessageBoxW
Листинг 2 пример вызова API-функции из импорта дрозофилы
Но в некоторых случаях, в импорте искомой функции нет функции. Что тогда? Есть два пути: использовать связку LoadLibrary\GetProcAddress или сканировать импорт вручную. Оба этих способа уже рассматривались в статье "техника написания переносимого shell-кода", опубликованной в таком-то номере журнала, так что не будет здесь повторятся.