Здравствуйте, я новичёк в программировании драйверов. Мне нужно перехватить окно в котором осуществляется ввод(в user mode GetForegroundWindow) т.е. нужно вызвать NtUserGetForegroundWindow(ранее этот вопрос обсуждался на форуме тема "Как определить активное акно",но я попробовал, и получил BSOD). Ещё я пробую дёргать эту функцию из SSDT, но она возвращает 0. Вот мой код : Код (Text): PVOID GetInfoTable(ULONG ATableType) { ULONG mSize = 0x4000; PVOID mPtr = NULL; NTSTATUS St; do { mPtr = ExAllocatePool(PagedPool, mSize); memset(mPtr, 0, mSize); if (mPtr) { St = ZwQuerySystemInformation(ATableType, mPtr, mSize, NULL); } else return NULL; if (St == STATUS_INFO_LENGTH_MISMATCH) { ExFreePool(mPtr); mSize = mSize * 2; } } while (St == STATUS_INFO_LENGTH_MISMATCH); if (St == STATUS_SUCCESS) return mPtr; ExFreePool(mPtr); return NULL; } HANDLE GetCsrPid() { HANDLE Process, hObject; HANDLE CsrId = (HANDLE)0; OBJECT_ATTRIBUTES obj; CLIENT_ID cid; UCHAR Buff[0x100]; POBJECT_NAME_INFORMATION ObjName = (PVOID)&Buff; PSYSTEM_HANDLE_INFORMATION_EX Handles; ULONG r; Handles = GetInfoTable(SystemHandleInformation); if (!Handles) return CsrId; for (r = 0; r < Handles->NumberOfHandles; r++) { if (Handles->Information[r].ObjectTypeNumber == 21) //Port object { InitializeObjectAttributes(&obj, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); cid.UniqueProcess = (HANDLE)Handles->Information[r].ProcessId; cid.UniqueThread = 0; if (NT_SUCCESS(NtOpenProcess(&Process, PROCESS_DUP_HANDLE, &obj, &cid))) { if (NT_SUCCESS(ZwDuplicateObject(Process, (HANDLE)Handles->Information[r].Handle,NtCurrentProcess(), &hObject, 0, 0, DUPLICATE_SAME_ACCESS))) { if (NT_SUCCESS(ZwQueryObject(hObject, ObjectNameInformation, ObjName, 0x100, NULL))) { if (ObjName->Name.Buffer && !wcsncmp(L"\\Windows\\ApiPort", ObjName->Name.Buffer, 20)) { CsrId = (HANDLE)Handles->Information[r].ProcessId; } } ZwClose(hObject); } ZwClose(Process); } } } ExFreePool(Handles); return CsrId; } VOID GetActiveWindowHandle(IN PDEVICE_OBJECT DeviceObject, PVOID Context) { NTSTATUS status; HANDLE wnd; status = PsLookupProcessByProcessId((ULONG)GetCsrPid(), &crsEProc); if (!NT_SUCCESS( status )) { DbgPrint("PsLookupProcessByProcessId() error\n"); } DbgPrint("Process ID = %d", (ULONG)GetCsrPid()); KeAttachProcess(crsEProc); originalGetForegroud = (_NtUserGetForegroundWindow_)KeServiceDescriptorTableShadow[1].ServiceTable[0x194]; DbgPrint("WND = %x", originalGetForegroud()); KeDetachProcess(); }
Во-вторых (сорри, что двумя постами) по-моему поток должен быть ГУЙным потоком, чтоб такие gdi API ядерные вызывать.
Универсальная обертка, пользуйся Код (Text): __declspec(naked) NTSTATUS _cdecl ZwXXX( IN ULONG SdtNumber, ... ) { __asm { mov eax, [esp+4] lea edx, [esp+8] int 0x2e ret } } Юзать, например, так: Первый параметр - номер в ссдт (вместе с полем номера таблицы), остальные параметры - аргументы (любое число).
Стуб("Zw обёртка") не нужен. Насчёт GUI-треда верно сказано, при первом вызове стуба выполняется не экспортируемая PsConvertToGuiThread(), собственно и выполняющая необходимую инициализацию. Нужно найти KiSystemService из любого экспортируемого стуба и её юзать, либо Int 0x2e использовать(PreviousMode в этом случае будет установлен в зависимости от селектора кодового сегмента, в KernelMode). Использовать Sysenter нельзя, ибо поток прыгнет в юзермод после отработки.
А чтобы юзать - да, нужен гуи поток. Я щас не поленился и написал тестовый дров. Берешь самую частовызываемую функцию ntos sdt (это NtWaitForMultipleObjects и NtClose), в обработчике хука смотришь в обработчике - если поток GUI (Thread->ServiceTable != KeServiceDescriptorTable), тогда вызываешь ZwXXX (GetForegroundWindow) и сигналишь контрольный эвент, иначе ничего не делаешь. А в кодесе обычном ставишь хук на NtWaitForMultipleObjects и ждешь на контрольном эвенте. Примерный код: Код (Text): // // всякие нужные глобальные переменные :) // ULONG nGetForegroundWindow; ULONG ServiceTableOffset; PVOID OldSdt; KEVENT SynchEvent; ULONG ExecutedOK = FALSE; // Сишный враппер VOID xTest() { PETHREAD Thread = PsGetCurrentThread(); PEPROCESS Process = PsGetCurrentProcess(); KdPrint(("In NewSdt()\n")); KdPrint(("Thread = %p, Process = %p\n", Thread, Process)); // Это гуи поток? PVOID Table = *(PVOID*)((PUCHAR)Thread + ServiceTableOffset); if (Table == KeServiceDescriptorTable) { KdPrint(("Non-GUI thread\n")); return; } // // ДА-дА! Это гуи поток // KdPrint(("In GUI thread!\n")); // Делаем что нам надо PVOID Wnd = (PVOID) ZwXXX (nGetForegroundWindow); KdPrint(("Wnd = %p\n", Wnd)); ExecutedOK = 1; KeSetEvent (&SynchEvent, 0, FALSE); } // Обработчик SDT __declspec(naked) void NewSdt() { __asm { cmp [ExecutedOK], 1 jz _1 call xTest _1: jmp [OldSdt] } } NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { // демонстрационный хард-код ;( ULONG Major, Minor; PsGetVersion (&Major, &Minor, 0, 0); if (Major == 5 && Minor == 1) nGetForegroundWindow = 0x1194; else if (Major == 5 && Minor == 2) nGetForegroundWindow = 0x1193; else { KdPrint(("Unknown windows version %d.%d\n", Major, Minor)); return STATUS_INVALID_PARAMETER; } // Ищем оффсет Thread->ServiceTable PUCHAR Thread = (PUCHAR) PsGetCurrentThread(); for (ULONG i=0; i<PAGE_SIZE; i++) { PVOID *p = (PVOID*)(Thread + i); if (MmIsAddressValid (p)) { if (*p == KeServiceDescriptorTable) { ServiceTableOffset = i; KdPrint(("ServiceTableOffset = %lx\n", ServiceTableOffset)); break; } } } if (!ServiceTableOffset) { KdPrint(("ServiceTableOffset not found!\n")); return STATUS_UNSUCCESSFUL; } // Ищем номер NtWaitForMultipleObjects ULONG nSdt = GetFunctionSdtNumber ("NtWaitForMultipleObjects"); if (!nSdt) { KdPrint(("NtCreateFile not found!\n")); return STATUS_UNSUCCESSFUL; } // Инициализируем синхронизирующий эвент KeInitializeEvent (&SynchEvent, SynchronizationEvent, FALSE); // Хук SDT OldSdt = KeServiceDescriptorTable[0].ServiceTable[nSdt]; KeServiceDescriptorTable[0].ServiceTable[nSdt] = NewSdt; // Ждем пока обработчик найдет гуи поток и выполнит что нам надо KeWaitForSingleObject (&SynchEvent, Executive, KernelMode, FALSE, NULL); // Снимаем хук KeServiceDescriptorTable[0].ServiceTable[nSdt] = OldSdt; // На всякий случай ждем - вдруг какие-то потоки еще застряли внутри нашего хука // Не очень хорошо, но для демонстрационных целей сойдет for (int i=0; i<5; i++) { LARGE_INTEGER Timeout = {-10000 * 100, -1}; KeDelayExecutionThread (KernelMode, FALSE, &Timeout); ZwYieldExecution(); } return STATUS_UNSUCCESSFUL; } PS. Как оказалось NtWaitForMultipleObjects вызывается часто из VMwareUser.exe, а вот NtClose - из WinLogon. Она больше подходит для реальной системы PPS. winlogon и NtClose что-то тоже иногда возвращают ноль. вообщем выполнять пока будет не ноль) Как-то не очень получилось..
Clerk Искать кисистемсервис смысла нет. Насчет PsConvertToGuiThread - проще уж найти GUI-поток и подцепиться к нему. Все равно потоки процесса System не конвертятся. Даже если аттачнуть к csrss. Почему - хз. См код в посте выше Правда? А почему ты дальше пишешь про int 2e/call KiSystemService?
Great Если тред GUI, то не нужна. Для вызова ядерного сервиса не теневого никакой стуб не нужен, я это имел ввиду.
barton Зачем он нужен, если поток нормальный(уточняю: стек ядерный, селекторы тоже, прев. моде соответствующий) ?
Если нужны проверки, тогда = UserMode, если такие проверки не нужны тогда KernelMode. Тоесть по вашему если я хочу к примеру выполнить NtConnectPort() я её не смогу заюзать ?
Не особо понял.. Ты видимо хочешь найти частный случай, доказав, что все-таки можно из ядра юзать sst-сервисы напрямую, без всяких оберток, нарушая весь принцип вызовы системных сервисов. Заодно, обьяснив ТС что такое previous mode, что такое ядерный стек и прочие совершенно не нужные ему в контексте данной задачи вещи, вместо того, чтоб юзать обертку, которая дает тебе возможность не думать ни о каких previousmode
barton Я ничего не хочу доказать, просто ваше мнение интересно(я то при своём). Юзайте что хотите. Тока вот у стуба недостаток - если я юзаю NtOpenProcess() как функцию, а не как сервис, то я к сст не обращаюсь. Если я её юзаю через стуб как сервис при активной проактивке я попадаю в его логово через сст, а дальше что делать с потоком решит фаерволл. Согласитесь в большинстве случаев это главное требование.)
Я понял. Мне интересно было то, что я вдруг подумал, что меня проглючило насчет previous mode и функции реально можно всегда вызывать без оберток. Проглючило с утра. Если интересно именно мое мнение, то оно насчет этого такое: если опять "обходы фаерволов" брать и прочую хэкерную штуку, то конечно лучше вообще тогда NtOpenProcess написать самопальную. На ассемблере обязательно или в опкодах=) Я не сторонник нарушения логики кода из-за внешних требований типа обхода фаерволов. Лучше вынести снятие хуков в одну абстрагированую задачу, снять хуки, а потом юзать нормальный документированый код как другую абстрагированую задачу. Это типа как с крипторами. На вебхаке. Советуют прям в коде бота делать шифрование строк и мусор для антиреверса. Если задача№1 == "сделать покрипченого бота", то я бы предпочел бы сделать отдельно абстрагированый от задачи№1 криптор и абстрагированого от задачи№1 бота, совместив их потом абстрагированым от 1 и 2 части механизмом)
Хотя, есть конечно такое понятие, как "минусы насталько несущественны, а плюсов настолько много, что можно принебречь". Может быть в данной задаче оно как раз имеет смысл.
barton Имхо после прочтения этого топега и на руткитсах я пришёл к выводу что вы не осознаёте суть механизма вызова системных сервисов, дальнейшая дискуссия невозможна.
Спасибо за пояснения. Но как мне вызвать NtUserGetForegroundWindow в определённый момет, т.е. когда я захочу, а не когда будет вызвана KeWaitForMultipleObjects. ? Как мне можно найти GUI поток в котором я смогу вызывать свою функцию.