初始化APC

一、初始化 APC:KeInitializeApc 函数到底做了什么

在理解 APC 的执行、投递之前,必须先搞清楚一个最基础的问题:

KeInitializeApc 初始化的,究竟是什么?

二、KeInitializeApc 的函数原型

bash 复制代码
VOID
KeInitializeApc(
    PKAPC               Apc,
    PKTHREAD            Thread,
    KAPC_ENVIRONMENT    Environment,
    PKKERNEL_ROUTINE    KernelRoutine,
    PKRUNDOWN_ROUTINE   RundownRoutine,
    PKNORMAL_ROUTINE    NormalRoutine,
    KPROCESSOR_MODE     ApcMode,
    PVOID               NormalContext
);

从参数就能看出,这个函数完全不涉及"什么时候执行",

它只描述三件事:

  1. APC 属于谁(Thread / Environment)

  2. APC 要执行什么(Routine)

  3. 执行时需要什么上下文(Mode / Context)

三、分析KeInitializeApc函数的反汇编

bash 复制代码
.text:004B0DF3
.text:004B0DF3 ; int __stdcall KeInitializeApc(int Apc, int Thread, int Environment, int KernelRoutine, int RundownRoutine, int NormalRoutine, char ApcMode, int NormalContext)
.text:004B0DF3                 public _KeInitializeApc@32
.text:004B0DF3 _KeInitializeApc@32 proc near           ; CODE XREF: IopfCompleteRequest(x,x)+2B2↑p
.text:004B0DF3                                         ; IopfCompleteRequest(x,x)+3DD↑p ...
.text:004B0DF3
.text:004B0DF3 Apc             = dword ptr  8
.text:004B0DF3 Thread          = dword ptr  0Ch
.text:004B0DF3 Environment     = dword ptr  10h
.text:004B0DF3 KernelRoutine   = dword ptr  14h
.text:004B0DF3 RundownRoutine  = dword ptr  18h
.text:004B0DF3 NormalRoutine   = dword ptr  1Ch
.text:004B0DF3 ApcMode         = byte ptr  20h
.text:004B0DF3 NormalContext   = dword ptr  24h
.text:004B0DF3
.text:004B0DF3                 mov     edi, edi
.text:004B0DF5                 push    ebp
.text:004B0DF6                 mov     ebp, esp
.text:004B0DF8                 mov     eax, [ebp+Apc]
.text:004B0DFB                 mov     edx, [ebp+Environment]
.text:004B0DFE                 mov     ecx, [ebp+Thread]
.text:004B0E01                 mov     [eax+_KAPC.Type], 12h
.text:004B0E04                 mov     [eax+_KAPC.Size], 30h ; '0'
.text:004B0E08                 cmp     edx, 2
.text:004B0E0B                 jnz     short loc_4B0E13 ; 如果Environment!=2,跳转
.text:004B0E0D                 mov     dl, [ecx+_KTHREAD.ApcStateIndex] ; 如果Environment=2(CurrentApcEnvironment)
.text:004B0E0D                                         ; dl=_KTHREAD.ApcStateIndex
.text:004B0E13
.text:004B0E13 loc_4B0E13:                             ; CODE XREF: KeInitializeApc(x,x,x,x,x,x,x,x)+18↑j
.text:004B0E13                 mov     [eax+_KAPC.Thread], ecx ; _KAPC.Thread=Thread
.text:004B0E16                 mov     ecx, [ebp+KernelRoutine]
.text:004B0E19                 mov     [eax+_KAPC.KernelRoutine], ecx ; _KAPC.KernelRoutine=KernelRoutine
.text:004B0E1C                 mov     ecx, [ebp+RundownRoutine]
.text:004B0E1F                 mov     [eax+_KAPC.ApcStateIndex], dl ; _KAPC.ApcStateIndex=_KTHREAD.ApcStateIndex
.text:004B0E22                 mov     [eax+_KAPC.RundownRoutine], ecx ; KAPC.RundownRoutine=RundownRoutine
.text:004B0E25                 mov     ecx, [ebp+NormalRoutine]
.text:004B0E28                 xor     edx, edx        ; edx=0
.text:004B0E2A                 mov     [eax+_KAPC.NormalRoutine], ecx ; KAPC.NormalRoutine=NormalRoutine
.text:004B0E2D                 cmp     ecx, edx
.text:004B0E2F                 jz      short loc_4B0E3F ; 如果NormalRoutine==0跳转
.text:004B0E31                 mov     cl, [ebp+ApcMode]
.text:004B0E34                 mov     [eax+_KAPC.ApcMode], cl ; 如果NormalRoutine!=0
.text:004B0E34                                         ; _KAPC.ApcMode=ApcMode
.text:004B0E37                 mov     ecx, [ebp+NormalContext]
.text:004B0E3A                 mov     [eax+_KAPC.NormalContext], ecx ; KAPC.NormalContext=NormalContext
.text:004B0E3D                 jmp     short loc_4B0E45
.text:004B0E3F ; ---------------------------------------------------------------------------
.text:004B0E3F
.text:004B0E3F loc_4B0E3F:                             ; CODE XREF: KeInitializeApc(x,x,x,x,x,x,x,x)+3C↑j
.text:004B0E3F                 mov     [eax+_KAPC.ApcMode], dl ; _KAPC.ApcMode=0
.text:004B0E42                 mov     [eax+_KAPC.NormalContext], edx ; _KAPC.NormalContext=0
.text:004B0E45
.text:004B0E45 loc_4B0E45:                             ; CODE XREF: KeInitializeApc(x,x,x,x,x,x,x,x)+4A↑j
.text:004B0E45                 mov     [eax+_KAPC.Inserted], dl ; _KAPC.Inserted=0
.text:004B0E48                 pop     ebp
.text:004B0E49                 retn    20h ; ' '

