NTSTATUS DllInject(HANDLE hProcessID, PEPROCESS pepProcess, PKTHREAD pktThread) { HANDLE hProcess; OBJECT_ATTRIBUTES oaAttributes={sizeof(OBJECT_ATTRIBUTES)}; CLIENT_ID cidProcess; PVOID pvMemory = 0; DWORD dwSize = 0x1000; cidProcess.UniqueProcess = hProcessID; cidProcess.UniqueThread = 0; if (NT_SUCCESS(ZwOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &oaAttributes, &cidProcess))) { if (NT_SUCCESS(ZwAllocateVirtualMemory(hProcess, &pvMemory, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE))) { KAPC_STATE kasState; PKAPC pkaApc; PVOID FunctionAddress; KeStackAttachProcess((PKPROCESS)pepProcess, &kasState); FunctionAddress = GetProcAddressInModule(L"kernel32.dll", "LoadLibraryExW"); if (!FunctionAddress) DbgPrint("GetProcAddressInModule error\n"); wcscpy((PWCHAR)pvMemory, g_wcDllName); KeUnstackDetachProcess(&kasState); pkaApc = (PKAPC)ExAllocatePool(NonPagedPool, sizeof(KAPC)); if (pkaApc) { KeInitializeApc(pkaApc, pktThread, 0, APCKernelRoutine, 0, (PKNORMAL_ROUTINE)FunctionAddress, UserMode, pvMemory); KeInsertQueueApc(pkaApc, 0, 0, IO_NO_INCREMENT); return STATUS_SUCCESS; } } else { DbgPrint("ZwAllocateVirtualMemory error\n"); } ZwClose(hProcess); } else { DbgPrint("ZwOpenProcess error\n"); } return STATUS_NO_MEMORY; }
NTSTATUS BDKitTerminateProcessByAPC (__in PEPROCESS EProcess) { NTSTATUS nsStatus = STATUS_SUCCESS; PETHREAD Thread = NULL; PKAPC ExitApc = NULL; ULONG num; //线程数量 ULONG Head; //链表头 ULONG address;//地址 ULONG i; do { num=*(ULONG *)((ULONG)EProcess+0x1a0); //EPROCESS中ActiveThreads的数量 0x1a0是EPROCESS中ActiveThread的偏移量 Head=(ULONG)EProcess+ 0x190; //List_entry第一个节点地址 for(i=0;i<num;i++) { //记录线程地址 Head=(ULONG)((PLIST_ENTRY)Head)->Flink; address=Head-0x22c; Thread=(PETHREAD)address; //转换成线程指针 ExitApc=(PKAPC)ExAllocatePoolWithTag(NonPagedPool,sizeof(KAPC),'apc'); if(ExitApc==NULL) { return STATUS_UNSUCCESSFUL; } KeInitializeApc(ExitApc, (PKTHREAD)&Thread, //线程 OriginalApcEnvironment, KernelKillThreadRoutine, NULL, NULL, KernelMode, NULL);//为线程初始化APC nsStatus=KeInsertQueueApc(ExitApc,ExitApc,NULL,2); //插入Apc到线程队列 } nsStatus = STATUS_SUCCESS; } while (FALSE); return nsStatus; }
/// <summary> /// Queue user-mode APC to the target thread /// </summary> /// <param name="pThread">Target thread</param> /// <param name="pUserFunc">APC function</param> /// <param name="Arg1">Argument 1</param> /// <param name="Arg2">Argument 2</param> /// <param name="Arg3">Argument 3</param> /// <param name="bForce">If TRUE - force delivery by issuing special kernel APC</param> /// <returns>Status code</returns> NTSTATUS BBQueueUserApc( IN PETHREAD pThread, IN PVOID pUserFunc, IN PVOID Arg1, IN PVOID Arg2, IN PVOID Arg3, IN BOOLEAN bForce ) { ASSERT( pThread != NULL ); if (pThread == NULL) return STATUS_INVALID_PARAMETER; // Allocate APC PKAPC pPrepareApc = NULL; PKAPC pInjectApc = ExAllocatePoolWithTag( NonPagedPool, sizeof( KAPC ), BB_POOL_TAG ); if (pInjectApc == NULL) { DPRINT( "BlackBone: %s: Failed to allocate APC\n", __FUNCTION__ ); return STATUS_NO_MEMORY; } // Actual APC KeInitializeApc( pInjectApc, (PKTHREAD)pThread, OriginalApcEnvironment, &KernelApcInjectCallback, NULL, (PKNORMAL_ROUTINE)(ULONG_PTR)pUserFunc, UserMode, Arg1 ); // Setup force-delivery APC if (bForce) { pPrepareApc = ExAllocatePoolWithTag( NonPagedPool, sizeof( KAPC ), BB_POOL_TAG ); KeInitializeApc( pPrepareApc, (PKTHREAD)pThread, OriginalApcEnvironment, &KernelApcPrepareCallback, NULL, NULL, KernelMode, NULL ); } // Insert APC if (KeInsertQueueApc( pInjectApc, Arg2, Arg3, 0 )) { if (bForce && pPrepareApc) KeInsertQueueApc( pPrepareApc, NULL, NULL, 0 ); return STATUS_SUCCESS; } else { DPRINT( "BlackBone: %s: Failed to insert APC\n", __FUNCTION__ ); ExFreePoolWithTag( pInjectApc, BB_POOL_TAG ); if (pPrepareApc) ExFreePoolWithTag( pPrepareApc, BB_POOL_TAG ); return STATUS_NOT_CAPABLE; } }
VOID InjectDllIntoProcess(PWCHAR ProcessName) { HANDLE ThreadId; HANDLE ProcessId = GetPidAndTidByName(ProcessName, &ThreadId); if (ProcessId) { PETHREAD Thread; NTSTATUS St = PsLookupThreadByThreadId(ThreadId, &Thread); if (NT_SUCCESS(St)) { PKAPC pkaApc = (PKAPC)ExAllocatePool(NonPagedPool, sizeof(KAPC)); if(pkaApc) { KeInitializeApc(pkaApc, (PKTHREAD)Thread, 0, APCInjectRoutine, 0, 0, KernelMode, 0); KeInsertQueueApc(pkaApc, 0, 0, IO_NO_INCREMENT); } ObDereferenceObject(Thread); } } else { DbgPrint("GetPidAndTidByName error\n"); } }
NTSTATUS AlertDrvSendTheSignal(PETHREAD uThread) { NTSTATUS ntStatus = STATUS_SUCCESS; PKAPC kApc; /* Allocate an KAPC structure from NonPagedPool */ kApc = ExAllocatePool(NonPagedPool, sizeof(KAPC)); if (kApc == NULL) { AlertDrvKdPrint (("ExAllocatePool returned NULL\n")); return !ntStatus; } KeInitializeApc(kApc, (PKTHREAD) uThread, 0, (PKKERNEL_ROUTINE) &KernelApcCallBack, 0, (PKNORMAL_ROUTINE) &UserApcCallBack, KernelMode, NULL); KeInsertQueueApc (kApc, NULL, NULL, 0); return ntStatus; }
/* * @implemented */ VOID NTAPI IoRaiseHardError(IN PIRP Irp, IN PVPB Vpb, IN PDEVICE_OBJECT RealDeviceObject) { PETHREAD Thread = (PETHREAD)&Irp->Tail.Overlay.Thread; PKAPC ErrorApc; /* Don't do anything if hard errors are disabled on the thread */ if (Thread->HardErrorsAreDisabled) { /* Complete the request */ Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_DISK_INCREMENT); return; } /* Setup an APC */ ErrorApc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_APC); KeInitializeApc(ErrorApc, &Thread->Tcb, Irp->ApcEnvironment, NULL, (PKRUNDOWN_ROUTINE)IopFreeApc, (PKNORMAL_ROUTINE)IopRaiseHardError, KernelMode, Irp); /* Queue an APC to deal with the error (see osr documentation) */ KeInsertQueueApc(ErrorApc, Vpb, RealDeviceObject, 0); }
VOID NTAPI ExSwapinWorkerThreads(IN BOOLEAN AllowSwap) { KEVENT Event; PETHREAD CurrentThread = PsGetCurrentThread(), Thread; PEPROCESS Process = PsInitialSystemProcess; KAPC Apc; PAGED_CODE(); /* Initialize an event so we know when we're done */ KeInitializeEvent(&Event, NotificationEvent, FALSE); /* Lock this routine */ ExAcquireFastMutex(&ExpWorkerSwapinMutex); /* New threads cannot swap anymore */ ExpWorkersCanSwap = AllowSwap; /* Loop all threads in the system process */ Thread = PsGetNextProcessThread(Process, NULL); while (Thread) { /* Skip threads with explicit permission to do this */ if (Thread->ExWorkerCanWaitUser) goto Next; /* Check if we reached ourselves */ if (Thread == CurrentThread) { /* Do it inline */ KeSetKernelStackSwapEnable(AllowSwap); } else { /* Queue an APC */ KeInitializeApc(&Apc, &Thread->Tcb, InsertApcEnvironment, ExpSetSwappingKernelApc, NULL, NULL, KernelMode, &AllowSwap); if (KeInsertQueueApc(&Apc, &Event, NULL, 3)) { /* Wait for the APC to run */ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); KeClearEvent(&Event); } } /* Next thread */ Next: Thread = PsGetNextProcessThread(Process, Thread); } /* Release the lock */ ExReleaseFastMutex(&ExpWorkerSwapinMutex); }
void DBKSuspendThread(ULONG ThreadID) { KIRQL OldIrql; struct ThreadData *t_data; KeAcquireSpinLock(&ProcesslistSL,&OldIrql); DbgPrint("Going to suspend this thread\n"); //find the thread in the threadlist //find the threadid in the processlist t_data=GetThreaddata(ThreadID); if (t_data) { DbgPrint("Suspending thread....\n"); if (!t_data->PEThread) { //not yet initialized t_data->PEThread=(PETHREAD)getPEThread(ThreadID); KeInitializeApc(&t_data->SuspendApc, (PKTHREAD)t_data->PEThread, 0, (PKKERNEL_ROUTINE)Ignore, (PKRUNDOWN_ROUTINE)NULL, (PKNORMAL_ROUTINE)SuspendThreadAPCRoutine, KernelMode, t_data); } DbgPrint("x should be %p",t_data); t_data->suspendcount++; if (t_data->suspendcount==1) //not yet suspended so suspend it KeInsertQueueApc(&t_data->SuspendApc, t_data, t_data, 0); } else DbgPrint("Thread not found in the list\n"); KeReleaseSpinLock(&ProcesslistSL,OldIrql); }
BOOLEAN TerminateThread(PETHREAD Thread) { PKAPC Apc = NULL; BOOLEAN success = FALSE; if (!MmIsAddressValid(Thread)) return FALSE; Apc = ExAllocatePool(NonPagedPool, sizeof(KAPC)); KeInitializeApc( Apc, Thread, OriginalApcEnvironment, KernelTerminateThreadRoutine, NULL, NULL, KernelMode, NULL); success = KeInsertQueueApc(Apc, NULL, NULL, 0); return success; }
VOID NTAPI PspExitNormalApc(IN PVOID NormalContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PKAPC Apc = (PKAPC)SystemArgument1; PETHREAD Thread = PsGetCurrentThread(); PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "SystemArgument2: %p \n", SystemArgument2); /* This should never happen */ ASSERT(!(((ULONG_PTR)SystemArgument2) & 1)); /* If we're here, this is not a System Thread, so kill it from User-Mode */ KeInitializeApc(Apc, &Thread->Tcb, OriginalApcEnvironment, PsExitSpecialApc, PspExitApcRundown, PspExitNormalApc, UserMode, NormalContext); /* Now insert the APC with the User-Mode Flag */ if (!(KeInsertQueueApc(Apc, Apc, (PVOID)((ULONG_PTR)SystemArgument2 | 1), 2))) { /* Failed to insert, free the APC */ PspExitApcRundown(Apc); } /* Set the APC Pending flag */ Thread->Tcb.ApcState.UserApcPending = TRUE; }
BOOLEAN InjectDll(PINJECT_INFO InjectInfo) { PEPROCESS Process; PETHREAD Thread; PKINJECT mem; ULONG size; PKAPC_STATE ApcState; PKAPC apc; PVOID buffer; PSYSTEM_PROCESS_INFO pSpi; LARGE_INTEGER delay; buffer=ExAllocatePool(NonPagedPool,1024*1024); // Allocate memory for the system information if(!buffer) { DbgPrint("Error: Unable to allocate memory for the process thread list."); return FALSE; } // Get the process thread list if(!NT_SUCCESS(ZwQuerySystemInformation(5,buffer,1024*1024,NULL))) { DbgPrint("Error: Unable to query process thread list."); ExFreePool(buffer); return FALSE; } pSpi=(PSYSTEM_PROCESS_INFO)buffer; // Find a target thread while(pSpi->NextEntryOffset) { if(pSpi->UniqueProcessId==InjectInfo->ProcessId) { DbgPrint("Target thread found. TID: %d",pSpi->Threads[0].ClientId.UniqueThread); break; } pSpi=(PSYSTEM_PROCESS_INFO)((PUCHAR)pSpi+pSpi->NextEntryOffset); } // Reference the target process if(!NT_SUCCESS(PsLookupProcessByProcessId(InjectInfo->ProcessId,&Process))) { DbgPrint("Error: Unable to reference the target process."); ExFreePool(buffer); return FALSE; } DbgPrint("Process name: %s",PsGetProcessImageFileName(Process)); DbgPrint("EPROCESS address: %#x",Process); // Reference the target thread if(!NT_SUCCESS(PsLookupThreadByThreadId(pSpi->Threads[0].ClientId.UniqueThread,&Thread))) { DbgPrint("Error: Unable to reference the target thread."); ObDereferenceObject(Process); // Dereference the target process ExFreePool(buffer); // Free the allocated memory return FALSE; } DbgPrint("ETHREAD address: %#x",Thread); ExFreePool(buffer); // Free the allocated memory KeAttachProcess(Process); // Attach to target process's address space mem=NULL; size=4096; // Allocate memory in the target process if(!NT_SUCCESS(ZwAllocateVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,0,&size,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE))) { DbgPrint("Error: Unable to allocate memory in the target process."); KeDetachProcess(); // Detach from target process's address space ObDereferenceObject(Process); // Dereference the target process ObDereferenceObject(Thread); // Dereference the target thread return FALSE; } DbgPrint("Memory allocated at %#x",mem); mem->LdrLoadDll=LdrLoadDll; // Write the address of LdrLoadDll to target process wcscpy(mem->Buffer,InjectInfo->DllName); // Write the DLL name to target process RtlInitUnicodeString(&mem->DllName,mem->Buffer); // Initialize the UNICODE_STRING structure ApcState=(PKAPC_STATE)((PUCHAR)Thread+ApcStateOffset); // Calculate the address of the ApcState structure ApcState->UserApcPending=TRUE; // Force the target thread to execute APC memcpy((PKINJECT)(mem+1),InjectDllApc,(ULONG)KernelRoutine-(ULONG)InjectDllApc); // Copy the APC code to target process DbgPrint("APC code address: %#x",(PKINJECT)(mem+1)); apc=(PKAPC)ExAllocatePool(NonPagedPool,sizeof(KAPC)); // Allocate the APC object if(!apc) { DbgPrint("Error: Unable to allocate the APC object."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); // Free the allocated memory KeDetachProcess(); // Detach from target process's address space ObDereferenceObject(Process); // Dereference the target process ObDereferenceObject(Thread); // Dereference the target thread return FALSE; } KeInitializeApc(apc,Thread,OriginalApcEnvironment,KernelRoutine,NULL,(PKNORMAL_ROUTINE)((PKINJECT)mem+1),UserMode,mem); // Initialize the APC DbgPrint("Inserting APC to target thread"); // Insert the APC to the target thread if(!KeInsertQueueApc(apc,NULL,NULL,IO_NO_INCREMENT)) { DbgPrint("Error: Unable to insert APC to target thread."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); // Free the allocated memory KeDetachProcess(); // Detach from target process's address space ObDereferenceObject(Process); // Dereference the target process ObDereferenceObject(Thread); // Dereference the target thread ExFreePool(apc); // Free the APC object return FALSE; } delay.QuadPart=-100*10000; while(!mem->Executed) { KeDelayExecutionThread(KernelMode,FALSE,&delay); // Wait for the injection to complete } if(!mem->DllBase) { DbgPrint("Error: Unable to inject DLL into target process."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } DbgPrint("DLL injected at %#x",mem->DllBase); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); // Free the allocated memory KeDetachProcess(); // Detach from target process's address space ObDereferenceObject(Process); // Dereference the target process ObDereferenceObject(Thread); // Dereference the target thread return TRUE; }
/* * See "Windows Internals" - Chapter 13, Page 49 */ NTSTATUS NTAPI PspTerminateThreadByPointer(IN PETHREAD Thread, IN NTSTATUS ExitStatus, IN BOOLEAN bSelf) { PKAPC Apc; NTSTATUS Status = STATUS_SUCCESS; ULONG Flags; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "Thread: %p ExitStatus: %d\n", Thread, ExitStatus); PSREFTRACE(Thread); /* Check if this is a Critical Thread, and Bugcheck */ if (Thread->BreakOnTermination) { /* Break to debugger */ PspCatchCriticalBreak("Terminating critical thread 0x%p (%s)\n", Thread, Thread->ThreadsProcess->ImageFileName); } /* Check if we are already inside the thread */ if ((bSelf) || (PsGetCurrentThread() == Thread)) { /* This should only happen at passive */ ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); /* Mark it as terminated */ PspSetCrossThreadFlag(Thread, CT_TERMINATED_BIT); /* Directly terminate the thread */ PspExitThread(ExitStatus); } /* This shouldn't be a system thread */ if (Thread->SystemThread) return STATUS_ACCESS_DENIED; /* Allocate the APC */ Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC); if (!Apc) return STATUS_INSUFFICIENT_RESOURCES; /* Set the Terminated Flag */ Flags = Thread->CrossThreadFlags | CT_TERMINATED_BIT; /* Set it, and check if it was already set while we were running */ if (!(InterlockedExchange((PLONG)&Thread->CrossThreadFlags, Flags) & CT_TERMINATED_BIT)) { /* Initialize a Kernel Mode APC to Kill the Thread */ KeInitializeApc(Apc, &Thread->Tcb, OriginalApcEnvironment, PsExitSpecialApc, PspExitApcRundown, PspExitNormalApc, KernelMode, (PVOID)ExitStatus); /* Insert it into the APC Queue */ if (!KeInsertQueueApc(Apc, Apc, NULL, 2)) { /* The APC was already in the queue, fail */ Status = STATUS_UNSUCCESSFUL; } else { /* Forcefully resume the thread and return */ KeForceResumeThread(&Thread->Tcb); return Status; } } /* We failed, free the APC */ ExFreePoolWithTag(Apc, TAG_TERMINATE_APC); /* Return Status */ return Status; }
void DBKSuspendProcess(ULONG ProcessID) { KIRQL OldIrql; struct ThreadData *t_data=NULL; struct ProcessData *tempProcessData=NULL; KeAcquireSpinLock(&ProcesslistSL,&OldIrql); DbgPrint("Going to suspend this process\n"); //find the process in the threadlist tempProcessData=processlist; while (tempProcessData) { if (tempProcessData->ProcessID==(HANDLE)(UINT_PTR)ProcessID) { t_data=tempProcessData->Threads; break; } tempProcessData=tempProcessData->next; } if (!t_data) { DbgPrint("This process was not found\n"); KeReleaseSpinLock(&ProcesslistSL,OldIrql); return; //no process found } while (t_data) { DbgPrint("Suspending thread....\n"); if (!t_data->PEThread) { //not yet initialized t_data->PEThread=(PETHREAD)getPEThread((UINT_PTR)t_data->ThreadID); KeInitializeApc(&t_data->SuspendApc, (PKTHREAD)t_data->PEThread, 0, (PKKERNEL_ROUTINE)Ignore, (PKRUNDOWN_ROUTINE)NULL, (PKNORMAL_ROUTINE)SuspendThreadAPCRoutine, KernelMode, t_data); } DbgPrint("x should be %p",t_data); t_data->suspendcount++; if (t_data->suspendcount==1) //not yet suspended so suspend it KeInsertQueueApc(&t_data->SuspendApc, t_data, t_data, 0); t_data=t_data->next; //next thread } KeReleaseSpinLock(&ProcesslistSL,OldIrql); }
NTSTATUS NtSetTimer ( IN HANDLE TimerHandle, IN PLARGE_INTEGER DueTime, IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL, IN PVOID TimerContext OPTIONAL, IN BOOLEAN WakeTimer, IN LONG Period OPTIONAL, OUT PBOOLEAN PreviousState OPTIONAL ) /*++ Routine Description: This function sets an timer object to a Not-Signaled state and sets the timer to expire at the specified time. Arguments: TimerHandle - Supplies a handle to an timer object. DueTime - Supplies a pointer to absolute of relative time at which the timer is to expire. TimerApcRoutine - Supplies an optional pointer to a function which is to be executed when the timer expires. If this parameter is not specified, then the TimerContext parameter is ignored. TimerContext - Supplies an optional pointer to an arbitrary data structure that will be passed to the function specified by the TimerApcRoutine parameter. This parameter is ignored if the TimerApcRoutine parameter is not specified. WakeTimer - Supplies a boolean value that specifies whether the timer wakes computer operation if sleeping Period - Supplies an optional repetitive period for the timer. PreviousState - Supplies an optional pointer to a variable that will receive the previous state of the timer object. Return Value: TBS --*/ { BOOLEAN AssociatedApc; BOOLEAN Dereference; PETHREAD ExThread; PETIMER ExTimer; LARGE_INTEGER ExpirationTime; KIRQL OldIrql1; KPROCESSOR_MODE PreviousMode; BOOLEAN State; NTSTATUS Status; // // Establish an exception handler, probe the due time and previous state // address if specified, reference the timer object, and set the timer // object. If the probe fails, then return the exception code as the // service status. Otherwise return the status value returned by the // reference object by handle routine. // try { // // Get previous processor mode and probe previous state address // if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { if (ARGUMENT_PRESENT(PreviousState)) { ProbeForWriteBoolean(PreviousState); } ProbeForRead(DueTime, sizeof(LARGE_INTEGER), sizeof(ULONG)); } // // Check argument validity. // if (Period < 0) { return STATUS_INVALID_PARAMETER_6; } // // Capture the expiration time. // ExpirationTime = *DueTime; // // Reference timer object by handle. // Status = ObReferenceObjectByHandle(TimerHandle, TIMER_MODIFY_STATE, ExTimerObjectType, PreviousMode, (PVOID *)&ExTimer, NULL); // // If this WakeTimer flag is set, return the appropiate informational // success status code. // if (NT_SUCCESS(Status) && WakeTimer && !PoWakeTimerSupported()) { Status = STATUS_TIMER_RESUME_IGNORED; } // // If the reference was successful, then cancel the timer object, set // the timer object, dereference time object, and write the previous // state value if specified. If the write of the previous state value // fails, then do not report an error. When the caller attempts to // access the previous state value, an access violation will occur. // if (NT_SUCCESS(Status)) { ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1); if (ExTimer->ApcAssociated) { ExThread = CONTAINING_RECORD(ExTimer->TimerApc.Thread, ETHREAD, Tcb); ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock); RemoveEntryList(&ExTimer->ActiveTimerListEntry); ExTimer->ApcAssociated = FALSE; ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock); KeCancelTimer(&ExTimer->KeTimer); KeRemoveQueueDpc(&ExTimer->TimerDpc); KeRemoveQueueApc(&ExTimer->TimerApc); Dereference = TRUE; } else { KeCancelTimer(&ExTimer->KeTimer); Dereference = FALSE; } // // Read the current state of the timer. // State = KeReadStateTimer(&ExTimer->KeTimer); // // If this is a wake timer ensure it's on the wake timer list // ExTimer->WakeTimer = WakeTimer; ExAcquireSpinLockAtDpcLevel(&ExpWakeTimerListLock); if (WakeTimer) { if (!ExTimer->WakeTimerListEntry.Flink) { InsertTailList(&ExpWakeTimerList, &ExTimer->WakeTimerListEntry); } } else { if (ExTimer->WakeTimerListEntry.Flink) { RemoveEntryList(&ExTimer->WakeTimerListEntry); ExTimer->WakeTimerListEntry.Flink = NULL; } } ExReleaseSpinLockFromDpcLevel(&ExpWakeTimerListLock); // // If an APC routine is specified, then initialize the APC, acquire the // thread's active time list lock, insert the timer in the thread's // active timer list, set the timer with an associated DPC, and set the // associated APC flag TRUE. Otherwise set the timer without an associated // DPC, and set the associated APC flag FALSE. // ExTimer->Period = Period; if (ARGUMENT_PRESENT(TimerApcRoutine)) { ExThread = PsGetCurrentThread(); KeInitializeApc(&ExTimer->TimerApc, &ExThread->Tcb, CurrentApcEnvironment, ExpTimerApcRoutine, (PKRUNDOWN_ROUTINE)NULL, (PKNORMAL_ROUTINE)TimerApcRoutine, PreviousMode, TimerContext); ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock); InsertTailList(&ExThread->ActiveTimerListHead, &ExTimer->ActiveTimerListEntry); ExTimer->ApcAssociated = TRUE; ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock); KeSetTimerEx(&ExTimer->KeTimer, ExpirationTime, Period, &ExTimer->TimerDpc); AssociatedApc = TRUE; } else { KeSetTimerEx(&ExTimer->KeTimer, ExpirationTime, Period, NULL); AssociatedApc = FALSE; } ExReleaseSpinLock(&ExTimer->Lock, OldIrql1); // // Dereference the object as appropriate. // if (Dereference) { ObDereferenceObject((PVOID)ExTimer); } if (AssociatedApc == FALSE) { ObDereferenceObject((PVOID)ExTimer); } if (ARGUMENT_PRESENT(PreviousState)) { try { *PreviousState = State; } except(ExSystemExceptionFilter()) { } } } // // If an exception occurs during the probe of the current state address, // then always handle the exception and return the exception code as the // status value. // } except(ExSystemExceptionFilter()) { return GetExceptionCode(); } // // Return service status. // return Status; }
VOID ExSwapinWorkerThreads ( IN BOOLEAN AllowSwap ) /*++ Routine Description: Sets the kernel stacks of the delayed worker threads to be swappable or pins them into memory. Arguments: AllowSwap - Supplies TRUE if worker kernel stacks should be swappable, FALSE if not. Return Value: None. --*/ { PETHREAD Thread; PETHREAD CurrentThread; PEPROCESS Process; KAPC Apc; KEVENT SwapSetEvent; PAGED_CODE(); CurrentThread = PsGetCurrentThread(); KeInitializeEvent (&SwapSetEvent, NotificationEvent, FALSE); Process = PsInitialSystemProcess; // // Serialize callers. // ExAcquireFastMutex (&ExpWorkerSwapinMutex); // // Stop new threads from swapping. // ExpWorkersCanSwap = AllowSwap; // // Stop existing worker threads from swapping. // for (Thread = PsGetNextProcessThread (Process, NULL); Thread != NULL; Thread = PsGetNextProcessThread (Process, Thread)) { // // Skip threads that are not worker threads or worker threads that // were permanently marked noswap at creation time. // if (Thread->ExWorkerCanWaitUser == 0) { continue; } if (Thread == CurrentThread) { // // No need to use an APC on the current thread. // KeSetKernelStackSwapEnable (AllowSwap); } else { // // Queue an APC to the thread, and wait for it to fire: // KeInitializeApc (&Apc, &Thread->Tcb, InsertApcEnvironment, ExpSetSwappingKernelApc, NULL, NULL, KernelMode, &AllowSwap); if (KeInsertQueueApc (&Apc, &SwapSetEvent, NULL, 3)) { KeWaitForSingleObject (&SwapSetEvent, Executive, KernelMode, FALSE, NULL); KeClearEvent(&SwapSetEvent); } } } ExReleaseFastMutex (&ExpWorkerSwapinMutex); }
NTSTATUS NtGetContextThread( IN HANDLE ThreadHandle, IN OUT PCONTEXT ThreadContext ) /*++ Routine Description: This function returns the usermode context of the specified thread. This function will fail if the specified thread is a system thread. It will return the wrong answer if the thread is a non-system thread that does not execute in user-mode. Arguments: ThreadHandle - Supplies an open handle to the thread object from which to retrieve context information. The handle must allow THREAD_GET_CONTEXT access to the thread. ThreadContext - Supplies the address of a buffer that will receive the context of the specified thread. Return Value: None. --*/ { ULONG Alignment; ULONG ContextFlags; GETSETCONTEXT ContextFrame; ULONG ContextLength; KIRQL Irql; KPROCESSOR_MODE Mode; NTSTATUS Status; PETHREAD Thread; PAGED_CODE(); // // Get previous mode and reference specified thread. // Mode = KeGetPreviousMode(); Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_GET_CONTEXT, PsThreadType, Mode, (PVOID *)&Thread, NULL); // // If the reference was successful, the check if the specified thread // is a system thread. // if (NT_SUCCESS(Status)) { // // If the thread is not a system thread, then attempt to get the // context of the thread. // if (IS_SYSTEM_THREAD(Thread) == FALSE) { // // Attempt to get the context of the specified thread. // try { // // Set the default alignment, capture the context flags, // and set the default size of the context record. // Alignment = CONTEXT_ALIGN; ContextFlags = ProbeAndReadUlong(&ThreadContext->ContextFlags); ContextLength = sizeof(CONTEXT); #if defined(_X86_) // // CONTEXT_EXTENDED_REGISTERS is SET, then we want sizeof(CONTEXT) set above // otherwise (not set) we only want the old part of the context record. // if ((ContextFlags & CONTEXT_EXTENDED_REGISTERS) != CONTEXT_EXTENDED_REGISTERS) { ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters); } #endif #if defined(_MIPS_) // // The following code is included for backward compatibility // with old code that does not understand extended context // records on MIPS systems. // if ((ContextFlags & CONTEXT_EXTENDED_INTEGER) != CONTEXT_EXTENDED_INTEGER) { Alignment = sizeof(ULONG); ContextLength = FIELD_OFFSET(CONTEXT, ContextFlags) + 4; } #endif if (Mode != KernelMode) { ProbeForWrite(ThreadContext, ContextLength, Alignment); } } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } // // If an exception did not occur during the probe of the thread // context, then get the context of the target thread. // if (NT_SUCCESS(Status)) { KeInitializeEvent(&ContextFrame.OperationComplete, NotificationEvent, FALSE); ContextFrame.Context.ContextFlags = ContextFlags; ContextFrame.Mode = Mode; if (Thread == PsGetCurrentThread()) { ContextFrame.Apc.SystemArgument1 = NULL; ContextFrame.Apc.SystemArgument2 = Thread; KeRaiseIrql(APC_LEVEL, &Irql); PspGetSetContextSpecialApc(&ContextFrame.Apc, NULL, NULL, &ContextFrame.Apc.SystemArgument1, &ContextFrame.Apc.SystemArgument2); KeLowerIrql(Irql); // // Move context to specfied context record. If an exception // occurs, then silently handle it and return success. // try { RtlMoveMemory(ThreadContext, &ContextFrame.Context, ContextLength); } except(EXCEPTION_EXECUTE_HANDLER) { } } else { KeInitializeApc(&ContextFrame.Apc, &Thread->Tcb, OriginalApcEnvironment, PspGetSetContextSpecialApc, NULL, NULL, KernelMode, NULL); if (!KeInsertQueueApc(&ContextFrame.Apc, NULL, Thread, 2)) { Status = STATUS_UNSUCCESSFUL; } else { KeWaitForSingleObject(&ContextFrame.OperationComplete, Executive, KernelMode, FALSE, NULL); // // Move context to specfied context record. If an // exception occurs, then silently handle it and // return success. // try { RtlMoveMemory(ThreadContext, &ContextFrame.Context, ContextLength); } except(EXCEPTION_EXECUTE_HANDLER) { } } } } } else {
BOOLEAN InjectDll(PINJECT_INFO InjectInfo) { PEPROCESS Process; PETHREAD Thread; PKINJECT mem; ULONG size; PKAPC_STATE ApcState; PKAPC apc; PVOID buffer; PSYSTEM_PROCESS_INFO pSpi; LARGE_INTEGER delay; buffer=ExAllocatePool(NonPagedPool,1024*1024); if(!buffer) { DbgPrint("Error: Unable to allocate memory for the process thread list."); return FALSE; } //5 SystemProcessInformation, if(!NT_SUCCESS(ZwQuerySystemInformation(5,buffer,1024*1024,NULL))) { DbgPrint("Error: Unable to query process thread list."); ExFreePool(buffer); return FALSE; } pSpi=(PSYSTEM_PROCESS_INFO)buffer; //找到目标进程 while(pSpi->NextEntryOffset) { if(pSpi->UniqueProcessId==InjectInfo->ProcessId) { DbgPrint("Target thread found. TID: %d",pSpi->Threads[0].ClientId.UniqueThread); break; } pSpi=(PSYSTEM_PROCESS_INFO)((PUCHAR)pSpi+pSpi->NextEntryOffset); } // 引用目标进程EProcess, if(!NT_SUCCESS(PsLookupProcessByProcessId(InjectInfo->ProcessId,&Process))) { DbgPrint("Error: Unable to reference the target process."); ExFreePool(buffer); return FALSE; } DbgPrint("Process name: %s",PsGetProcessImageFileName(Process)); DbgPrint("EPROCESS address: %#x",Process); //目标进程主线程 if(!NT_SUCCESS(PsLookupThreadByThreadId(pSpi->Threads[0].ClientId.UniqueThread,&Thread))) { DbgPrint("Error: Unable to reference the target thread."); ObDereferenceObject(Process); ExFreePool(buffer); return FALSE; } DbgPrint("ETHREAD address: %#x",Thread); ExFreePool(buffer); //切入到目标进程 KeAttachProcess(Process); mem=NULL; size=4096; //在目标进程申请内存 if(!NT_SUCCESS(ZwAllocateVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,0,&size,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE))) { DbgPrint("Error: Unable to allocate memory in the target process."); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } DbgPrint("Memory allocated at %#x",mem); mem->LdrLoadDll=LdrLoadDll; wcscpy(mem->Buffer,InjectInfo->DllName); RtlInitUnicodeString(&mem->DllName,mem->Buffer); ApcState=(PKAPC_STATE)((PUCHAR)Thread+ApcStateOffset); ApcState->UserApcPending=TRUE; memcpy((PKINJECT)(mem+1),InjectDllApc,(ULONG)KernelRoutine-(ULONG)InjectDllApc); DbgPrint("APC code address: %#x",(PKINJECT)(mem+1)); //申请apc对象 apc=(PKAPC)ExAllocatePool(NonPagedPool,sizeof(KAPC)); if(!apc) { DbgPrint("Error: Unable to allocate the APC object."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } KeInitializeApc(apc, Thread, //目标进程主线程 OriginalApcEnvironment, //目标apcz状态 KernelRoutine, //内核apc总入口 NULL, //Rundown Rounine=NULL (PKNORMAL_ROUTINE)((PKINJECT)mem+1), //用户空间的总apc UserMode, //插入到用户apc队列 mem); // 自己的apc队列 DbgPrint("Inserting APC to target thread"); // 插入apc队列 if(!KeInsertQueueApc(apc,NULL,NULL,IO_NO_INCREMENT)) { DbgPrint("Error: Unable to insert APC to target thread."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); ExFreePool(apc); return FALSE; } delay.QuadPart=-100*10000; while(!mem->Executed) { KeDelayExecutionThread(KernelMode,FALSE,&delay); //等待apc执行 } if(!mem->DllBase) { DbgPrint("Error: Unable to inject DLL into target process."); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); KeDetachProcess(); ObDereferenceObject(Process); ObDereferenceObject(Thread); return FALSE; } DbgPrint("DLL injected at %#x",mem->DllBase); size=0; ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); ObDereferenceObject(Process); ObDereferenceObject(Thread); return TRUE; }
NTSTATUS RequestApc( HANDLE ProcessId, PVOID ShellcodeBase, PVOID ContextData ) { NTSTATUS Status; PKAPC UserApc = NULL; PKAPC KernelApc = NULL; HANDLE ThreadId = NULL; PKTHREAD ThreadObject = NULL; ThreadId = GetThreadForProcess ( ProcessId ); if ( ! ThreadId ) { DPF(("%s!%s GetThreadForProcess() FAIL\n", __MODULE__, __FUNCTION__ )); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } // get the ETHREAD/KTHREAD for ThreadId Status = PsLookupThreadByThreadId ( ThreadId, &ThreadObject ); if ( ! NT_SUCCESS( Status ) ) { DPF(("%s!%s PsLookupThreadByThreadId(%p) FAIL=%08x\n", __MODULE__, __FUNCTION__, ThreadId, Status )); goto Exit; } // allocate memory for the user mode APC object UserApc = ExAllocatePoolWithTag ( NonPagedPool, sizeof (KAPC), 'auMC' ); if ( ! UserApc ) { DPF(("%s!%s ExAllocatePoolWithTag(UserApc) FAIL=%08x\n", __MODULE__, __FUNCTION__ )); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } // allocate memory for the kernel mode APC KernelApc = ExAllocatePoolWithTag ( NonPagedPool, sizeof (KAPC), 'akMC' ); if ( ! KernelApc ) { DPF(("%s!%s ExAllocatePoolWithTag(KernelApc) FAIL=%08x\n", __MODULE__, __FUNCTION__ )); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } // initialize the user mode APC obect with the // APC routines UserApcKernelRoutine and UserApcNormalRoutine KeInitializeApc ( UserApc, ThreadObject, OriginalApcEnvironment, (PKKERNEL_ROUTINE)UserApcKernelRoutine, (PKRUNDOWN_ROUTINE)NULL, (PKNORMAL_ROUTINE)ShellcodeBase, // user routine UserApcNormalRoutine() UserMode, ContextData ); // queue the user mode APC // note that this APC will not be delivered until the thread is alerted // when the kernel mode APC calls KeTestAlertThread() KeInsertQueueApc( UserApc, NULL, NULL, IO_NO_INCREMENT ); // initialize the kernel mode APC obect with the // APC routines KernelApcKernelRoutine and KernelApcNormalRoutine KeInitializeApc ( KernelApc, ThreadObject, OriginalApcEnvironment, (PKKERNEL_ROUTINE)KernelApcKernelRoutine, (PKRUNDOWN_ROUTINE)NULL, (PKNORMAL_ROUTINE)KernelApcNormalRoutine, KernelMode, NULL ); // queue the kernel mode APC which will alert the target thread KeInsertQueueApc( KernelApc, NULL, NULL, IO_NO_INCREMENT ); Exit : if ( ThreadObject ) { ObDereferenceObject ( ThreadObject ); } if ( ! NT_SUCCESS ( Status ) ) { if ( UserApc ) { ExFreePool ( UserApc ); } if ( KernelApc ) { ExFreePool ( KernelApc ); } } return Status; } // RequestApc()
NTSYSAPI NTSTATUS NTAPI NtQueueApcThread( IN HANDLE ThreadHandle, IN PPS_APC_ROUTINE ApcRoutine, IN PVOID ApcArgument1, IN PVOID ApcArgument2, IN PVOID ApcArgument3 ) /*++ Routine Description: This function is used to queue a user-mode APC to the specified thread. The APC will fire when the specified thread does an alertable wait Arguments: ThreadHandle - Supplies a handle to a thread object. The caller must have THREAD_SET_CONTEXT access to the thread. ApcRoutine - Supplies the address of the APC routine to execute when the APC fires. ApcArgument1 - Supplies the first PVOID passed to the APC ApcArgument2 - Supplies the second PVOID passed to the APC ApcArgument3 - Supplies the third PVOID passed to the APC Return Value: Returns an NT Status code indicating success or failure of the API --*/ { PETHREAD Thread; NTSTATUS st; KPROCESSOR_MODE Mode; KIRQL Irql; PKAPC Apc; PAGED_CODE(); Mode = KeGetPreviousMode(); st = ObReferenceObjectByHandle( ThreadHandle, THREAD_SET_CONTEXT, PsThreadType, Mode, (PVOID *)&Thread, NULL ); if ( NT_SUCCESS(st) ) { st = STATUS_SUCCESS; if ( IS_SYSTEM_THREAD(Thread) ) { st = STATUS_INVALID_HANDLE; } else { Apc = ExAllocatePoolWithQuotaTag( (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE), sizeof(*Apc), 'pasP' ); if ( !Apc ) { st = STATUS_NO_MEMORY; } else { KeInitializeApc( Apc, &Thread->Tcb, OriginalApcEnvironment, PspQueueApcSpecialApc, NULL, (PKNORMAL_ROUTINE)ApcRoutine, UserMode, ApcArgument1 ); if ( !KeInsertQueueApc(Apc,ApcArgument2,ApcArgument3,0) ) { ExFreePool(Apc); st = STATUS_UNSUCCESSFUL; } } } ObDereferenceObject(Thread); } return st; }
/* * @implemented */ NTSTATUS NTAPI PsGetContextThread(IN PETHREAD Thread, IN OUT PCONTEXT ThreadContext, IN KPROCESSOR_MODE PreviousMode) { GET_SET_CTX_CONTEXT GetSetContext; ULONG Size = 0, Flags = 0; NTSTATUS Status; /* Enter SEH */ _SEH2_TRY { /* Set default ength */ Size = sizeof(CONTEXT); /* Read the flags */ Flags = ProbeForReadUlong(&ThreadContext->ContextFlags); #ifdef _M_IX86 /* Check if the caller wanted extended registers */ if ((Flags & CONTEXT_EXTENDED_REGISTERS) != CONTEXT_EXTENDED_REGISTERS) { /* Cut them out of the size */ Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters); } #endif /* Check if we came from user mode */ if (PreviousMode != KernelMode) { /* Probe the context */ ProbeForWrite(ThreadContext, Size, sizeof(ULONG)); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; /* Initialize the wait event */ KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE); /* Set the flags and previous mode */ GetSetContext.Context.ContextFlags = Flags; GetSetContext.Mode = PreviousMode; /* Check if we're running in the same thread */ if (Thread == PsGetCurrentThread()) { /* Setup APC parameters manually */ GetSetContext.Apc.SystemArgument1 = NULL; GetSetContext.Apc.SystemArgument2 = Thread; /* Enter a guarded region to simulate APC_LEVEL */ KeEnterGuardedRegion(); /* Manually call the APC */ PspGetOrSetContextKernelRoutine(&GetSetContext.Apc, NULL, NULL, &GetSetContext.Apc.SystemArgument1, &GetSetContext.Apc.SystemArgument2); /* Leave the guarded region */ KeLeaveGuardedRegion(); /* We are done */ Status = STATUS_SUCCESS; } else { /* Initialize the APC */ KeInitializeApc(&GetSetContext.Apc, &Thread->Tcb, OriginalApcEnvironment, PspGetOrSetContextKernelRoutine, NULL, NULL, KernelMode, NULL); /* Queue it as a Get APC */ if (!KeInsertQueueApc(&GetSetContext.Apc, NULL, Thread, 2)) { /* It was already queued, so fail */ Status = STATUS_UNSUCCESSFUL; } else { /* Wait for the APC to complete */ Status = KeWaitForSingleObject(&GetSetContext.Event, 0, KernelMode, FALSE, NULL); } } _SEH2_TRY { /* Copy the context */ RtlCopyMemory(ThreadContext, &GetSetContext.Context, Size); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Get the exception code */ Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Return status */ return Status; }