/* * @implemented */ BOOLEAN NTAPI KeSetTimerEx(IN OUT PKTIMER Timer, IN LARGE_INTEGER DueTime, IN LONG Period, IN PKDPC Dpc OPTIONAL) { KIRQL OldIrql; BOOLEAN Inserted; ULONG Hand = 0; BOOLEAN RequestInterrupt = FALSE; ASSERT_TIMER(Timer); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); DPRINT("KeSetTimerEx(): Timer %p, DueTime %I64d, Period %d, Dpc %p\n", Timer, DueTime.QuadPart, Period, Dpc); /* Lock the Database and Raise IRQL */ OldIrql = KiAcquireDispatcherLock(); /* Check if it's inserted, and remove it if it is */ Inserted = Timer->Header.Inserted; if (Inserted) KxRemoveTreeTimer(Timer); /* Set Default Timer Data */ Timer->Dpc = Dpc; Timer->Period = Period; if (!KiComputeDueTime(Timer, DueTime, &Hand)) { /* Signal the timer */ RequestInterrupt = KiSignalTimer(Timer); /* Release the dispatcher lock */ KiReleaseDispatcherLockFromDpcLevel(); /* Check if we need to do an interrupt */ if (RequestInterrupt) HalRequestSoftwareInterrupt(DISPATCH_LEVEL); } else { /* Insert the timer */ Timer->Header.SignalState = FALSE; KxInsertTimer(Timer, Hand); } /* Exit the dispatcher */ KiExitDispatcher(OldIrql); /* Return old state */ return Inserted; }
VOID NTAPI KiAdjustQuantumThread(IN PKTHREAD Thread) { PKPRCB Prcb = KeGetCurrentPrcb(); PKTHREAD NextThread; /* Acquire thread and PRCB lock */ KiAcquireThreadLock(Thread); KiAcquirePrcbLock(Prcb); /* Don't adjust for RT threads */ if ((Thread->Priority < LOW_REALTIME_PRIORITY) && (Thread->BasePriority < (LOW_REALTIME_PRIORITY - 2))) { /* Decrease Quantum by one and see if we've ran out */ if (--Thread->Quantum <= 0) { /* Return quantum */ Thread->Quantum = Thread->QuantumReset; /* Calculate new Priority */ Thread->Priority = KiComputeNewPriority(Thread, 1); /* Check if there's no next thread scheduled */ if (!Prcb->NextThread) { /* Select a ready thread and check if we found one */ NextThread = KiSelectReadyThread(Thread->Priority, Prcb); if (NextThread) { /* Set it on standby and switch to it */ NextThread->State = Standby; Prcb->NextThread = NextThread; } } else { /* This thread can be preempted again */ Thread->Preempted = FALSE; } } } /* Release locks */ KiReleasePrcbLock(Prcb); KiReleaseThreadLock(Thread); KiExitDispatcher(Thread->WaitIrql); }
/*++ * @name KeInsertQueueApc * @implemented NT4 * * The KeInsertQueueApc routine queues a APC for execution when the right * scheduler environment exists. * * @param Apc * Pointer to an initialized control object of type APC for which the * caller provides the storage. * * @param SystemArgument[1,2] * Pointer to a set of two parameters that contain untyped data. * * @param PriorityBoost * Priority Boost to apply to the Thread. * * @return If the APC is already inserted or APC queueing is disabled, FALSE. * Otherwise, TRUE. * * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered, * and at PASSIVE_LEVEL for the NormalRoutine registered. * * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. * *--*/ BOOLEAN NTAPI KeInsertQueueApc(IN PKAPC Apc, IN PVOID SystemArgument1, IN PVOID SystemArgument2, IN KPRIORITY PriorityBoost) { PKTHREAD Thread = Apc->Thread; KLOCK_QUEUE_HANDLE ApcLock; BOOLEAN State = TRUE; ASSERT_APC(Apc); ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); /* Get the APC lock */ KiAcquireApcLock(Thread, &ApcLock); /* Make sure we can Queue APCs and that this one isn't already inserted */ if (!(Thread->ApcQueueable) || (Apc->Inserted)) { /* Fail */ State = FALSE; } else { /* Set the System Arguments and set it as inserted */ Apc->SystemArgument1 = SystemArgument1; Apc->SystemArgument2 = SystemArgument2; Apc->Inserted = TRUE; /* Call the Internal Function */ KiInsertQueueApc(Apc, PriorityBoost); } /* Release the APC lock and return success */ KiReleaseApcLockFromDpcLevel(&ApcLock); KiExitDispatcher(ApcLock.OldIrql); return State; }
VOID FASTCALL KeSignalGateBoostPriority ( __inout PKGATE Gate ) /*++ Routine Description: This function conditionally sets the signal state of a gate object and attempts to unwait the first waiter. Arguments: Gate - Supplies a pointer to a dispatcher object of type gate. Return Value: None. --*/ { PKTHREAD CurrentThread; PLIST_ENTRY Entry; KIRQL OldIrql; SCHAR Priority; PKQUEUE Queue; PKWAIT_BLOCK WaitBlock; PKTHREAD WaitThread; ASSERT_GATE(Gate); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); // // Raise IRQL to SYNCH_LEVEL and acquire the object lock. // // If the object is not already signaled, then attempt to wake a waiter. // CurrentThread = KeGetCurrentThread(); do { OldIrql = KeRaiseIrqlToSynchLevel(); KiAcquireKobjectLock(Gate); if (Gate->Header.SignalState == 0) { // // If there are any waiters, then remove and ready the first // waiter. Otherwise, set the signal state. // if (IsListEmpty(&Gate->Header.WaitListHead) == FALSE) { Entry = Gate->Header.WaitListHead.Flink; WaitBlock = CONTAINING_RECORD(Entry, KWAIT_BLOCK, WaitListEntry); WaitThread = WaitBlock->Thread; // // Try to acquire the thread lock. // // If the thread lock cannot be acquired, then release the // object lock, lower IRQL to is previous value, and try to // signal the gate again. Otherwise, remove the entry from // the list, set the wait completion status to success, set // the deferred processor number, set the thread state to // deferred ready, release the thread lock, release the // object lock, compute the new thread priority, ready the // thread for execution, and exit the dispatcher. // if (KiTryToAcquireThreadLock(WaitThread)) { RemoveEntryList(Entry); WaitThread->WaitStatus = STATUS_SUCCESS; WaitThread->State = DeferredReady; WaitThread->DeferredProcessor = KeGetCurrentPrcb()->Number; KiReleaseKobjectLock(Gate); KiReleaseThreadLock(WaitThread); Priority = CurrentThread->Priority; if (Priority < LOW_REALTIME_PRIORITY) { Priority = Priority - CurrentThread->PriorityDecrement; if (Priority < CurrentThread->BasePriority) { Priority = CurrentThread->BasePriority; } if (CurrentThread->PriorityDecrement != 0) { CurrentThread->PriorityDecrement = 0; CurrentThread->Quantum = CLOCK_QUANTUM_DECREMENT; } } WaitThread->AdjustIncrement = Priority; WaitThread->AdjustReason = (UCHAR)AdjustBoost; // // If the wait thread is associated with a queue, then // increment the concurrency level. // // N.B. This can be done after dropping all other locks, // but must be done holding the dispatcher lock // since the concurrency count is not accessed with // interlocked operations elsewhere. // if (WaitThread->Queue != NULL) { KiLockDispatcherDatabaseAtSynchLevel(); if ((Queue = WaitThread->Queue) != NULL) { Queue->CurrentCount += 1; } KiUnlockDispatcherDatabaseFromSynchLevel(); } KiDeferredReadyThread(WaitThread); KiExitDispatcher(OldIrql); return; } else { KiReleaseKobjectLock(Gate); KeLowerIrql(OldIrql); continue; } } else { Gate->Header.SignalState = 1; break; } } else { break; } } while (TRUE); // // Release the object lock and lower IRQL to its previous value. // KiReleaseKobjectLock(Gate); KeLowerIrql(OldIrql); return; }
/************************************************************************ *************************************** PgDumpTimerTable ************************************************************************* Description: All PatchGuard 2 related timers will wear the "suspect" sttribute. ATTENTION: The code uses undocumented kernel APIs. Please keep in mind that you shouldn't change the code logic and remember that during enumeration your code will run at DISPATCH_LEVEL! */ NTSTATUS PgDumpTimerTable() { KIRQL OldIrql; ULONG Index; PKSPIN_LOCK_QUEUE LockQueue; PKTIMER_TABLE_ENTRY TimerListHead; PLIST_ENTRY TimerList; PKTIMER Timer; PKDPC TimerDpc; CHAR LogEntryText[2048]; NTSTATUS Result = STATUS_SUCCESS; HANDLE hLogFile; UNICODE_STRING LogFileName; OBJECT_ATTRIBUTES ObjAttr; IO_STATUS_BLOCK IOStatus; ULONG LogEntryTextLen; SINGLE_LIST_ENTRY LogListHead = {NULL}; PSINGLE_LIST_ENTRY LogList; LOGENTRY* LogEntry; ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); /* Open log file... */ RtlInitUnicodeString(&LogFileName, L"\\??\\C:\\patchguard.log"); InitializeObjectAttributes( &ObjAttr, &LogFileName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL) if(!NT_SUCCESS(Result = ZwCreateFile( &hLogFile, GENERIC_WRITE, &ObjAttr, &IOStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, NULL, 0))) { KdPrint(("\r\n" "ERROR: Unable to open file \"\\??\\C:\\patchguard.log\". (NTSTATUS: 0x%p)\r\n", (void*)Result)); return Result; } /* Lock the dispatcher database and loop through the timer list... */ Result = STATUS_SUCCESS; OldIrql = KiAcquireDispatcherLockRaiseToSynch(); for(Index = 0; Index < TIMER_TABLE_SIZE; Index++) { // we have to emulate the windows timer bug "Index & 0xFF" for this to work... LockQueue = KeTimerIndexToLockQueue((UCHAR)(Index & 0xFF)); KeAcquireQueuedSpinLockAtDpcLevel(LockQueue); // now we can work with the timer list... TimerListHead = &KiTimerTableListHead[Index]; TimerList = TimerListHead->Entry.Flink; while(TimerList != (PLIST_ENTRY)TimerListHead) { Timer = CONTAINING_RECORD(TimerList, KTIMER, TimerListEntry); TimerDpc = PgDeobfuscateTimerDpc(Timer); TimerList = TimerList->Flink; if(TimerDpc != NULL) { memset(LogEntryText, 0, sizeof(LogEntryText)); LogEntryTextLen = _snprintf(LogEntryText, sizeof(LogEntryText) - 1, "<timer address=\"%p\" index=\"%d\" period=\"0x%p\" hand=\"%d\" duetime=\"0x%p\">\r\n" "%s" " <dpc>\r\n" " <DeferredContext value=\"0x%p\">%s</DeferredContext>\r\n" " <DeferredRoutine>0x%p</DeferredRoutine>\r\n" " <DpcListBlink value=\"0x%p\">%s</DpcListBlink>\r\n" " <DpcListFlink value=\"0x%p\">%s</DpcListFlink>\r\n" " <DpcData value=\"0x%p\">%s</DpcData>\r\n" " <Importance>%d</Importance>\r\n" " <Number>%d</Number>\r\n" " <SystemArgument1 value=\"0x%p\">%s</SystemArgument1>\r\n" " <SystemArgument2 value=\"0x%p\">%s</SystemArgument2>\r\n" " <Type>%d</Type>\r\n" " </dpc>\r\n" "</timer>\r\n\r\n", Timer, Index, (ULONGLONG)Timer->Period, (ULONG)Timer->Header.Hand, Timer->DueTime.QuadPart, PgIsPatchGuardContext(TimerDpc->DeferredContext)?" <SUSPECT>true</SUSPECT>\t\n":"", TimerDpc->DeferredContext, PointerToString(TimerDpc->DeferredContext), TimerDpc->DeferredRoutine, TimerDpc->DpcListEntry.Blink, PointerToString(TimerDpc->DpcListEntry.Blink), TimerDpc->DpcListEntry.Flink, PointerToString(TimerDpc->DpcListEntry.Flink), TimerDpc->DpcData, PointerToString(TimerDpc->DpcData), (ULONG)TimerDpc->Importance, (ULONG)TimerDpc->Number, TimerDpc->SystemArgument1, PointerToString(TimerDpc->SystemArgument1), TimerDpc->SystemArgument2, PointerToString(TimerDpc->SystemArgument2), (ULONG)TimerDpc->Type ); // allocate memory and add log entry to list... if((LogEntry = (LOGENTRY*)ExAllocatePool(NonPagedPool, sizeof(LOGENTRY) + LogEntryTextLen + 1)) == NULL) { KeReleaseQueuedSpinLockFromDpcLevel(LockQueue); Result = STATUS_NO_MEMORY; DbgPrint("\r\n" "WARNING: Not enough non-paged memory to write suspect timer to file. Aborting enumeration...\r\n"); break; } LogEntry->Text = (CHAR*)(LogEntry + 1); LogEntry->Length = LogEntryTextLen; memcpy(LogEntry->Text, LogEntryText, LogEntryTextLen); PushEntryList(&LogListHead, &LogEntry->List); } } KeReleaseQueuedSpinLockFromDpcLevel(LockQueue); } KiReleaseDispatcherLockFromSynchLevel(); KiExitDispatcher(OldIrql); KdPrint(("\r\n" "INFORMATION: Completed PatchGuard scan...\r\n")); /* Loop through the log entries and flush them to disk... In case of an error during enumeration this actually won't write any files, but just free allocated memory... */ LogList = PopEntryList(&LogListHead); while(LogList != NULL) { LogEntry = CONTAINING_RECORD(LogList, LOGENTRY, List); if(NT_SUCCESS(Result)) { Result = ZwWriteFile( hLogFile, NULL, NULL, NULL, &IOStatus, LogEntry->Text, LogEntry->Length, NULL, NULL); } ExFreePool(LogEntry); LogList = PopEntryList(&LogListHead); } ZwClose(hLogFile); return Result; }