把反汇编大概翻译成c语言如下:

c 复制代码
VOID KeInitializeApc(
    PKAPC               Apc,
    PKTHREAD            Thread,
    KAPC_ENVIRONMENT    Environment,
    PKKERNEL_ROUTINE    KernelRoutine,
    PKRUNDOWN_ROUTINE   RundownRoutine,
    PKNORMAL_ROUTINE    NormalRoutine,
    KPROCESSOR_MODE     ApcMode,
    PVOID               NormalContext
)
{
    UCHAR ApcStateIndex;

    //_KAPC 固定初始化
    Apc->Type = 0x12;            // mov [eax+Type], 12h
    Apc->Size = 0x30;            // mov [eax+Size], 30h
	
    //0 OriginalApcEnvironment 原始 APC 环境(线程最初所在的进程)
    //1 AttachedApcEnvironment 靠环境(KeStackAttachProcess 后的 APC 环境)
    //2 CurrentApcEnvironment 初始化APC时环境
    //3 InsertApcEnvironment  插入APC时环境
	//如果Environment==CurrentApcEnvironment
    if (Environment == CurrentApcEnvironment)
    {
		//则使用 Thread->ApcStateIndex(此字段用于描述此线程是否为挂靠状态)
        ApcStateIndex = Thread->ApcStateIndex;   
    }
    else
    {
		//否则直接使用传入的 Environment
        ApcStateIndex = (UCHAR)Environment;
    }

    //绑定 APC 所属线程
    Apc->Thread = Thread;

    //设置 APC 回调函数
    Apc->KernelRoutine  = KernelRoutine;
    Apc->RundownRoutine = RundownRoutine;
    Apc->NormalRoutine  = NormalRoutine;

    //APC 所属 APC_STATE
    Apc->ApcStateIndex = ApcStateIndex;
	
   
    //存在 NormalRoutine,表示该 APC 支持 Normal APC 阶段(执行模式由 ApcMode 决定)
    if (NormalRoutine != NULL)
    {
        Apc->ApcMode        = ApcMode;        
        Apc->NormalContext = NormalContext;
    }
    else
    {
        
       `//没有 NormalRoutine:
        //强制 ApcMode = 0
        //NormalContext = NULL
        Apc->ApcMode        = 0;
        Apc->NormalContext = NULL;
    }

    //设置APC插入状态:还未插入队列
    Apc->Inserted = FALSE;      
}

四、KeInitializeApc 实际只做了 5 件事

1. 初始化 _KAPC 的固定头部

  • 设置对象类型与大小
  • 让内核知道:"这是一块 APC 结构"
bash 复制代码
Apc->Type = 0x12;
Apc->Size = sizeof(KAPC);

本质:对象合法性标记

2. 绑定 APC 的"归属线程"

  • 指定 APC 将来在哪个线程上下文中执行
bash 复制代码
Apc->Thread = Thread;

本质:APC 是线程私有的,不是进程私有的

3. 决定 APC 属于哪个 APC_STATE(环境)

  • 如果是 CurrentApcEnvironment(2)

    → 自动使用 Thread->ApcStateIndex

  • 否则

    → 使用调用者指定的环境

bash 复制代码
Apc->ApcStateIndex = 
    (Environment == CurrentApcEnvironment)
        ? Thread->ApcStateIndex
        : Environment;

本质:APC 要挂到"原始进程"还是"挂靠进程"的 APC 队列

4.填充 APC 的回调函数与执行上下文

  • KernelRoutine(必有)

  • RundownRoutine(可选)

  • NormalRoutine(决定是否存在 Normal APC 阶段)

bash 复制代码
Apc->KernelRoutine  = KernelRoutine;
Apc->RundownRoutine = RundownRoutine;
Apc->NormalRoutine  = NormalRoutine;

并且:

只有在存在 NormalRoutine 的情况下,APC 才具备进入 Normal APC 阶段的可能。

此时:

  • ApcMode 用于指定 NormalRoutine 的执行模式(Kernel / User)

  • NormalContext 作为传递给 NormalRoutine 的上下文参数

如果 NormalRoutine == NULL:

  • 该 APC 必定是一个 纯内核 APC

  • ApcMode 与 NormalContext 对执行流程不再具有语义意义,因此被内核强制清零

5. 标记 APC 当前"尚未被插入"

bash 复制代码
Apc->Inserted = FALSE;

本质:告诉内核:这个 APC 只是准备好了,但还没投递

五、结论

KeInitializeApc 的唯一目的:

把一块内存,初始化成一个"合法、完整、可被 KeInsertQueueApc 投递的 APC 描述对象"。

相关推荐
C++ 老炮儿的技术栈13 小时前
什么是通信规约
开发语言·数据结构·c++·windows·算法·安全·链表
Ankie Wan14 小时前
windows技巧:要将 Windows 资源管理器的默认查看方式设置为详细信息
windows·windows11·效率提升·文件夹·windows技巧·详细信息
ayaya_mana16 小时前
VS Code 远程开发:SSH连接与远程资源管理器的配置
linux·ide·windows·vscode·远程资源管理
龙潜月七17 小时前
做一个背单词的脚本
数据库·windows·c#·aigc·程序那些事
ohoy17 小时前
RedisTemplate 使用之List
数据结构·windows·list
L Jiawen17 小时前
【Windows 系统】Chrome浏览器退出登录状态失效
前端·chrome·windows
怣疯knight18 小时前
微软outlook邮箱被封后如何解决和原因
windows·outlook
开开心心就好18 小时前
系统管理工具,多功能隐私清理文件粉碎工具
java·网络·windows·r语言·电脑·excel·symfony
广州服务器托管18 小时前
比较优秀的视频音频播放器PotPlayer64-v1.7.22764绿色版
运维·windows·计算机网络·电脑·音视频·可信计算技术