Путеводитель по написанию вирусов: 13. Новая школа — Архив WASM.RU

Все статьи

Путеводитель по написанию вирусов: 13. Новая школа — Архив WASM.RU

Здесь я сделаю маленькое введение в новый мир 32-х битного программирования под Windows. Совет: измените образ мышления :).

Если вы добрались до этого места, я исхожу из того, что вы уже достаточно продвинуты в 16-ти битном ассемблере. Поэтому настало время для изучения среды окружения Window. Поскольку эта часть туториала является приложением, она не будет подробной, так что вам надо будет поискать дополнительную информацию в другом месте :).

Краткое описание того, что происходит

Здесь мы должны отбросить практически все наше знание DOS, если хотим добиться успеха, потому что сейчас оно нам не понадобиться. Все, что было объяснено выше больше не имеет никакого смысла в Win32 (конечно, есть несколько исключений)... прерывания, структуры, COM-файлы, методы резидентности и невидимости, методы добавления, антиотладки, антиэвристики и т.д. Единственное, что осталось - это концепция, код же будет совершенно другим.

Некоторые люди ошибочно называют Win95-вирусы Win32-вирусами. Нет. Win32 вирусы означают, что вирус ДОЛЖЕН быть совместим с Win95, WinNT, Win3X+ Win32s и Win98.

Ладно, у нас есть новые регистры, новые сегменты, новые структуры... и множество новых вещей, которые необходимо исследовать. Это не так трудно, как могло бы показаться... Просто подумайте о том, что это то, чего вы ждали: так много неисследованных техник, что вы легко можете стать пионером :).

Забудьте обо все этих 16-ти битных сегментах, 16-ти битных смещениях, 16-ти битных регистрах... Теперь у нас есть все то же самое + много нового в 32-х битной версии.

Различия между 16-ти битным и 32-х битным программированием

Теперь мы будем, в основном, с двойными словами вместо слов, что открывает нам целый мир новых возможностей. У нас есть на два сегмента больше: FS и GS (в дополнение к CS, DS, ES и SS). И у нас есть новые 32-х битные регистры - EAX, EBX, ECX, EDX, ESI, EDI, EBP и ESP. Давайте посмотрим, как играть с регистрами: представьте, что у нас есть доступ к нижнему слову EAX.Что мы можем сделать? До этой части можно добраться с помощью AX, который и является нижним словом EAX. Например, EAX = 00000000, а мы хотим поместить 1234h в его нижнее слово. Мы просто должны сделать "mov ax, 1234h" и все. Но что, если нам нужен доступ к верхнему слову EAX? Для этих целей мы не можем использовать регистр: мы должны прибегнуть к какой-нибудь из инструкций вроде ROL (или SHL, если значение нижнего слова для нас неважно).

Основы программирования в Ring-3 : API

Можно считать, что функции API заменяют в Windows прерывания. При разработке "обычных" приложение мы можем работать с ними без особых проблем. При разработке вирусов все меняется, так как нам нужно искать эти функции. Но это другая история, о которой мы поговорим в другой раз и в другое время. Хорошо, когда мы используем функции API, параметры должны быть в стеке, поэтому мы должны их push'ить. Давайте взглянем на пример:

                push    00000000h
                call    ExitProcess

ExitProcess в Windows - это эквивалент знаменитого INT 20h в DOS. Значение, которое мы должны поместить - это код выхода. Давайте посмотрим:

 VOID ExitProcess(
  UINT uExitCode        // код выхода для всех ветвей
 );

Другим примером API может быть MessageBox(A/W). Да, она показывает этот проклятый msgbox.

 int MessageBox(
  HWND hWnd,            // хэндл окна-владельца
  LPCTSTR lpText,       // адрес текста в messagebox'е
  LPCTSTR lpCaption,    // адрес заголовка messagebox'а
  UINT uType            // стиль messagebox'а
 );

Давайте посмотрим на другие примеры.

Интересные API

Вся информация была взята из потрясающего справочника по функциям Win32 API. Я помещу только наиболее часто используемые из них. Я действительно рекомендую скачать вам его: там много интересных функций и если бы я поместил здесь все, что считал нужным, этот туториал занял бы 10 метров :).

¤ GetProcAddress

Функция GetProcAddress возвращает адрес указанной экспортируемой функции.

 FARPROC GetProcAddress(
  HMODULE hModule,    // хэндл на модуль DLL
  LPCSTR lpProcName   // имя функции
 );

- Параметры

· hModule

