После прочетния статьи Ms-Rem'а "Обнаружение скрытых процессов" мне рпишла в голову идея попробывать перехватить какое-нибудь неиспользуемое прерывание. Было бы удобно обращаться к драйверу не через Write File, а через ассемблерную инструкцию int. Для перехвата int 2Eh автор использовал код: pushad cli sidt [Idt] mov esi, NewSyscall mov ebx, Idt.Base xchg [ebx + 0x170], si rol esi, 0x10 xchg [ebx + 0x176], si ror esi, 0x10 mov OldSyscall, esi sti popad Я так понял, что Idt.Base указывает на массив структур размером по 8 байт (0x170/0x2E) типа WORD малдший адрес callback DWORD незнаю что WORD старший адрес callback Затем я предположил, что прерывание int 55h не используется системой и накатал следующее: void Int55Hook() { IDT_STRUCT IDT; _asm { pushad cli sidt [IDT] mov esi, NewInt55Proc mov ebx, IDT.Base xchg [ebx + 8*55h], si rol esi, 16 xchg [ebx + 8*55h+6], si rol esi, 16 mov OldInt55Proc, esi sti popad } DPRINT("Int55HookDriver: int 55h is hooked"); } ...... После того, как я получил bsod стал вопрос: Правильно ли я понял формат и размер структуры на массив которых указывает IDT.Base? Является ли прерывание int 55h неиспользуемым? Какие вобще прерывания я могу использывать в своих целях? Буду благодарен за ответы.
Да и еще, что означает __declspec(naked)? чем объявленная таким образом функция отличается от stdcall?
Для начала смотрим с помощью IdtDump, юзается ли прерывание - это раз. Затем, не забываем переключать регистр fs на ядро, и загрузать все сегментные регистры (если они модифицированы юзермодным кодом). После чего - не забываем корректно ставить PreviousMode в структуре потока. Как это делаеться - смотри в сорцах винды функцию KiSestemService. И всеравно, при вызовах некоторых апей может произойти падение (причем не сразу, а только иногда). Причину этого я не нашел, видно просто забыл учесть какую-либо особенность. Учим матчасть по компилятору. Это означает, что функция не будет иметь пролога и эпилога, и компилиться так как есть. З.Ы. и еще советую не забывать про обработку прерываний на многопроцессорных системах. Пример я гдето тут выкладывал.
Я так понимаю, все это относиться уже к функции-обработчику прерывания? Если да, то во-первых вот она: void NewRealInt55Proc(ULONG reg_eax) { DPRINT("Int55HookDriver: int 55h is called, eax == %x08.", reg_eax); } void _declspec(naked) NewInt55Proc() { _asm { pushad pushfd push fs mov di, 0x30 mov fs, di push eax call NewRealInt55Proc pop fs popfd popad } } а во-вторых система падает во время установки обработчика, то есть до NewInt55Proc() дело не доходит. ItdDump говорит, что прерывание свободно 0055 0008:804DE6D2 0 P 32 bits Interrupt Gate User Defined (Non-reserved) Interrupts --добавлено-- да, кстати что-то мне подсказывает, что в конце NewInt55Proc() должен стоять iret....
вписал int 3 в обработчик DriverObject->MajorFunction[IRP_MJ_WRITE] в той части, где приходит irp пакет от юзера с указанием на то, что нужно поставить хук. из SoftIce я смог выжать только то, что bsod наступает сразу после выполнения инструкции xchg [ebx + 8*55h], si. надпись на синем экране разобрать не успеваю - она держиться меньше cекунды. зато если не ставить бряк на функцию IRP_MJ_WRITE, SoftIce показывает что где-то внутри ядра происходит ошибка IRQL_NOT_LESS_OR_EQUAL, не имею понятия, чей IRQL не подходит. Как будто кто-то пытается вызвать функцию прерывания, пока я еще не установил вторую часть адреса. Выкладываю сурсы на всеобщее обозрение, авось кто поможет .
Попробуй такой код: Код (Text): IDT_STRUCT* pIdtStr; IDT_STRUCT* p55IdtStr; __asm { sidt pIdStr; } p55IdtStr=&(pIdtStr[0x55]); __asm { cli; lea eax,NewInt55Proc; mov ebx,p55IdtStr; mov [ebx],ax; shr eax,16; mov [ebx+6],ax; sti; }
Напутал, вроде сейчас поправил: Код (Text): typedef struct _IDT { USHORT IDTLimit; USHORT LowIDTbase; USHORT HiIDTbase; }IDT,*PIDT; #pragma pack(1) typedef struct _IDTSTRUCT { USHORT LowOffset; USHORT selector; UCHAR unused_lo; unsigned char unused_hi:5; unsigned char DPL:2; unsigned char P:1; USHORT HiOffset; } IDTSTRUCT,*PIDTSTRUCT; #pragma pack() IDT idt; PIDTSTRUCT* pIdtStr; PIDTSTRUCT* p55IdtStr; __asm { sidt idt; } pIdtStr=(PIDTSTRUCT)MAKELONG(idt.LowIDTbase,idt.HiIDTbase); p55IdtStr=&(pIdtStr[0x55]); __asm { cli; lea eax,NewInt55Proc; mov ebx,p55IdtStr; mov [ebx],ax; shr eax,16; mov [ebx+6],ax; sti; }
drmist В ключе HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CrashControl установи значение AutoReboot в ноль.
В общем я присмотрелся к драйверу в IDA и сразу стало понятно, почему он не пашет (вобще дурак я - через soft ice мог бы углядеть). Там получается код типа Код (Text): Int55Hook: sidt [ebp+x] ; ... то есть мы пытаемся работать с локальной переменной, память под которую почему-то не выделена. При этом функция была объявлена, как в первом поcте. Вот что у меня получилось: Код (Text): #define HOOKED 0x55 ULONG OldInt55Proc; ULONG IDT_Base; void _declspec(naked) Int55Hook() { _asm { pushad mov ebp, esp sub esp, 6 mov edi, esp sidt [edi] inc edi inc edi mov edi, [edi] mov IDT_Base, edi mov esi, NewInt55Proc cli xchg si, [edi + 8*HOOKED] rol esi, 16 xchg si, [edi + 8*HOOKED+6] sti rol esi, 16 mov OldInt55Proc, esi mov esp, ebp popad ret } } void _declspec(naked) Int55Unhook() { _asm { pushad mov edi, IDT_Base mov esi, OldInt55Proc cli mov [edi + 8*HOOKED], si shr esi, 16 mov [edi + 8*HOOKED+6], si sti popad ret } } Правда я расчитывал, что смогу в ring3 делать int 55h и переходить к обработчику в драйвере, а вместо этого получаю STATUS_ACCES_VIOLATION. Так и запланированно или я может не туда адрес обработчика вписал?
Товарищи, о чем спорите? Я же обьяснил, что выкладывал на форуме модуль, позволяющий работать с прерываниями (в том числе и добавлять новые). Привожу ключевые фрагменты кода: Код (Text): typedef struct _DPC_PARAMS { KDPC Dpc; PCALLBACK_PROC Proc; PVOID Param; PKEVENT SyncEvent; BOOLEAN Syncronous; } DPC_PARAMS, *PDPC_PARAMS; void DpcRoutine( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { PDPC_PARAMS Params = DeferredContext; Params->Proc(Params->Param); if (Params->Syncronous) KeSetEvent(Params->SyncEvent, IO_NO_INCREMENT, FALSE); ExFreePool(Params); } void CallToProcessor( IN CCHAR Cpu, IN PCALLBACK_PROC Proc, IN PVOID Param, IN BOOLEAN Syncronous ) { KEVENT SyncEvent; PDPC_PARAMS DpcParams; DpcParams = ExAllocatePool(NonPagedPool, sizeof(DPC_PARAMS)); if (DpcParams) { KeInitializeEvent(&SyncEvent, NotificationEvent, FALSE); DpcParams->Syncronous = Syncronous; DpcParams->Param = Param; DpcParams->Proc = Proc; DpcParams->SyncEvent = &SyncEvent; KeInitializeDpc(&DpcParams->Dpc, DpcRoutine, DpcParams); KeSetTargetProcessorDpc(&DpcParams->Dpc, Cpu); KeInsertQueueDpc(&DpcParams->Dpc, NULL, NULL); if (Syncronous) KeWaitForSingleObject(&SyncEvent, Executive, KernelMode, FALSE, NULL); } } void CallToAllProcessors( IN PCALLBACK_PROC Proc, IN PVOID Param, IN BOOLEAN NoSelfCall, IN BOOLEAN Syncronous ) { KIRQL OldIrql; CCHAR Num; ULONG MyProc; PDPC_PARAMS DpcParams; if (KeNumberProcessors == 1) { if (!NoSelfCall) { OldIrql = KeRaiseIrqlToDpcLevel(); Proc(Param); KeLowerIrql(OldIrql); } } else { MyProc = KeGetCurrentProcessorNumber(); for (Num = 0; Num < KeNumberProcessors; Num++) { if (!NoSelfCall || Num != MyProc) CallToProcessor(Num, Proc, Param, Syncronous); } } } void GetInt( IN OUT INTERRUPT *Interrupt ) { IDTR Idt; PINTDESC IntGate; __asm sidt[Idt]; IntGate = &((PINTDESC)Idt.Base)[Interrupt->IntNum]; Interrupt->Handler = (PVOID)((IntGate->OffsetHi << 16) | IntGate->OffsetLo); Interrupt->Selector = IntGate->Selector; Interrupt->Access = IntGate->Access; } void SetInt( IN INTERRUPT *Interrupt ) { IDTR Idt; PINTDESC IntGate; __asm cli; __asm sidt[Idt]; IntGate = &((PINTDESC)Idt.Base)[Interrupt->IntNum]; IntGate->OffsetHi = (USHORT)((ULONG)Interrupt->Handler >> 16); IntGate->OffsetLo = (USHORT)Interrupt->Handler; IntGate->Selector = Interrupt->Selector; IntGate->Access = Interrupt->Access; __asm sti; } void GetInterrupt( IN UCHAR IntNum, OUT INTERRUPT *Interrupt ) { KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); Interrupt->IntNum = IntNum; GetInt(Interrupt); KeLowerIrql(OldIrql); } void GetInterruptEx( IN CCHAR Cpu, IN UCHAR IntNum, OUT INTERRUPT *Interrupt ) { Interrupt->IntNum = IntNum; CallToProcessor(Cpu, GetInt, Interrupt, TRUE); } void SetInterruptEx( IN CCHAR Cpu, IN PINTERRUPT Interrupt ) { CallToProcessor(Cpu, SetInt, Interrupt, TRUE); } void SetInterrupt( IN PINTERRUPT Interrupt ) { CallToAllProcessors(SetInt, Interrupt, FALSE, TRUE); } UCHAR GetFreeInterrupt() { KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); IDTR Idt; PINTDESC IntGate; USHORT IntNum; UCHAR Result = 0; __asm sidt[Idt]; for (IntNum = 0; IntNum <= Idt.Limit / sizeof(INTDESC); IntNum++) { IntGate = &((PINTDESC)Idt.Base)[IntNum]; if (!(IntGate->Access & ACC_PRESENT)) { Result = (UCHAR)IntNum; break; } } KeLowerIrql(OldIrql); return Result; } BOOLEAN AddInterrupt( IN OUT INTERRUPT *Interrupt ) { Interrupt->IntNum = GetFreeInterrupt(); if (Interrupt->IntNum) SetInterrupt(Interrupt); return Interrupt->IntNum; } Код (Text): #define ACC_PRESENT 0x80 // ñåãìåíò åñòü â ïàìÿòè #define ACC_CSEG 0x18 // ñåãìåíò êîäà #define ACC_DSEG 0x10 // ñåãìåíò äàííûõ #define ACC_EXPDOWN 0x04 // ñåãìåíò ðàñøèðÿåòñÿ âíèç #define ACC_CONFORM 0x04 // ñîãëàñîâàííûé ñåãìåíò #define ACC_DATAWR 0x02 // ðàçðåøåíà çàïèñü #define ACC_INT_GATE 0x06 // âåíòèëü ïðåðûâàíèÿ #define ACC_TRAP_GATE 0x07 // âåíòèëü èñêëþ÷åíèÿ #define ACC_CALL_GATE 0x0C // øëþç âûçîâà #define ACC_32BIT 0x08 // 32 áèòíûé äåñêðèïòîð #define DPL_0 0x00 #define DPL_1 0x20 #define DPL_2 0x40 #define DPL_3 0x60 #define DATA_ACC ACC_PRESENT | ACC_DSEG | ACC_DATAWR // ñåãìåíò äàííûõ #define CODE_ACC ACC_PRESENT | ACC_CSEG | ACC_CONFORM // ñåãìåíò êîäà #define STACK_ACC ACC_PRESENT | ACC_DSEG | ACC_DATAWR | ACC_EXPDOWN // ñåãìåíò ñòåêà #define IDT_ACC DATA_ACC // áàéò äîñòóïà ñåãìåíòà òàáëèöû IDT #define INT_ACC ACC_PRESENT | ACC_INT_GATE | ACC_32BIT // áàéò äîñòóïà âåíòèëÿ ïðåðûâàíèÿ #define GATE_ACC ACC_PRESENT | ACC_CALL_GATE | ACC_32BIT // áàéò äîñòóïà øëþçà âûçîâà #define TRAP_ACC ACC_PRESENT | ACC_TRAP_GATE | ACC_32BIT // áàéò äîñòóïà âåíòèëÿ èñêëþ÷åíèÿ #pragma pack(push, 1) typedef struct _INTDESC { USHORT OffsetLo; USHORT Selector; UCHAR Unused; UCHAR Access; USHORT OffsetHi; } INTDESC, *PINTDESC; typedef struct _GATEDESC { USHORT OffsetLo; USHORT Selector; UCHAR Unused; UCHAR Access; USHORT OffsetHi; } GATEDESC, *PGATEDESC; typedef struct _GDTR { USHORT Limit; ULONG Base; } GDTR, *PGDTR; typedef struct _INTERRUPT { UCHAR IntNum; PVOID Handler; USHORT Selector; UCHAR Access; } INTERRUPT, *PINTERRUPT; С его помощью добавление прерывания - тривиальное дело, и выгляди так: Код (Text): IntGate.Access = INT_ACC | DPL_3; IntGate.Handler = SystemService; IntGate.Selector = 0x08; Result = AddInterrupt(&IntGate); Вот мой вариант обработчика системных вызовов (найди три ошибки) Код (Text): _declspec(naked) SystemService() { __asm { push fs push 0x30 pop fs sti cmp eax, MAX_SYSCALL jnb __exit cmp edx, MmUserProbeAddress jnb __exit push __handler push dword ptr fs:[0] mov dword ptr fs:[0], esp movzx ecx, ArgumentTable[eax] lea edx, [edx + ecx * 4 - 4] __next: jecxz __call push [edx] sub edx, 0x04 dec ecx jmp __next __call: call ServiceTable[eax * 4] jmp __unseh __handler: mov eax, [esp + 0x0C] mov ecx, [esp + 0x08] mov ebx, __unseh mov [eax + 0xB8], ebx mov ebx, 0xC0000005 mov [eax + 0xB0], ebx mov [eax + 0xC4], ecx xor eax, eax ret __unseh: pop dword ptr fs:[0] add esp, 4 __exit: pop fs iretd } } Надеюсь что этот код поможет, но сомневаюсь (сильно долго вы мучаетесь с примитивных мобавлением прерывания, без учета SMP и прочих гадостей).
Большое спасибо. Значицо я не указал Selector и Access. --добавлено-- В аттаче утилита-пример, которая делает то, что мне хотелось (сурсы+бинарник). Работает при условии, что в машине один процессор и int 55h никем не занят. Использование: load hook int55 unhook unload exit Предварительно не зыбываем включить DbgView.