六、APC
dt _KTHREAD与APC相关的项
+0x03a Alerted : [2] UChar //可警惕。只有允许被唤醒的情况下,警惕才有意义
+0x03c Alertable : Pos 5, 1 Bit //是否可以被唤醒
+0x040 ApcState : _KAPC_STATE
+0x0b8 ApcQueueable : Pos 5, 1 Bit //是否允许APC插入队列,默认为1
+0x134 ApcStateIndex : UChar
+0x168 ApcStatePointer : [2] Ptr32 _KAPC_STATE //里面的两个指针,存的是0x040和0x170的指针
+0x170 SavedApcState : _KAPC_STATE
KTHREAD下面几个成员分析
-
ApcStateIndex
-
ApcStatePointer
-
ApcState
-
//没有挂靠的时候
ApcStateIndex = 0
ApcStatePointer[0] = ApcState
ApcStatePointer[1] = SaveApcState
//挂靠的时候
ApcStateIndex = 1
ApcStatePointer[0] = SaveApcState
ApcStatePointer[1] = ApcState //新进程环境中要执行的APC对象
KAPC_STATE结构
kd> dt _KAPC_STATE
ntdll!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY //数组有两个元素,分别代表内核模式APC和用户模式APC对象链表
+0x010 Process : Ptr32 _KPROCESS //apc所关联的进程
+0x014 KernelApcInProgress : UChar //内核APC是否正在执行
+0x015 KernelApcPending : UChar //有内核模式apc对象正在等待交付
+0x016 UserApcPending : UChar //有用户模式apc对象正在等待交付
KAPC结构
kd> dt _KAPC
ntdll!_KAPC
+0x000 Type : UChar
+0x001 SpareByte0 : UChar
+0x002 Size : UChar //KAPC结构的大小
+0x003 SpareByte1 : UChar
+0x004 SpareLong0 : Uint4B
+0x008 Thread : Ptr32 _KTHREAD //指向此apc对象所在的KTHREAD对象
+0x00c ApcListEntry : _LIST_ENTRY
+0x014 KernelRoutine : Ptr32 void
+0x018 RundownRoutine : Ptr32 void
+0x01c NormalRoutine : Ptr32 void
+0x020 NormalContext : Ptr32 Void //参数
+0x024 SystemArgument1 : Ptr32 Void //参数
+0x028 SystemArgument2 : Ptr32 Void //参数
+0x02c ApcStateIndex : Char
+0x02d ApcMode : Char //0为内核模式,1为用户模式
+0x02e Inserted : UChar //是否被插入过,插入了就等于1
-
KernelRoutine:是一个函数指针,该函数将在内核模式的 APC_LEVEL 上被执行
-
RundownRoutine:是一个函数指针,当一个线程终止时,如果它的 APC 链表中还有 APC 对象。
如果RundownRoutine 成员非空,则调用它所指的函数。
-
NormalRoutine:指向一个在 PASSIVE_LEVEL 上执行的函数。在这三个函数指针成员中,只有KernelRoutine域是必须的
RundownRoutine 和 NormalRoutine都是可选的。如果 NormalRoutine 为空的话,则其后的 NormalContext 和
APCMode域也将被忽略。
-
SystemArgument1和SystemArgument2:是两个提供给 KernelRoutine 或 NormalRoutine 函数的参数。
内核模式特殊 APC
-
是指NormalRoutine成员为NULL的apc
内核模式普通APC
-
NormalRoutine成员不为NULL
-
ApcMode成员为KernelMode
用户模式APC
-
NormalRoutine 成员不为 NULL
-
ApcMode成员为UserMode
查看wrk的KeInitializeApc结构
KeInitializeApc
KeInitializeApc (
__out PRKAPC Apc,
__in PRKTHREAD Thread, //要插入到哪个线程
__in KAPC_ENVIRONMENT Environment, //插入的环境
__in PKKERNEL_ROUTINE KernelRoutine,
__in_opt PKRUNDOWN_ROUTINE RundownRoutine, //一般写NULL就可以了
__in_opt PKNORMAL_ROUTINE NormalRoutine,
__in_opt KPROCESSOR_MODE ProcessorMode, //内核还是用户模式
__in_opt PVOID NormalContext //参数
);
typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT;
KAPC_ENVIRONMENT ,插入的APC的四种环境
-
无论挂不挂靠,都插入到原始环境
-
无论挂不挂靠,都插入到挂靠环境
-
在初始化apc函数中,看线程的 ApcStateIndex这个值,如果ApcStateIndex =0 插入到原始环境,如果ApcStateIndex =1 插入到挂靠环境
-
选择插入,初始化函数不插入,插入APC函数的时候再选择插入
ida分析KeInitializeApc
有两种APC类型,内核模式和用户模式。
内核模式的APC并不要求从目标线程获得许可就可以运行在该线程的环境中,而用户模式的APC必须先获得许可才可以。内核模式的APC无需目标线程的干涉或者同意,就可以中断该线程并执行一个过程。
内核模式的APC也有两种类型:普通的和特殊的。
特殊的APC在APC级别上执行,并且运行APC例程修改某些APC参数。普通的APC在被动级别上执行,并且接收被特殊APC例程修改的参数(如果它们未被修改过,则直接接收原始的参数)。
通过将IRQL提升到APC级别或者调用KeEnterGuardRegion,就可以进制普通的和特殊的内核模式APC。KeEnterGuardRegion通过设置调用线程的KTHREAD结构中的SpecialApcDisable域,来进制APC被交付。一个线程要想进制普通类型的APC,唯一的办法是调用KeEnterCriticalRegion,它会设置该线程的KTHREAD结构中的KernelAPCDisable域。
每种类型APC的插入和交付行为
wrk查看KeInsertQueueAPC函数参数
BOOLEAN KeInsertQueueApc (
__inout PRKAPC Apc, //APC结构
__in_opt PVOID SystemArgument1, //可选参数1
__in_opt PVOID SystemArgument2, //可选参数2
__in KPRIORITY Increment //优先级
)
ida分析KeInsertQueueAPC
'
进到KeInsertQueueAPC@12
6.3.内核APC
#pragma once
#include <ntifs.h>
typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT;
typedef VOID(*PKNORMAL_ROUTINE) (
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
typedef VOID(*PKKERNEL_ROUTINE) (
IN struct _KAPC* Apc,
IN OUT PKNORMAL_ROUTINE* NormalRoutine,
IN OUT PVOID* NormalContext,
IN OUT PVOID* SystemArgument1,
IN OUT PVOID* SystemArgument2
);
typedef VOID(*PKRUNDOWN_ROUTINE) (
IN struct _KAPC* Apc
);
VOID KeInitializeApc(
__out PRKAPC Apc,
__in PRKTHREAD Thread,
__in KAPC_ENVIRONMENT Environment,
__in PKKERNEL_ROUTINE KernelRoutine,
__in_opt PKRUNDOWN_ROUTINE RundownRoutine,
__in_opt PKNORMAL_ROUTINE NormalRoutine,
__in_opt KPROCESSOR_MODE ApcMode,
__in_opt PVOID NormalContext
);
BOOLEAN KeInsertQueueApc(
__inout PRKAPC Apc,
__in_opt PVOID SystemArgument1,
__in_opt PVOID SystemArgument2,
__in KPRIORITY Increment
);
DriverMain.c
#include <ntifs.h>
#include "struct.h"
VOID NormalRoutineFunc(
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
DbgPrintEx(77, 0, "[db]:NormalRoutineFunc\r\n");
//设置事件
KeSetEvent(SystemArgument1, 0, FALSE);
}
VOID kernelRoutineFunc(
IN struct _KAPC* Apc,
IN OUT PKNORMAL_ROUTINE* NormalRoutine,
IN OUT PVOID* NormalContext,
IN OUT PVOID* SystemArgument1,
IN OUT PVOID* SystemArgument2
)
{
DbgPrintEx(77, 0, "[db]:---------kernelRoutineFunc pid = %d--------------\r\n", PsGetCurrentProcessId());
DbgPrintEx(77, 0, "[db]:kernelRoutineFunc\r\n");
ExFreePool(Apc);
}
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
PKAPC pApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
memset(pApc, 0, sizeof(KAPC));
PKEVENT pEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
memset(pEvent, 0, sizeof(KEVENT));
//初始化事件
KeInitializeEvent(pEvent, SynchronizationEvent, FALSE);
//apc要插入到的线程
PETHREAD eThread = NULL;
PsLookupThreadByThreadId(1376, &eThread); //explorer的主线程id
DbgPrintEx(77, 0, "[db]:---------main pid = %d--------------\r\n", PsGetCurrentProcessId());
//初始化apc
KeInitializeApc(pApc, eThread, OriginalApcEnvironment,
kernelRoutineFunc, NULL, NormalRoutineFunc, KernelMode, (PVOID)1);
//插入apc
BOOLEAN is = KeInsertQueueApc(pApc, pEvent, NULL, 0);
DbgPrintEx(77, 0, "[db]:-----------------------\r\n");
if (!is)
{
ExFreePool(pApc);
ExFreePool(pEvent);
}
else
{
KeWaitForSingleObject(pEvent, Executive, KernelMode, FALSE, NULL);
DbgPrintEx(77, 0, "[db]:-----------111111111------------\r\n");
ExFreePool(pEvent);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
结果
[db]:---------main pid = 4--------------
[db]:-----------------------
[db]:---------kernelRoutineFunc pid = 1372--------------
[db]:kernelRoutineFunc
[db]:NormalRoutineFunc
[db]:-----------111111111------------
6.4.用户APC
6.4.1.32位系统
#include <stdio.h>
#include <Windows.h>
VOID Test(PVOID param1, PVOID param2, PVOID param3)
{
printf("Apc被执行\r\n");
}
int main()
{
printf("%d,Test = %x\r\n", GetCurrentThreadId(), Test);
system("pause");
while (1)
{
printf("--------------------\r\n");
Sleep(100000);
}
return 0;
}
#pragma once
#include <ntifs.h>
typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT;
typedef VOID(*PKNORMAL_ROUTINE) (
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
typedef VOID(*PKKERNEL_ROUTINE) (
IN struct _KAPC* Apc,
IN OUT PKNORMAL_ROUTINE* NormalRoutine,
IN OUT PVOID* NormalContext,
IN OUT PVOID* SystemArgument1,
IN OUT PVOID* SystemArgument2
);
typedef VOID(*PKRUNDOWN_ROUTINE) (
IN struct _KAPC* Apc
);
VOID KeInitializeApc(
__out PRKAPC Apc,
__in PRKTHREAD Thread,
__in KAPC_ENVIRONMENT Environment,
__in PKKERNEL_ROUTINE KernelRoutine,
__in_opt PKRUNDOWN_ROUTINE RundownRoutine,
__in_opt PKNORMAL_ROUTINE NormalRoutine,
__in_opt KPROCESSOR_MODE ApcMode,
__in_opt PVOID NormalContext
);
BOOLEAN KeInsertQueueApc(
__inout PRKAPC Apc,
__in_opt PVOID SystemArgument1,
__in_opt PVOID SystemArgument2,
__in KPRIORITY Increment
);
BOOLEAN
KeAlertThread(
__inout PKTHREAD Thread,
__in KPROCESSOR_MODE AlertMode
);
//EXTERN_C NTSTATUS PsWrapApcWow64Thread(PVOID* ApcContext, PVOID* ApcRoutine);
#include <ntifs.h>
#include "stuct.h"
VOID kernelRoutineFunc(
IN struct _KAPC* Apc,
IN OUT PKNORMAL_ROUTINE* NormalRoutine,
IN OUT PVOID* NormalContext,
IN OUT PVOID* SystemArgument1,
IN OUT PVOID* SystemArgument2
)
{
DbgPrintEx(77, 0, "[db]:---------kernelRoutineFunc pid = %d--------------\r\n", PsGetCurrentProcessId());
DbgPrintEx(77, 0, "[db]:kernelRoutineFunc\r\n");
ExFreePool(Apc);
}
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
PKAPC pApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
memset(pApc, 0, sizeof(KAPC));
PKEVENT pEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
memset(pEvent, 0, sizeof(KEVENT));
KeInitializeEvent(pEvent, SynchronizationEvent, FALSE);
PETHREAD eThread = NULL;
PsLookupThreadByThreadId(2132, &eThread); //线程cid
DbgPrintEx(77, 0, "[db]:---------main pid = %d--------------\r\n", PsGetCurrentProcessId());
KeInitializeApc(pApc, eThread, OriginalApcEnvironment,
kernelRoutineFunc, NULL, 0x401000, UserMode, (PVOID)1); //函数地址
*((PUCHAR)eThread + 0x3c) |= 0x20; //Alertable 置1
BOOLEAN is = KeInsertQueueApc(pApc, pEvent, NULL, 0);
KeAlertThread(eThread, UserMode);
DbgPrintEx(77, 0, "[db]:-----------------------\r\n");
if (!is)
{
ExFreePool(pApc);
ExFreePool(pEvent);
}
else
{
DbgPrintEx(77, 0, "[db]:-----------111111111------------\r\n");
ExFreePool(pEvent);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
6.4.2.64位系统
stuct.c
#pragma once
#include <ntifs.h>
typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT;
typedef VOID (*PKNORMAL_ROUTINE) (
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
typedef VOID (*PKKERNEL_ROUTINE) (
IN struct _KAPC *Apc,
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
IN OUT PVOID *NormalContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2
);
typedef VOID (*PKRUNDOWN_ROUTINE) (
IN struct _KAPC *Apc
);
VOID KeInitializeApc(
__out PRKAPC Apc,
__in PRKTHREAD Thread,
__in KAPC_ENVIRONMENT Environment,
__in PKKERNEL_ROUTINE KernelRoutine,
__in_opt PKRUNDOWN_ROUTINE RundownRoutine,
__in_opt PKNORMAL_ROUTINE NormalRoutine,
__in_opt KPROCESSOR_MODE ApcMode,
__in_opt PVOID NormalContext
);
BOOLEAN KeInsertQueueApc(
__inout PRKAPC Apc,
__in_opt PVOID SystemArgument1,
__in_opt PVOID SystemArgument2,
__in KPRIORITY Increment
);
BOOLEAN
KeAlertThread(
__inout PKTHREAD Thread,
__in KPROCESSOR_MODE AlertMode
);
EXTERN_C NTSTATUS PsWrapApcWow64Thread(PVOID *ApcContext, PVOID *ApcRoutine);
DriverMain.c
#include <ntifs.h>
#include "stuct.h"
VOID kernelRoutineFunc(
IN struct _KAPC *Apc,
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
IN OUT PVOID *NormalContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2
)
{
DbgPrintEx(77, 0, "[db]:---------kernelRoutineFunc pid = %d--------------\r\n", PsGetCurrentProcessId());
DbgPrintEx(77, 0, "[db]:kernelRoutineFunc\r\n");
ULONG64 addr = 0x401000;
PsWrapApcWow64Thread(NULL, &addr); //64位系统需要调用跟这个加密
//DbgBreakPoint();
*NormalRoutine = addr;
ExFreePool(Apc);
}
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
PKAPC pApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
memset(pApc, 0, sizeof(KAPC));
PKEVENT pEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
memset(pEvent, 0, sizeof(KEVENT));
KeInitializeEvent(pEvent, SynchronizationEvent, FALSE);
PETHREAD eThread = NULL;
PsLookupThreadByThreadId(3676, &eThread);
DbgPrintEx(77, 0, "[db]:---------main pid = %d--------------\r\n", PsGetCurrentProcessId());
KeInitializeApc(pApc, eThread, OriginalApcEnvironment,
kernelRoutineFunc, NULL, 0x401000, UserMode, (PVOID)1);
*((PUCHAR)eThread + 0x4c) |= 0x20;
BOOLEAN is = KeInsertQueueApc(pApc, pEvent, NULL, 0);
KeAlertThread(eThread, UserMode);
DbgPrintEx(77, 0, "[db]:-----------------------\r\n");
if (!is)
{
ExFreePool(pApc);
ExFreePool(pEvent);
}
else
{
DbgPrintEx(77, 0, "[db]:-----------111111111------------\r\n");
ExFreePool(pEvent);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
posted on 2022-09-18 23:34 zhang_derek 阅读(315) 评论(0) 编辑 收藏 举报