Идентифицирует DLL-модуль, содержащий функцию. Функция LoadLibrary или функция GetModuleHandle возвращает этот хэндл. · lpProcName

Указывает на строку, заканчивающуюся нулем и содержащую имя функции, или укзывающая значение функции по ординалу. Если параметр - ординал, он должен находиться в нижнем слове; верхнее слово должно быть равно нулю.

· Возвращаемые значения

A. Если функция выполнилась успешно, возвращаемое значение будет адресом экспортированной DLL функции.

B. Если во время выполнения функции произошла ошибка, возвращаемое значение равно NULL. Дополнительную информацию об ошибке можно получить с помощью GetLastError.

А теперь вероятно самое интересная функция API:

¤ GetModuleHandle(A/W)

Функция GetModuleHandl возвращает хэндл модуля, если тот был промэппирован в адресное пространство вызывающего процесса.

 HMODULE GetModuleHandle(
  LPCTSTR lpModuleName       // адрес имени модуля, чей хэндл нужен
 );

- Параметры

· lpModuleName

Указывает на строку, в которой содержится имя Win32-модуля (.DLL или .EXE). Если расширение файла было опущено, добавляется расширение .DLL. Строка с именем файла может иметь в конце точку, чтобы показать, что у файла нет расширения. Строка не обязательно должна указывать путь. Имя сравнивается (без учета регистра) с именами модулей уже загруженных в адресное пространство вызывающего процесса.

Если этот параметр равен NULL, GetModuleHandle возвращает хэндл файла, который был использован при создании вызывающего процесса.

· Возвращаемые значения

A. Если функция выполнилась успешно, возвращаемое значение - это хэндл указанного модуля.

B. Если произошла ошибка, возвращаемое значение равно NULL. Чтобы получить расширенную информацию об ошибке, вызовите GetLastError.

¤ FindFirst(A/W)

Функция FindFirstFile осуществляет поиск в директории файла, имя которого совпадает с заданным. FindFirstFile проверяем имена поддиректорий, также как и имена файлов.

 HANDLE FindFirstFile(
  LPCTSTR lpFileName,        // указатель на имя файла
  LPWIN32_FIND_DATA lpFindFileData    // указатель на полученную информацию
 );

- Параметры

· lpFileName

A. Windows 95: Указывает на ASCIIZ-строку, которая задает верную директорию или путь и имя файла. Строка может содержать * и ?. Это строка не должна превышать MAX_PATH символов.

B. Windows NT: Указывает на ASCIIZ-строку, которая задает верную директорию или путь и имя файла. Строка может содержать * и ?.

MAX_PATH - это лимит по умолчанию для путей. Этот лимит оказывает влияние на то, как FindFirstFile парсит пути. Приложение может обойти этот лимит и послать путь больше, чем MAX_PATH, если вызовет расширенную (W) версию FindFirstFle и добавит "\\?\" к началу пути. "\\?\" отключает парсинг пути и позволяет использовать пути длинее, чем MAX_PATH. Это также работает с UNC-именами. "\\?\" как часть пути игнорируется. Например "\\?\C:\myworld\private будет считаться "C:\myworld\private", а "\\?\UNC\bill_g_1\hotstuff\coolapps" - "\\bill_g_1\hotstuff\coolapps".

· lpFindFileData

Указывает на структуру WIN32_FIND_DATA, которая получает информацию о найденном файле или поддиректории. Структура может использоваться в последовательных вызовах FindNextFile или FindClose, чтобы сослаться на файл или директорию.

· Возвращаемые значения

A. Если вызов функции прошел успешно, возвращаемое значение - это хэндл поиска, используемых в следующих вызовах FindNextFile или FindClose.

B. Если происходит ошибка, возвращаемое значение равно INVALID_HANDLE_VALUE. Чтобы получить расширенную информацию, вызовите GetLastError.

¤ FindNext(A/W)

Функция FindNextFile продолжает файловый поиск, начатый в прошлом вызове функцией FindFirstFile.

 BOOL FindNextFile(
  HANDLE hFindFile,   // хэндл поиска
  LPWIN32_FIND_DATA lpFindFileData    // указатель на структуру данных
                                      // найденного файла
 );

- Параметры

· hFindFile

Хэндл поиска, возвращенный предыдущим вызовом функции FindFirstFile.

· lpFindFileData

Указывает на структуру WIN32_FIND_DATA, которая получает информацию о найденном файле или поддиректории. Структура можно использовать в последующих вызовах FindNextFile, чтобы ссылаться на найденный файл или директорию.

· Возвращаемые значения

A. Если вызов функции прошел успешно, возвращаемое значение не равно нулю.

