Здравствуйте! Кто-нибудь знает, как использовать функции MmProbeAndLockPages и MmMapLockedPages? Я пишу драйвер, а в нем реализую перехват NtQuerySystemInformation. Системы Windows 2000/XP падают при обращении Диспетчера задач к этой функции. BSOD. Видимо проблема с адресом указателя ASystemInformation. Параметр ASystemInformation нужно отобразить на системное адресное пространство и зафиксировать в памяти. Делают это вышеперечисленные функции, но я не знаю, как их использовать. Может кто приведет кусок кода? Вот код драйвера: Код (Text): #include <ntddk.h> typedef UCHAR BYTE; typedef ULONG DWORD; typedef DWORD* PDWORD; typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation = 0, SystemPerformanceInformation = 2, SystemTimeOfDayInformation = 3, SystemProcessInformation = 5, SystemProcessorPerformanceInformation = 8, SystemInterruptInformation = 23, SystemExceptionInformation = 33, SystemRegistryQuotaInformation = 37, SystemLookasideInformation = 45 } SYSTEM_INFORMATION_CLASS; NTSYSCALLAPI NTSTATUS NTAPI NtQuerySystemInformation(IN SYSTEM_INFORMATION_CLASS SystemInformationClass,OUT PVOID SystemInformation,IN ULONG SystemInformationLength,OUT PULONG ReturnLength OPTIONAL); #pragma pack(push,1) typedef struct _far_jmp { BYTE PushOp; PVOID PushArg; BYTE RetOp; } far_jmp, *pfar_jmp; typedef struct _OldCode { USHORT One; ULONG TWO; } OldCode, *POldCode; #pragma pack(pop) OldCode OldNtQuerySystemInformation; PVOID NewNtQuerySystemInformationAdr; //True функция для перехватываемой функции NTSTATUS TrueNtQuerySystemInformation(SYSTEM_INFORMATION_CLASS ASystemInformationClass,PVOID ASystemInformation,DWORD ASystemInformationLength,PDWORD AReturnLength) { ULONG CR0Reg; NTSTATUS Result; POldCode Func = (POldCode)NtQuerySystemInformation; pfar_jmp Fnjp = (pfar_jmp)NtQuerySystemInformation; __asm { cli // запрещаем прерывания mov eax, cr0 mov CR0Reg,eax and eax,0xFFFEFFFF // сбросить WP bit mov cr0, eax } // снимаем перехват Func->One = OldNtQuerySystemInformation.One; Func->TWO = OldNtQuerySystemInformation.TWO; Result = NtQuerySystemInformation(ASystemInformationClass,ASystemInformation,AS ystemInformationLength,AReturnLength); //устанавливаем перехват Fnjp->PushOp = 0x68; Fnjp->PushArg = NewNtQuerySystemInformationAdr; Fnjp->RetOp = 0xC3; __asm { mov eax, CR0Reg mov cr0, eax // востановить содержимое CR0 sti // разрешаем прерывания } return Result; } //функция - обработчик перехвата NTSTATUS NewNtQuerySystemInformation(SYSTEM_INFORMATION_CLASS ASystemInformationClass,PVOID ASystemInformation,DWORD ASystemInformationLength,PDWORD AReturnLength) { if(!MmIsAddressValid(ASystemInformation)) return STATUS_INVALID_PARAMETER; __try { //Здесь будет реализована обработка... } __except(EXCEPTION_EXECUTE_HANDLER) { return STATUS_INVALID_PARAMETER; } return TrueNtQuerySystemInformation(ASystemInformationClass,ASystemInformatio n,ASystemInformationLength,AReturnLength); } VOID DriverUnload(IN PDRIVER_OBJECT DriverObject) { ULONG CR0Reg; NTSTATUS Result; POldCode Func = (POldCode)NtQuerySystemInformation; __asm { cli // запрещаем прерывания mov eax, cr0 mov CR0Reg,eax and eax,0xFFFEFFFF // сбросить WP bit mov cr0, eax } // снимаем перехват Func->One = OldNtQuerySystemInformation.One; Func->TWO = OldNtQuerySystemInformation.TWO; __asm { mov eax, CR0Reg mov cr0, eax // востановить содержимое CR0 sti // разрешаем прерывания } return; } NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath) { ULONG CR0Reg; POldCode Func = (POldCode)NtQuerySystemInformation; pfar_jmp Fnjp = (pfar_jmp)NtQuerySystemInformation; //устанавливаем перехват __asm { cli // запрещаем прерывания mov eax, cr0 mov CR0Reg,eax and eax,0xFFFEFFFF // сбросить WP bit mov cr0, eax } NewNtQuerySystemInformationAdr = NewNtQuerySystemInformation; OldNtQuerySystemInformation.One = Func->One; OldNtQuerySystemInformation.TWO = Func->TWO; Fnjp->PushOp = 0x68; Fnjp->PushArg = NewNtQuerySystemInformationAdr; Fnjp->RetOp = 0xC3; __asm { mov eax, CR0Reg mov cr0, eax // востановить содержимое CR0 sti // разрешаем прерывания } //назначаем процедуру выгрузки драйвера DriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
Не используй кривой способ хуков с постоянной установкой/снятием джампа. Он годится только для юзермода, и только если нужна максимальная простота. Юзай лучше SDT хуки. Вторая твоя ошибка в том, что нельзя использовать MmIsAddressValid для юзермодных адресов, эта функция подходит только для nonpaged пула. И для того чтобы получить в обработчике хука доступ к данным, совсем не надо ничего переотображать. А если так охото, то в общем случае выглядит так: Код (Text): Mdl = IoAllocateMdl(Addr, Len, FALSE, FALSE, NULL); if (Mdl) { __try { MmProbeAndLockPages(Mdl, UserMode, IoWriteAccess); SysAddr = MmGetSystemAddressForMdl(Mdl); if (SysAddr) { //тут делаем что надо } MmUnlockPages(Mdl); } __except(EXCEPTION_EXECUTE_HANDLER){} IoFreeMdl(Mdl); }
Почему не использовать кривой способ хуков с постоянной установкой/снятием джампа ? Цитата - Перехват через SDT несомненно удобен, но у него имеется один недостаток - его легко обнаружить и удалить. А как иначе перехватывать, если не через SDT и не через "кривые" хуки ?
Время, возможность ошибки => BSoD. Я делал вот так: Код (Text): void WriteFarJmp(PUCHAR pWriteAddr, ULONG dwJmpAddr){ *pWriteAddr = 0x68; pWriteAddr++; *(PULONG)pWriteAddr = dwJmpAddr; pWriteAddr += 4; *pWriteAddr = 0xC3; } void HookFunction(){ ULONG dwCopySize = 0, i; NtYieldExecution = *KeServiceDescriptorTable->NtoskrnlTable.ServiceTable[SYSCALL_NUMBER(ZwYieldExecution)]; for (i = 0; i < COMMAND_COUNT; i++) dwCopySize += SizeOfCode((PUCHAR)NtYieldExecution_New + dwCopySize, NULL); while (dwOldCodeSize < dwCopySize + 6) dwOldCodeSize += SizeOfCode((PUCHAR)NtYieldExecution + dwOldCodeSize, NULL); NtYieldExecution_Old = ExAllocatePool(NonPagedPool, dwOldCodeSize + 6); if (!NtYieldExecution_Old) return ; memcpy(NtYieldExecution_Old, NtYieldExecution, dwOldCodeSize); WriteFarJmp((PUCHAR)NtYieldExecution_Old + dwOldCodeSize, (ULONG)NtYieldExecution + dwOldCodeSize); MODIFY_START memcpy((PVOID)NtYieldExecution, NtYieldExecution_New, dwCopySize); WriteFarJmp((PUCHAR)NtYieldExecution + dwCopySize, (ULONG)NtYieldExecution_New + dwCopySize); MODIFY_END } void UnHookFunction(){ if (!NtYieldExecution_Old) return ; memcpy(NtYieldExecution, NtYieldExecution_Old, dwOldCodeSize); ExFreePool(NtYieldExecution_Old); } Вызов оригинальной функции очень прост: Код (Text): NTSTATUS NtYieldExecution_New(){ DbgPrint("NtYieldExecution called."); return ((TNtYieldExecution)(NtYieldExecution_Old))(); }
Спасибо за код, но можно в кратце узнать работу функции WriteFarJmp. И код не совсем полный, в частности, что скрывается за MODIFY_START и MODIFY_END, что - то вроде Код (Text): __asm { cli mov eax, cr0 mov CR0Reg,eax and eax,0xFFFEFFFF mov cr0, eax } и Код (Text): __asm { sti mov eax, CR0Reg mov cr0, eax // востановить содержимое CR0 } соответсвенно или я ошибаюсь ? И dwOldCodeSize не объявлена, но это ладно главное ее начальное значение - 0 ?
Код (Text): PVOID NtYieldExecution_Old = NULL; ULONG dwOldCodeSize = 0; PVOID NtYieldExecution = NULL; По адресу pWriteAddr, записывает push pJmpAddr/ret. Что при выполнении сделает скачек на pJmpAddr. Код (Text): #define MODIFY_START _asm cli\ _asm mov eax, cr0\ _asm push eax\ _asm and eax, 0xFFFEFFFF\ _asm mov cr0, eax #define MODIFY_END _asm pop eax\ _asm mov cr0, eax\ _asm sti
Great <__asm cli;> После этого тебе уже ничего не страшно. Зачем тогда уровень прерывания задирать ? Да, кстати, это только на однопроцессорных системах поможет
k3internal ну всетаки лучше на всякий пожарный и IRQL поднять. а если префикс LOCK ? хотя не удастся записать 6 байт в одну команду. так что есть маленький шанс того, что кто-то вызовет функцию пока мы еще не записали хук.
А еще у какого-то потока может закончиться время между инструкциями прямо посередине хука. И против этого никакой lock не поможет хм :\
если бы можно было записать это одной командой, то LOCK бы помог. А для двух команд его ставить бесполезно. зы. Какое нафиг время? А как же CLI и IRQL на уровне HIGH? Выше DPC/Dispatch потоки вообще не планируются. ПРо HIGH я вообще молчу..