21-内核回调表注入(KernelCallbackTable Injection)

参考资料GitHub - 0xHossam/KernelCallbackTable-Injection-PoC:在进程环境块 (PEB) 中操作内核回调表以执行进程注入和劫持执行流程的概念验证arrow-up-right

注意:本人才疏学浅,只针对参考资料进行翻译和补充一下自己的见解。

一、PEB

进程环境块 (PEB) 是 Windows 中每个正在运行的程序都依赖的关键结构。将其视为“控制中心”或“中心”,其中包含有关程序如何运行以及与系统交互的基本信息。PEB 是进程内存空间的一部分,它可以帮助操作系统和程序管理各种内部细节。

进程镂空注入(Process Hollowing Injection) 中我们获取了PEB的地址,就可以操纵PEB里的 ImageBaseAddress 这个字段

DLL劫持注入(涉及白加黑) 中,我们获取PEB的地址,进一步获取Ldr的地址,从而可以修改进程的已装载模块。

在本节 内核回调表注入(KernelCallbackTable Injection) 中,我们将获取PEB的地址,就可以获取 KernelCallbackTable 这个数据结构的地址。

在后面的 自举的代码幽灵——反射DLL注入(Reflective DLL Injection) 中,我们深入的去使用PEB中的LDR字段,从而获得InMemoryOrderModuleList链表获取dll名称,遍历函数所在的dll导出表获得必要的函数的名称,如果匹配成功就返回目标函数的地址。

使用windbg查看PEB数据结构,我这里调式的是64位的程序,可以看到 KernelCallbackTable 在PEB偏移0x058的位置上。

二、KernelCallbackTable

2.1 介绍

当程序调用USER32.DLL时,KernelCallbackTable就会被初始化为函数数组。这个函数数组中的函数通常用于响应窗口消息。

KernelCallbackTable 注入可以被用于在远程进程中注入shellcode,KernelCallbackTable 可以在PEB中找到,它被KeUserModeCallback所使用,内核态调用KeUserModeCallback就可以在用户态中执行KernelCallbackTable中对应的函数

此表包含指向处理窗口消息和其他进程间通信的各种回调函数的指针。此表中的每个函数都对应于特定任务,例如处理数据传输消息或管理窗口销毁事件

如果我们可以修改里面的内核回调函数,让其指向我们的shellcode,当收到某一特定Windows消息后,从而调用shellcode,即可完成注入。

KernelCallbackTable的结构定义

用windgb查看内核回调表

  1. 首先让windbg Step Over 十几条命令让它完成初始化

  2. 查看peb各字段的值,命令:dt <PEB的地址> ntdll!_PEB。例如 dt 0x000000e22f856000 ntdll!_PEB

  1. 查看内核回调表内容

命令

2.2 分析 Kernel Callback Table 中的函数指针

在内核回调表中,每个条目对应于一个指向回调函数的指针,当收到特定 Windows 消息时,进程可以调用该函数

在内核回调注入中,需要特别关注 _fnCOPYDATA 条目,因为它可以通过消息触发,从而允许在进程之间传递数据,我们可以将其值修改为shellcode

三、通过内核回调表操作进行进程注入

步骤一:启用调试权限

出于测试目的,我需要启用一些调试权限。调试权限允许当前进程访问其他进程的敏感区域,这些区域对于内存读取和写入等任务是必需的。如果没有这些权限,Windows 安全性将限制对其他进程的访问,从而无法注入。

详细说明

  1. OpenProcessToken 打开当前进程的访问令牌的句柄,该令牌拥有安全权限。

  2. LookupPrivilegeValue 检索权限的本地唯一标识符 (LUID) ,该权限授予进程调试功能。SE_DEBUG_NAME

  3. AdjustTokenPrivileges 在令牌中设置权限以启用调试权限,从而允许进程对其他进程执行内存操作。

步骤二:加载 NtQueryInformationProcess

NtQueryInformationProcess 函数是一个底层 Windows API 函数,用于访问有关进程的特定信息,包括进程环境块 (PEB)。PEB 包含内核回调表等结构,该表存储指向各种回调函数的指针。此函数必须动态加载,因为它是未导出API函数,通常无法通过标准 Windows 标头访问。

  • GetModuleHandle 加载到进程中,从而访问其函数。

  • GetProcAddress 检索的地址,以便以后调用此函数以收集有关目标进程的详细信息。NtQueryInformationProcess

步骤三:启动 Target 流程

进程注入首先创建一个 Notepad 实例,该实例将用作注入代码的目标。此示例使用 Notepad,因为它是一个简单、众所周知的应用程序,允许对注射过程进行受控测试。

步骤四:等待进程完成初始化

步骤五:找到记事本的窗口句柄

  • FindWindow 按名称搜索记事本的 window 类并找到其句柄。如果找到句柄,则检索关联的进程 ID。GetWindowThreadProcessId

  • 为什么我们要找到记事本的窗口句柄的句柄呢?因为内核回调表在这个窗口进程中

步骤六: 获得记事本窗口关联的进程的PID

步骤七:检索 PEB 地址

NtQueryInformationProcess 用于检索目标进程的 PEB 地址。PEB 包含 Kernel Callback Table,这是此注入技术中修改的重点。

步骤八:读取与目标窗口关联的进程的内核回调表的地址

使用 PEB 地址,代码读取 Kernel Callback Table 的内存地址,从而允许修改此表中的函数指针以控制回调。

步骤九:将内核回调表所有回调函数指针读到KERNELCALLBACKTABLE结构体中

步骤十:在远程目标窗口进程中申请一块RWX内存区域并将shellcode写入该区域

有效负载(通常是 shellcode)在目标进程的内存空间内分配。内存标记为 executable 以确保代码可以运行。

步骤十一: 修改 _fnCOPYDATA 函数指针为shellcode

步骤十二: 为修改后的KERNELCALLBACKTABLE申请一块内存区域

步骤十三: 更新PEB中的KernelCallbackTable指针,使其指向并修改后的内核回调表。

步骤十四: 发送消息给目标窗口,从而触发执行shellcode

四、完整代码

struct.h

main.cpp

Last updated