APC篇——备用APC队列

博客 分享
0 271
优雅殿下
优雅殿下 2022-01-26 17:54:33
悬赏:0 积分 收藏

APC 篇——备用 APC 队列

APC 篇之备用 APC 队列,详细介绍备用 APC 队列相关的知识。

写在前面

??此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。

??看此教程之前,问几个问题,基础知识储备好了吗?保护模式篇学会了吗?练习做完了吗?没有的话就不要继续了。

?


?? 华丽的分割线 ??


?

练习及参考

本次答案均为参考,可以与我的答案不一致,但必须成功通过。

1?? 自己编写WriteProcessMemory函数(不使用任何DLL,直接调用0环函数)并在代码中使用。

?? 点击查看答案 ??


??话不多说,给个效果图看看,代码见折叠:


?
?? 点击查看代码 ??
```cpp#include "stdafx.h"#include <stdlib.h>#define  _WIN32_WINNT 0x400 //解决 QueueUserAPC 函数未定义#include <windows.h>VOID WINAPI APCProc(ULONG Param){    Sleep(1000);    printf("APC…… 0x%X \n",Param);}DWORD WINAPI ThreadProc(VOID* Param){    for (int i =0 ;i<100;i++)    {        SleepEx(1000,TRUE); //思考为什么?        //Sleep(1000);        printf("Running\n");    }    return 0;}int main(int argc, char* argv[]){    HANDLE hTread=CreateThread(NULL,NULL,ThreadProc,NULL,NULL,NULL);    Sleep(3000);    QueueUserAPC(APCProc,hTread,10);    CloseHandle(hTread);    system("pause");    return 0;}

TerminateThread 分析

??该函数在kernel32.dll当中,我们通过IDA轻松定位到了该函数:

; BOOL __stdcall TerminateThread(HANDLE hThread, DWORD dwExitCode)                public _TerminateThread@8_TerminateThread@8 proc near            ; DATA XREF: .text:off_7C802654↑ohThread         = dword ptr  8dwExitCode      = dword ptr  0Ch; FUNCTION CHUNK AT .text:7C8449A4 SIZE 00000016 BYTES                mov     edi, edi                push    ebp                mov     ebp, esp                cmp     [ebp+hThread], 0                jz      loc_7C8449A4                push    [ebp+dwExitCode] ; ExitStatus                push    [ebp+hThread]   ; ThreadHandle                call    ds:__imp__NtTerminateThread@8 ; NtTerminateThread(x,x)                test    eax, eax                jl      loc_7C8449AD                xor     eax, eax                inc     eaxloc_7C81CB49:                           ; CODE XREF: TerminateThread(x,x)+27E92↓j                pop     ebp                retn    8_TerminateThread@8 endp

??发现该函数会调用NtTerminateThread,这个函数来自ntdll.dll当中,如下所示:

; Exported entry 349. NtTerminateThread; Exported entry 1158. ZwTerminateThread; =============== S U B R O U T I N E =======================================; __stdcall ZwTerminateThread(x, x)                public _ZwTerminateThread@8_ZwTerminateThread@8 proc near          ; CODE XREF: LdrpGenericExceptionFilter(x,x)+90FD↓p                                        ; RtlQueryProcessDebugInformation(x,x,x)+10B↓p ...                mov     eax, 102h       ; NtTerminateThread                mov     edx, 7FFE0300h                call    dword ptr [edx]                retn    8_ZwTerminateThread@8 endp

??可以看出,这个开始进入系统内核了,我们根据PCHunter很容易定义到内核函数为NtTerminateThread,如下所示:

; NTSTATUS __stdcall NtTerminateThread(HANDLE ThreadHandle, NTSTATUS ExitStatus)_NtTerminateThread@8 proc near          ; DATA XREF: .text:0042AF94↑oAccessMode      = byte ptr -4ThreadHandle    = dword ptr  8ExitStatus      = dword ptr  0Ch                mov     edi, edi                push    ebp                mov     ebp, esp                push    ecx                push    ebx                push    esi                push    edi                xor     edi, edi                mov     eax, large fs:_KPCR.PrcbData.CurrentThread                cmp     [ebp+ThreadHandle], edi                mov     esi, eax                jnz     short loc_4F1E4F                mov     eax, [esi+_ETHREAD.Tcb.ApcState.Process]                cmp     [eax+_EPROCESS.ActiveThreads], 1                jnz     short loc_4F1E8B                mov     eax, 0C00000DBh                jmp     short loc_4F1EAA; ---------------------------------------------------------------------------loc_4F1E4F:                             ; CODE XREF: NtTerminateThread(x,x)+16↑j                cmp     [ebp+ThreadHandle], 0FFFFFFFEh                jz      short loc_4F1E8B                mov     al, [esi+_ETHREAD.Tcb.PreviousMode]                push    0               ; HandleInformation                mov     [ebp+AccessMode], al                lea     eax, [ebp+ThreadHandle]                push    eax             ; Object                push    dword ptr [ebp+AccessMode] ; AccessMode                push    _PsThreadType   ; ObjectType                push    1               ; DesiredAccess                push    [ebp+ThreadHandle] ; Handle                call    _ObReferenceObjectByHandle@24 ; ObReferenceObjectByHandle(x,x,x,x,x,x)                mov     edi, eax                test    edi, edi                jl      short loc_4F1EA8                mov     ebx, [ebp+ThreadHandle]                cmp     ebx, esi                jnz     short loc_4F1E96                mov     ecx, ebx        ; Object                call    @ObfDereferenceObject@4 ; ObfDereferenceObject(x)loc_4F1E8B:                             ; CODE XREF: NtTerminateThread(x,x)+22↑j                                        ; NtTerminateThread(x,x)+2F↑j                push    [ebp+ExitStatus] ; int                push    esi             ; Response                call    _PspTerminateThreadByPointer@8 ; PspTerminateThreadByPointer(x,x)                jmp     short loc_4F1EA8; ---------------------------------------------------------------------------loc_4F1E96:                             ; CODE XREF: NtTerminateThread(x,x)+5E↑j                push    [ebp+ExitStatus] ; int                push    ebx             ; Response                call    _PspTerminateThreadByPointer@8 ; PspTerminateThreadByPointer(x,x)                mov     ecx, ebx        ; Object                mov     edi, eax                call    @ObfDereferenceObject@4 ; ObfDereferenceObject(x)loc_4F1EA8:                             ; CODE XREF: NtTerminateThread(x,x)+57↑j                                        ; NtTerminateThread(x,x)+70↑j                mov     eax, ediloc_4F1EAA:                             ; CODE XREF: NtTerminateThread(x,x)+29↑j                pop     edi                pop     esi                pop     ebx                leave                retn    8_NtTerminateThread@8 endp

??我们要求是分析该函数是怎样终止别的线程的,故里面的细节不要仔细分析,我们看到该函数调用了关键函数PspTerminateThreadByPointer

?? 点击查看反汇编 ??
; int __stdcall PspTerminateThreadByPointer(PETHREAD Thread, int a2)_PspTerminateThreadByPointer@8 proc near                                        ; CODE XREF: PspUserThreadStartup(x,x)+A9↑p                                        ; PspSystemThreadStartup(x,x)+3B↑p ...Interval        = _LARGE_INTEGER ptr -0Chres             = dword ptr -4Thread          = dword ptr  8arg_4           = dword ptr  0Ch                mov     edi, edi                push    ebp                mov     ebp, esp                sub     esp, 0Ch                or      dword ptr [ebp+Interval+4], 0FFFFFFFFh                push    esi                push    edi                mov     edi, [ebp+Thread]                lea     esi, [edi+248h]                test    byte ptr [esi], 40h                mov     dword ptr [ebp+Interval], 0FFF0BDC0h                jz      short loc_4F1B3C                mov     eax, [edi+220h]                add     eax, 174h                push    eax             ; BugCheckParameter3                push    edi             ; Response                push    offset aTerminatingCri ; "Terminating critical thread 0x%p (in %s"...                call    _PspCatchCriticalBreak@12 ; PspCatchCriticalBreak(x,x,x)loc_4F1B3C:                             ; CODE XREF: PspTerminateThreadByPointer(x,x)+21↑j                mov     eax, large fs:_KPCR.PrcbData.CurrentThread                cmp     edi, eax                jnz     short loc_4F1B54                xor     eax, eax                inc     eax                lock or [esi], eax                push    [ebp+arg_4]                call    _PspExitThread@4 ; PspExitThread(x)loc_4F1B54:                             ; CODE XREF: PspTerminateThreadByPointer(x,x)+42↑j                test    byte ptr [esi], 10h                jz      short loc_4F1B63                mov     eax, 0C0000022h                jmp     loc_4F1BF6; ---------------------------------------------------------------------------loc_4F1B63:                             ; CODE XREF: PspTerminateThreadByPointer(x,x)+55↑j                push    ebx                xor     ebx, ebx                mov     [ebp+res], ebx                mov     esi, 'xEsP'                jmp     short loc_4F1B7B; ---------------------------------------------------------------------------loc_4F1B70:                             ; CODE XREF: PspTerminateThreadByPointer(x,x)+86↓j                lea     eax, [ebp+Interval]                push    eax             ; Interval                push    ebx             ; Alertable                push    ebx             ; WaitMode                call    _KeDelayExecutionThread@12 ; KeDelayExecutionThread(x,x,x)loc_4F1B7B:                             ; CODE XREF: PspTerminateThreadByPointer(x,x)+6C↑j                push    esi             ; Tag                push    30h ; '0'       ; NumberOfBytes                push    ebx             ; PoolType                call    _ExAllocatePoolWithTag@12 ; ExAllocatePoolWithTag(x,x,x)                mov     edi, eax                cmp     edi, ebx                jz      short loc_4F1B70                mov     ecx, [ebp+Thread]                xor     edx, edx                inc     edx                add     ecx, 248h                mov     eax, [ecx]loc_4F1B98:                             ; CODE XREF: PspTerminateThreadByPointer(x,x)+9E↓j                mov     esi, eax                or      esi, edx                lock cmpxchg [ecx], esi                jnz     short loc_4F1B98                test    dl, al                jnz     short loc_4F1BEB                push    [ebp+arg_4]                push    ebx                push    offset _PspExitNormalApc@12 ; PspExitNormalApc(x,x,x)                push    offset _ExFreeCallBack@4 ; ExFreeCallBack(x)                push    offset _PsExitSpecialApc@20 ; PsExitSpecialApc(x,x,x,x,x)                push    ebx                push    [ebp+Thread]                push    edi                call    _KeInitializeApc@32 ; KeInitializeApc(x,x,x,x,x,x,x,x)                push    2                push    ebx                push    edi                push    edi                call    _KeInsertQueueApc@16 ; KeInsertQueueApc(x,x,x,x)                test    al, al                jnz     short loc_4F1BE1                push    ebx             ; Tag                push    edi             ; P                call    _ExFreePoolWithTag@8 ; ExFreePoolWithTag(x,x)                mov     [ebp+res], 0C0000001h                jmp     short loc_4F1BF2; ---------------------------------------------------------------------------loc_4F1BE1:                             ; CODE XREF: PspTerminateThreadByPointer(x,x)+CD↑j                push    [ebp+Thread]                call    _KeForceResumeThread@4 ; KeForceResumeThread(x)                jmp     short loc_4F1BF2; ---------------------------------------------------------------------------loc_4F1BEB:                             ; CODE XREF: PspTerminateThreadByPointer(x,x)+A2↑j                push    ebx             ; Tag                push    edi             ; P                call    _ExFreePoolWithTag@8 ; ExFreePoolWithTag(x,x)loc_4F1BF2:                             ; CODE XREF: PspTerminateThreadByPointer(x,x)+DD↑j                                        ; PspTerminateThreadByPointer(x,x)+E7↑j                mov     eax, [ebp+res]                pop     ebxloc_4F1BF6:                             ; CODE XREF: PspTerminateThreadByPointer(x,x)+5C↑j                pop     edi                pop     esi                leave                retn    8_PspTerminateThreadByPointer@8 endp

??为了方便阅读,用F5翻译为伪代码,如下所示:

int __stdcall PspTerminateThreadByPointer(PETHREAD Thread, int a2){  volatile signed __int32 *v2; // esi  bool v3; // zf  PVOID buffer; // edi  union _LARGE_INTEGER Interval; // [esp+8h] [ebp-Ch] BYREF  int res; // [esp+10h] [ebp-4h]  v2 = &Thread[1].WaitBlock[1];  v3 = (Thread[1].WaitBlock[1].WaitListEntry.Flink & 0x40) == 0;  Interval.QuadPart = -1000000i64;  if ( !v3 )    PspCatchCriticalBreak(      "Terminating critical thread 0x%p (in %s)\n",      Thread,      &Thread[1].WaitListEntry.Flink[46].Blink);  if ( Thread == KeGetCurrentThread() )  {    _InterlockedOr(v2, 1u);    PspExitThread(a2);  }  if ( (*v2 & 0x10) != 0 )    return 0xC0000022;  res = 0;  while ( 1 )  {    buffer = ExAllocatePoolWithTag(NonPagedPool, 0x30u, 'xEsP');    if ( buffer )      break;    KeDelayExecutionThread(0, 0, &Interval);  }  if ( (_InterlockedOr(&Thread[1].WaitBlock[1], 1u) & 1) != 0 )  {    ExFreePoolWithTag(buffer, 0);  }  else  {    KeInitializeApc(buffer, Thread, 0, PsExitSpecialApc, ExFreeCallBack, PspExitNormalApc, 0, a2);    if ( KeInsertQueueApc(buffer, buffer, 0, 2) )    {      KeForceResumeThread(Thread);    }    else    {      ExFreePoolWithTag(buffer, 0);      res = 0xC0000001;    }  }  return res;}

??根据伪代码,我们可以看出如果发现结束的线程是自身线程,就会调用PspExitThread结束自己,如果不是自己,就会使用KeInitializeApc初始化APC,然后调用KeInsertQueueApc插入APC。如下是泄露的NT3.5内核代码,与之对比:

?? 点击查看代码 ??
NTSTATUSPspTerminateThreadByPointer(    IN PETHREAD Thread,    IN NTSTATUS ExitStatus    )/*++Routine Description:    This function causes the specified thread to terminate.Arguments:    ThreadHandle - Supplies a referenced pointer to the thread to terminate.    ExitStatus - Supplies the exit status associated with the thread.Return Value:    TBD--*/{    PKAPC ExitApc;    PAGED_CODE();    if ( Thread == PsGetCurrentThread() ) {        ObDereferenceObject(Thread);        PspExitThread(ExitStatus);        //        // Never Returns        //    } else {        ExitApc = ExAllocatePool(NonPagedPool,(ULONG)sizeof(KAPC));        if ( !ExitApc ) {            return STATUS_INSUFFICIENT_RESOURCES;        }        KeInitializeApc(            ExitApc,            &Thread->Tcb,            OriginalApcEnvironment,            PsExitSpecialApc,            NULL,            PspExitNormalApc,            KernelMode,            (PVOID) ExitStatus            );        if ( !KeInsertQueueApc(ExitApc,ExitApc,NULL, 2) ) {            ExFreePool(ExitApc);            return STATUS_UNSUCCESSFUL;        }    }    return STATUS_SUCCESS;}

??可以看出,TerminateThread这个函数是通过APC实现控制其他线程,实现终止线程。

SuspendThread 分析

??该函数和TerminateThread函数调用流程是一样的,我们直接从内核层函数调用开始,三环的自行分析就行了。如下是内核函数:

; NTSTATUS __stdcall NtSuspendThread(HANDLE ThreadHandle, PULONG PreviousSuspendCount)_NtSuspendThread@8 proc near            ; DATA XREF: .text:0042AF84↑ovar_30          = dword ptr -30hvar_2C          = dword ptr -2Chvar_28          = dword ptr -28hvar_24          = dword ptr -24hAccessMode      = byte ptr -20hObject          = dword ptr -1Chms_exc          = CPPEH_RECORD ptr -18hThreadHandle    = dword ptr  8PreviousSuspendCount= dword ptr  0Ch; __unwind { // __SEH_prolog                push    20h                push    offset stru_402FE8                call    __SEH_prolog                xor     ebx, ebx                mov     [ebp+ms_exc.registration.TryLevel], ebx                mov     eax, large fs:124h                mov     [ebp+var_30], eax                mov     al, [eax+140h]                mov     [ebp+AccessMode], al                mov     esi, [ebp+PreviousSuspendCount]                cmp     al, bl                jz      short loc_4F3B99                cmp     esi, ebx                jz      short loc_4F3B99                mov     eax, _MmUserProbeAddress                cmp     esi, eax                jb      short loc_4F3B95                mov     [eax], ebxloc_4F3B95:                             ; CODE XREF: NtSuspendThread(x,x)+35↑j                mov     eax, [esi]                mov     [esi], eaxloc_4F3B99:                             ; CODE XREF: NtSuspendThread(x,x)+28↑j                                        ; NtSuspendThread(x,x)+2C↑j                or      [ebp+ms_exc.registration.TryLevel], 0FFFFFFFFh                push    ebx             ; HandleInformation                lea     eax, [ebp+Object]                push    eax             ; Object                push    dword ptr [ebp+AccessMode] ; AccessMode                push    _PsThreadType   ; ObjectType                push    2               ; DesiredAccess                push    [ebp+ThreadHandle] ; Handle                call    _ObReferenceObjectByHandle@24 ; ObReferenceObjectByHandle(x,x,x,x,x,x)                cmp     eax, ebx                jl      short loc_4F3C15                lea     eax, [ebp+var_24]                push    eax                push    [ebp+Object]                call    _PsSuspendThread@8 ; PsSuspendThread(x,x)                mov     edi, eax                mov     ecx, [ebp+Object] ; Object                call    @ObfDereferenceObject@4 ; ObfDereferenceObject(x)                mov     [ebp+ms_exc.registration.TryLevel], 1                cmp     esi, ebx                jz      short loc_4F3BF5                mov     eax, [ebp+var_24]                mov     [esi], eax                jmp     short loc_4F3BF5; ---------------------------------------------------------------------------loc_4F3BE1:                             ; DATA XREF: .text:stru_402FE8↑o                mov     eax, [ebp+ms_exc.exc_ptr]                mov     eax, [eax]                mov     eax, [eax]                mov     [ebp+var_28], eax                xor     eax, eax                inc     eax                retn; ---------------------------------------------------------------------------loc_4F3BEF:                             ; DATA XREF: .text:stru_402FE8↑o                mov     esp, [ebp+ms_exc.old_esp]                mov     edi, [ebp+var_28]loc_4F3BF5:                             ; CODE XREF: NtSuspendThread(x,x)+7C↑j                                        ; NtSuspendThread(x,x)+83↑j                or      [ebp+ms_exc.registration.TryLevel], 0FFFFFFFFh                mov     eax, edi                jmp     short loc_4F3C15; ---------------------------------------------------------------------------loc_4F3BFD:                             ; DATA XREF: .text:stru_402FE8↑o                mov     eax, [ebp+ms_exc.exc_ptr]                mov     eax, [eax]                mov     eax, [eax]                mov     [ebp+var_2C], eax                xor     eax, eax                inc     eax                retn; ---------------------------------------------------------------------------loc_4F3C0B:                             ; DATA XREF: .text:stru_402FE8↑o                mov     esp, [ebp+ms_exc.old_esp]                or      [ebp+ms_exc.registration.TryLevel], 0FFFFFFFFh                mov     eax, [ebp+var_2C]loc_4F3C15:                             ; CODE XREF: NtSuspendThread(x,x)+5B↑j                                        ; NtSuspendThread(x,x)+9F↑j                call    __SEH_epilog                retn    8; } // starts at 4F3B5C_NtSuspendThread@8 endp

??我们注意到,该函数调用了PsSuspendThread函数来实现功能,点击去看看,为了方便阅读,我直接放上它的伪代码:

unsigned int __stdcall PsSuspendThread(PETHREAD Thread, _DWORD *PreviousSuspendCount){  unsigned int v2; // esi  int v4; // [esp+18h] [ebp-1Ch]  v2 = 0;  v4 = 0;  if ( Thread == KeGetCurrentThread() )  {    v4 = KeSuspendThread(Thread);    goto LABEL_10;  }  if ( ExAcquireRundownProtection(&Thread[1].WaitBlock[0].WaitListEntry.Blink) )  {    if ( (Thread[1].WaitBlock[1].WaitListEntry.Flink & 1) == 0 )    {      v4 = KeSuspendThread(Thread);      if ( (Thread[1].WaitBlock[1].WaitListEntry.Flink & 1) == 0 )      {LABEL_8:        ExReleaseRundownProtection(&Thread[1].WaitBlock[0].WaitListEntry.Blink);        goto LABEL_10;      }      KeForceResumeThread(Thread);      v4 = 0;    }    v2 = 0xC000004B;    goto LABEL_8;  }  v2 = 0xC000004B;LABEL_10:  if ( PreviousSuspendCount )    *PreviousSuspendCount = v4;  return v2;}

??而这个函数又调用KeSuspendThread函数进行实现,我们直接看看其伪代码:

int __stdcall KeSuspendThread(PETHREAD Thread){  int res; // edi  struct _KLOCK_QUEUE_HANDLE LockHandle; // [esp+Ch] [ebp-Ch] BYREF  KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);  res = Thread->SuspendCount;  if ( res == 0x7F )  {    KeReleaseInStackQueuedSpinLock(&LockHandle);    ExRaiseStatus(0xC000004A);  }  if ( Thread->ApcQueueable == 1 )  {    ++Thread->SuspendCount;    if ( !res && !Thread->FreezeCount && !KiInsertQueueApc(&Thread->SuspendApc, 0) )      --Thread->SuspendSemaphore.Header.SignalState;  }  KeReleaseInStackQueuedSpinLock(&LockHandle);  return res;}

??我们可以看出,SuspendThread这个函数也是通过APC实现控制其他线程,实现挂起线程,如下同是NT3.5泄露代码与之对比:

?? 点击查看代码 ??
ULONGKeSuspendThread (    IN PKTHREAD Thread    )/*++Routine Description:    This function suspends the execution of a thread. If the suspend count    overflows the maximum suspend count, then a condition is raised.Arguments:    Thread  - Supplies a pointer to a dispatcher object of type thread.Return Value:    The previous suspend count.--*/{    ULONG OldCount;    KIRQL OldIrql;    ASSERT_THREAD(Thread);    //    // Raise IRQL to dispatcher level and lock dispatcher database.    //    KiLockDispatcherDatabase(&OldIrql);    //    // Capture the current suspend count.    //    OldCount = Thread->SuspendCount;    //    // If the suspend count is at its maximum value, then unlock dispatcher    // database, lower IRQL to its previous value, and raise an error    // condition.    //    if (OldCount == MAXIMUM_SUSPEND_COUNT) {        //        // Unlock the dispatcher database and raise an exception.        //        KiUnlockDispatcherDatabase(OldIrql);        ExRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED);    }    //    // Increment the suspend count. If the thread was not previously suspended,    // then queue the thread's suspend APC.    //    Thread->SuspendCount += 1;    if ((OldCount == 0) && (Thread->FreezeCount == 0)) {        if (KiInsertQueueApc(&Thread->SuspendApc, RESUME_INCREMENT) == FALSE) {            Thread->SuspendSemaphore.Header.SignalState -= 1;        }    }    //    // Unlock dispatcher database and lower IRQL to its previous    // value.    //    KiUnlockDispatcherDatabase(OldIrql);    //    // Return the previous suspend count.    //    return OldCount;}

备用 APC 队列

??在上一篇中我们讲过,如果想让线程做什么事情,就给它的APC队列里面挂一个APC。在介绍备用APC队列之前,我们先看看下面的结构体:

kd> dt _KTHREADnt!_KTHREAD    ...    +0x034 ApcState         : _KAPC_STATE    ...    +0x138 ApcStatePointer  : [2] Ptr32 _KAPC_STATE    ...    +0x14c SavedApcState    : _KAPC_STATE    ...    +0x165 ApcStateIndex    : UChar    +0x166 ApcQueueable     : UChar    ...

??上面展示的就是KTHREAD结构体中与APC相关的内容。下面我们来详细介绍它们的用途。

ApcState 与 SavedApcState

??它们存储的是一个结构体,我们上篇简单介绍过,再拿来看看:

kd> dt _KAPC_STATEntdll!_KAPC_STATE   +0x000 ApcListHead      : [2] _LIST_ENTRY   +0x010 Process          : Ptr32 _KPROCESS   +0x014 KernelApcInProgress : UChar   +0x015 KernelApcPending : UChar   +0x016 UserApcPending   : UChar

??这个结构体存储了它的“养父”是谁以及APC相关的信息。有了ApcState了,为啥还要有SavedApcState?我们可以考虑一个事情,线程APC队列中的APC函数都是与进程相关联的,具体点说:A进程的T线程中的所有APC函数,要访问的内存地址都是A进程的。但线程是可以挂靠到其他的进程:比如A进程的线程T,通过修改Cr3(改为B进程的页目录基址),就可以访问B进程地址空间,即所谓“进程挂靠”。
??当T线程挂靠B进程后,APC队列中存储的却仍然是原来的APC。具体点说,比如某个APC函数要读取一个地址为0x12345678的数据,如果此时进行读取,读到的将是B进程的地址空间,这样逻辑就错误了。为了避免混乱,在T线程挂靠B进程时,会将ApcState中的值暂时存储到SavedApcState中,等回到原进程A时,再将APC队列恢复,这就是所谓的备用APC队列。
??当线程是否处于挂靠环境下,ApcState的意义是不一样的。我们设个条件:A进程的T线程挂靠B进程,A是T的所属进程,B是T的挂靠进程。那么如果处于挂靠环境,ApcState存储是就是B进程相关的APC函数,而SavedApcState存储的是A进程相关的APC函数。在正常情况下,当前进程就是所属进程A,如果是挂靠情况下,当前进程就是挂靠进程B。

ApcStatePointer

??为了操作方便,_KTHREAD结构体中定义了一个指针数组ApcStatePointer ,一共两个成员。不同情况下的存储的值我们用下面的表格进行展示:

情况ApcStatePointer[0]ApcStatePointer[1]
正常情况ApcStateSavedApcState
挂靠情况SavedApcStateApcState

ApcStateIndex

??用来标识当前线程处于什么状态。如果值为0则为正常状态;如果值为1则为挂靠状态。

ApcQueueable

??用于表示是否可以向线程的APC队列中插入APC。一个线程不可能每时每刻都能被插入APC的。比如当线程正在执行退出的代码时,会将这个值设置为0 ,如果此时执行插入APC的代码(KiInsertQueueApc函数),在插入函数中会判断这个值的状态,如果为0,则插入失败。当线程为系统内核线程,没有3环的部分,如果插入3环的APC,这也是不行的。

ApcStatePointer 与 ApcStateIndex 组合寻址

??正常情况下,向ApcState队列中插入APC时,ApcStatePointer[0]指向ApcState,此时ApcStateIndex的值为0,ApcStatePointer[ApcStateIndex]指向ApcState
??挂靠情况下,向ApcState队列中插入APC时,ApcStatePointer[1]指向ApcState,此时ApcStateIndex的值为1,ApcStatePointer[ApcStateIndex]指向ApcState
??于是我们可以下一个结论:无论什么环境下,ApcStatePointer[ApcStateIndex]指向的都是ApcState,也就是该值总是表示线程当前使用的APC状态。

本节练习

本节的答案将会在下一节进行讲解,务必把本节练习做完后看下一个讲解内容。不要偷懒,实验是学习本教程的捷径。

??俗话说得好,光说不练假把式,如下是本节相关的练习。如果练习没做好,就不要看下一节教程了,越到后面,不做练习的话容易夹生了,开始还明白,后来就真的一点都不明白了。本节练习不多,请保质保量的完成,本篇参考将会在正文给出。

1?? 分析NtReadVirtualMemory在挂靠时如何备份和恢复APC队列的。

下一篇

??APC 篇—— APC 挂入

知识共享许可协议
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
本文来自博客园,作者:寂静的羽夏 ,一个热爱计算机技术的菜鸟
转载请注明原文链接:https://www.cnblogs.com/wingsummer/p/15847498.html
posted @ 2022-01-26 17:32 寂静的羽夏 阅读(9) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员