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 NTAPI ExpTimerDpcRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PETIMER Timer = DeferredContext; BOOLEAN Inserted = FALSE; /* Reference the timer */ if (!ObReferenceObjectSafe(Timer)) return; /* Lock the Timer */ KeAcquireSpinLockAtDpcLevel(&Timer->Lock); /* Check if the timer is associated */ if (Timer->ApcAssociated) { /* Queue the APC */ Inserted = KeInsertQueueApc(&Timer->TimerApc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT); } /* Release the Timer */ KeReleaseSpinLockFromDpcLevel(&Timer->Lock); /* Dereference it if we couldn't queue the APC */ if (!Inserted) ObDereferenceObject(Timer); }
/* * @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); }
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; }
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"); } }
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; }
/* * 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 ExpTimerDpcRoutine ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: This function is the DPC routine that is called when a timer expires that has an associated APC routine. Its function is to insert the associated APC into the target thread's APC queue. Arguments: Dpc - Supplies a pointer to a control object of type DPC. DeferredContext - Supplies a pointer to the executive timer that contains the DPC that caused this routine to be executed. SystemArgument1, SystemArgument2 - Supplies values that are not used by this routine. Return Value: None. --*/ { PETIMER ExTimer; PKTIMER KeTimer; KIRQL OldIrql; // // Get address of executive and kernel timer objects. // ExTimer = (PETIMER)DeferredContext; KeTimer = &ExTimer->KeTimer; // // If there is still an APC associated with the timer, then insert the APC // in target thread's APC queue. It is possible that the timer does not // have an associated APC. This can happen when the timer is set to expire // by a thread running on another processor just after the DPC has been // removed from the DPC queue, but before it has acquired the timer related // spin lock. // ExAcquireSpinLock(&ExTimer->Lock, &OldIrql); if (ExTimer->ApcAssociated) { KeInsertQueueApc(&ExTimer->TimerApc, SystemArgument1, SystemArgument2, TIMER_APC_INCREMENT); } ExReleaseSpinLock(&ExTimer->Lock, OldIrql); return; }
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); }
/* * @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; }
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()
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); }
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; }
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; }