B. Если произошла ошибка, возвращаемое значение равно нулю. Чтобы получить расширенную информацию об ошибке, вызовите GetLastError.

C. Если файлов, подходящих под заданный шаблон найдено не было, функция GetLastError возвратит ERROR_NO_MORE_FILES.

 [** WIN32_FIND_DATA **]

 typedef struct _WIN32_FIND_DATA { // wfd
  DWORD dwFileAttributes;
  FILETIME ftCreationTime;
  FILETIME ftLastAccessTime;
  FILETIME ftLastWriteTime;
  DWORD    nFileSizeHigh;
  DWORD    nFileSizeLow;
  DWORD    dwReserved0;
  DWORD    dwReserved1;
  TCHAR    cFileName[ MAX_PATH ];
  TCHAR    cAlternateFileName[ 14 ];
 } WIN32_FIND_DATA;

- Поля

· dwFileAttributes

Указывает файловые атрибуты найденного файла. Это поле может быть одним из следующих значений [ недостаточно места, чтобы включить их в данный туториал: вы можете найти их в .inc-файлах 29A (29A#2) и в справочнике по функциям Win32 ].

· ftCreationTime

Это структура FILETIME, которая содержит время, когда был создан файл. FindFirstFile и FindNextFile возвращают время файла в UTC-формате. Эти функции устанавливают не поддерживаемые файловой системой поля FILETIME в ноль. Вы можете использовать функцию FileTimeToLocalFileTime, чтобы сконвертировать UTC в местное время, а затем использовать функцию FleTimeToSystemTuime, чтобы сконвертировать местное время в структуру SYSTEMTIME, которая содержит отдельные поля для месяца, дня, года, дня недели, часа, минуты, секунды и миллисекунды.

· ftLastAccessTime

Структура FILETIME содержит время, когда в последний раз к файлу осуществлялся доступ. Время в UTC-формате; поля FILETIME равны нулю, если файловая система не поддерживает их.

· ftLastWriteTime

Задает структуру FILETIME содержит время, когда в файл последний раз осуществлялась запись. Время в UTC-формате; поля FILETIME равны нулю, если файловая система не поддерживает их.

· nFileSizeHigh

Задает верхнее двойное слово размера файла в байтах. Это поле равно нулю, если только размер файла не превышает MAXDWORD. Размер файла равен (nFileSizeHigh * MAXDWORD) + nFileSizeLow.

· nFileSizeLow

Содержит нижнее двойное слово размера файла в байтах.

· dwReserved0

Зарезервировано для будущего использования.

· dwReserved1

Зарезервировано для будущего использования.

· cFileName

ASCIIZ-строка, содержащая имя файла.

· cAlternateFileName

ASCIIZ-строка, содержащая альтернативное имя файла. Это имя в классическом формате 8.3 (filename.ext).

Другие интересные API - GetFileAttributes(A/W), SetFileAttributes(A/W), CreateProcess, CreateFile(A/W), VirtualAlloc, CreateFileMapping(A/W), SetEndOfFile и т.д.

---

Вообщем, можно найти много параллелей между DOS-функциями и функциями API. Ищите их ;).

Давайте продолжим и рассмотрим пример, который мы всегда пишем при изучении нового языка: "Hello, world!" ;).

Hello World in Win32

Это очень просто. Мы должны использовать функцию MessageBoxA, поэтому мы указываем ее с помощью известной команды "extrn", а затем push'им параметры и вызываем данный API.

 .386                                           ; Процессор
 .model         flat                            ; Используются 32-х битные
                                                ; регистры

 extrn          ExitProcess:proc                ; Используемые функции API
 extrn          MessageBoxA:proc

 .data
 szMessage       db      "Hello World!",0
 szTitle         db      "Windows coding - lame example",0

 .code                                          ; Поехали!

 HelloWorld:
                push    large 0
                push    offset szTitle
                push    offset szMessage
                push    large 0
                call    MessageBoxA

                push    large 0
                call    ExitProcess

 end HelloWorld

Как вы можете видеть, это очень просто. Может быть не так просто, как 16-ти битном окружении, но действительно просто, если вы подумаете о всех преимуществах 32-х битного окружения. Вообще, так как это пособие по написанию вирусов предназначается для людей, только начинающих в этой области, я думаю, что пора остановиться. Если вы хотите узнать больше об этом, вам придется подождать туториала, который я пишу сейчас: Пособие по написанию вирусов под Win32 1.00. Звучит круто, правда? :)

2002-2013 (c) wasm.ru