Добрый день! Я решил написать простой перехватчик NtUserSetWindowsHookEx на ассемблере. Драйвер находит адрес KeServiceDescriptorTableShadow, находит главный поток винлогона и в нем делает apc. Эта процедура уже и записывает по нужному смещению адрес моей ловушки, которая должна вывести текстовое сообщение и передать управление реальной функции. Но проблема в том, что как только я запускаю банальный кейлогер, система перезагружается и до моей ловушки дело вообще не доходит (я пробовал ее зациклить в выводе сообщений, результат прежний). В чем может быть проблема?
Если как-то может помочь, то вот код. Я не силен в ассемблере и в С, поэтому прошу не судить строго). Здесь не предусмотрена передача управления реальной функции + нет возвращения в таблицу исходного адреса. Пока я пытаюсь сделать так, чтобы моей ловушке хотя бы передавалось управление... Код (Text): ;@echo off ;goto make .386 .model flat, stdcall option casemap:none ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; I N C L U D E F I L E S ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: include \masm32\include\w2k\ntstatus.inc include \masm32\include\w2k\ntddk.inc include \masm32\include\w2k\ntoskrnl.inc includelib \masm32\lib\w2k\ntoskrnl.lib include \masm32\mProgs\Macros\Strings.mac include seh0.inc include common1.inc include ProcPath.asm .data? StartAPC KAPC <> StopAPC KAPC <> SDTShAdr DWORD ? FileNameOffset DWORD ? ThreadListHeadOffset DWORD ? PETHREAD DWORD ? CR0Reg DWORD ? TrueProcAddress DWORD ? ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; C O D E ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .code NewHook proc Param1:DWORD, Param2:DWORD, Param3:DWORD, Param4:DWORD,Param5:DWORD, Param6:DWORD mov ecx, 0ffffffffh .while ecx dec ecx invoke DbgPrint, $CTA0("...") .endw xor eax, eax ret NewHook endp PatchSDTShadowTable proc Apc:PKAPC, NormalRoutine:PVOID, NormalContext:DWORD, SystemArgument1:DWORD, SystemArgument2:DWORD cli mov eax, cr0 mov CR0Reg,eax and eax,0FFFEFFFFh mov cr0, eax mov eax, SDTShAdr _try add eax, 16 mov eax, [eax] mov edx, [eax+225h*4] mov TrueProcAddress, edx mov [eax+225h*4], NewHook _finally mov eax,CR0Reg mov cr0,eax sti invoke DbgPrint, $CTA0("Adress Table Pointer: %X\n"), TrueProcAddress mov eax, 0 ret PatchSDTShadowTable endp UnPatchSDTShadowTable proc Apc:PKAPC, NormalRoutine:PVOID, NormalContext:DWORD, SystemArgument1:DWORD, SystemArgument2:DWORD ret UnPatchSDTShadowTable endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; GetServiceDescriptorTableShadowAddress ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: GetServiceDescriptorTableShadowAddress proc uses esi edi ebx local dwThreadId:DWORD local peThread:PVOID local dwCurThread:DWORD local TargetPeProcess:DWORD local PeProcessEntry:DWORD local us1:UNICODE_STRING local as1:ANSI_STRING local TargetPeThread:DWORD local peTHREAD:DWORD local flag:DWORD xor ebx, ebx ; = NULL. Assume ServiceDescriptorTableShadow will be not found mov flag, ebx mov eax, KeServiceDescriptorTable mov esi, [eax] ; Find KTHREAD.ServiceTable field ; For non-GUI threads this field == KeServiceDescriptorTable ; and it points to ServiceDescriptorTable ; For GUI threads ; ServiceDescriptorTableShadow invoke KeGetCurrentThread mov edi, 200h-4 .while edi .break .if dword ptr [eax][edi] == esi dec edi .endw push eax invoke DbgPrint, $CTA0("KSDT Entry In PETHREAD: %X\n"), edi pop eax push eax push esi push edi invoke PsGetCurrentProcess push eax invoke DbgPrint, $CTA0("Current process PEPROCESS: %X\n"), eax pop ecx invoke KeGetCurrentThread mov edi, 200h-4 .while edi .break .if dword ptr [eax][edi] == ecx dec edi .endw mov PeProcessEntry, edi invoke DbgPrint, $CTA0("PEPROCESS Entry In PETHREAD: %X\n"), edi invoke PsGetCurrentProcessId invoke DbgPrint, $CTA0("Current process ID: %X\n"), eax pop edi pop esi pop eax .if edi != 0 ; edi = offset to ServiceTable field in KTHREAD structure mov dwThreadId, 080h .while dwThreadId < 400h push eax ; reserve DWORD on stack invoke PsLookupThreadByThreadId, dwThreadId, esp pop ecx ; -> ETHREAD/KTHREAD mov peThread,ecx .if eax == STATUS_SUCCESS push esi push edi mov ecx, peThread mov edi, PeProcessEntry push dword ptr [ecx][edi] pop esi mov TargetPeProcess, esi pop edi pop esi push dword ptr [ecx][edi] ;fastcall ObfDereferenceObject, ecx pop eax .if eax != esi mov edx, MmSystemRangeStart mov edx, [edx] mov edx, [edx] .if eax > edx ; some stupid error checking push eax push ecx push esi push edi ;mov ecx, peThread ;mov edi, PeProcessEntry ;push dword ptr [ecx][edi] ;pop esi ;mov TargetPeProcess, esi push eax invoke GetImageFilePath, TargetPeProcess, addr us1 .if eax==STATUS_SUCCESS invoke RtlUnicodeStringToAnsiString,addr as1,addr us1,TRUE invoke DbgPrint,as1.Buffer invoke ExFreePool,us1.Buffer invoke RtlFreeAnsiString,addr as1 .endif invoke DbgPrint, $CTA0("Target Process PEPROCESS: %X\n"), TargetPeProcess mov eax, TargetPeProcess add eax, FileNameOffset invoke _strnicmp, eax, $CTA0("winlogon.exe"), 12 .if eax == NULL invoke DbgPrint, $CTA0("Found") mov ecx, peThread mov TargetPeThread, ecx mov eax, TargetPeProcess add eax, ThreadListHeadOffset mov eax, (LIST_ENTRY PTR [eax]).Flink sub eax, 22Ch mov PETHREAD, eax invoke DbgPrint, $CTA0("FirstThread: %X\n"), eax inc flag .endif pop eax mov ebx, eax mov SDTShAdr,ebx invoke DbgPrint, $CTA0("FindShadowTable: Found in thread with ID: %X\n"), dwThreadId mov ecx, peThread fastcall ObfDereferenceObject, ecx pop edi pop esi pop ecx pop eax .break .if flag .endif .endif .endif add dwThreadId, 4 .endw .endif ;mov ecx, peThread ;fastcall ObfDereferenceObject, ecx mov ebx, SDTShAdr mov eax, ebx ret GetServiceDescriptorTableShadowAddress endp GetImageFileNameOffset proc uses esi ebx ; Finds EPROCESS.ImageFileName field offset ; W2K EPROCESS.ImageFileName = 01FCh ; WXP EPROCESS.ImageFileName = 0174h ; WNET EPROCESS.ImageFileName = 0154h ; Instead of hardcoding above offsets we just scan ; the EPROCESS structure of System process one page down. ; It's well-known trick. invoke IoGetCurrentProcess mov esi, eax xor ebx, ebx .while ebx < 1000h ; one page more than enough. ; Case insensitive compare. lea eax, [esi+ebx] invoke _strnicmp, eax, $CTA0("system"), 6 .break .if eax == 0 inc ebx .endw .if eax == 0 ; Found. mov eax, ebx .else ; Not found. xor eax, eax .endif ret GetImageFileNameOffset endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DriverEntry ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING invoke DbgPrint, $CTA0("\nFindShadowTable: Entering DriverEntry\n") invoke GetImageFileNameOffset mov FileNameOffset, eax invoke DbgPrint, $CTA0("ImageFileNameOffset: %X\n"), eax mov eax, 190h sub eax, 174h add eax, FileNameOffset mov ThreadListHeadOffset, eax invoke DbgPrint, $CTA0("ThreadListHeadOffset: %X\n"), eax mov eax, KeServiceDescriptorTable mov eax, [eax] invoke DbgPrint, $CTA0("FindShadowTable: ServiceDescriptorTable at address: %08X\n"), eax _try lea eax, KeServiceDescriptorTable mov eax, [eax] ;add eax, 16 mov eax, [eax] mov eax, [eax+225h*4] _finally invoke DbgPrint, $CTA0("FindShadowTable: ServiceDescriptorTable at address: %08X\n"), eax _try lea eax, KeServiceDescriptorTable mov eax, [eax] ;add eax, 16 mov eax, [eax] mov eax, [eax+125h*4] _finally invoke DbgPrint, $CTA0("FindShadowTable: ServiceDescriptorTable at address: %08X\n"), eax invoke GetServiceDescriptorTableShadowAddress .if eax != NULL invoke DbgPrint, $CTA0("FindShadowTable: ServiceDescriptorTableShadow found at address: %08X\n"), eax .endif invoke KeInitializeApc, addr StartAPC, PETHREAD, 0, PatchSDTShadowTable, NULL, NULL, NULL, NULL invoke KeInsertQueueApc, addr StartAPC, NULL, NULL, 0 invoke DbgPrint, $CTA0("FindShadowTable: Leaving DriverEntry\n") mov eax, STATUS_DEVICE_CONFIGURATION_ERROR ret DriverEntry endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: end DriverEntry :make set drv=FindShadowTable \masm32\bin\ml /nologo /c /coff %drv%.bat \masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj del %drv%.obj echo. pause
Неужели никто не писал подобные вещи?! Гугл дает только примеры на С (который я не очень перевариваю...). Пытался перевести на асм, но результат прежний(
TSS Мой уровень знаний действительно недостаточен (я упоминал выше), но он не может быть нулевым... Как мне кажется, вопросы задают для того, чтобы те, кому есть что сказать, помогли решить проблему, а не указывали на уровень знаний задавшего этот вопрос. Прошу прощения конечно, без наездов, а Вам так случайно не кажется?!
Начал смотреть, написал скрипт для перечисления потоков: Код (Text): .echo Find process: r $t0 = nt!PsActiveProcessHead .for (r $t1 = poi(@$t0); (@$t1 != 0) & (@$t1 != @$t0); r $t1 = poi(@$t1)) { r? $t2 = #CONTAINING_RECORD(@$t1, nt!_EPROCESS, ActiveProcessLinks); as /x ${/v:$Procc} @$t2 as /ma ${/v:$ImageName} @@c++(&@$t2->ImageFileName[0]) .block { .if ( $scmp( "${$ImageName}", "winlogon.exe" ) == 0 ) { .echo ${$ImageName} at ${$Procc} .echo " KTHREAD Tid Alertable" r? $t3 = (nt!_LIST_ENTRY *)&@$t2->ThreadListHead; .for (r $t4 = poi(@$t3); (@$t4 != 0) & (@$t4 != @$t3); r $t4 = poi(@$t4)) { r? $t5 = #CONTAINING_RECORD(@$t4, nt!_ETHREAD, ThreadListEntry) r? $t6 = @$t5->Cid r? $t6 = (nt!_CLIENT_ID *)&@$t6 r $t6 = @@c++(@$t6->UniqueThread) r? $t7 = (nt!_KTHREAD *)@$t5 r $t8 = @@c++(@$t7->Alertable) as /x ${/v:$Kthread} @$t7 as /x ${/v:$Tid} @$t6 as /x ${/v:$Alertable} @$t8 .block { .echo ${$Kthread} ${$Tid} ${$Alertable} } ad ${/v:$Kthread} ad ${/v:$Tid} ad ${/v:$Alertable} } ad ${/v:$ImageName} ad ${/v:$Procc} .break } } ad ${/v:$ImageName} ad ${/v:$Procc} } Таки да, есть у winlogon потоки которым можно apc послать, потом глянул код, в драйвере(!) шлется apc винлогонуЮ, чтобы пропатчить shadow sdt(ранее вычисленный адрес таблицы благополучно оставляется в забытьи) вобщем бредокод скопипащенный откудато, автор не мучай себя и обитателей сего форума, в коммерц дружище, в коммерц
Да нет, с адресом таблицы как раз все в порядке+адрес реальной функции, считываемый внутри апц является действительным адресом внутри вин32к!
Проблема в том, что как только на месте реального адреса оказывается адрес моей ловушки, то система через некоторое время падает...
Я забыл исправить STATUS_DEVICE_CONFIGURATION_ERROR на STATUS_SUCCESS. Что ж, и такое бывает... Спасибо