6.1.APC初始化
dt _KTHREAD与APC相关的项
代码语言:javascript复制 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
- SaveApcState
//没有挂靠的时候
ApcStateIndex = 0
ApcStatePointer[0] = ApcState
ApcStatePointer[1] = SaveApcState
//挂靠的时候
ApcStateIndex = 1
ApcStatePointer[0] = SaveApcState
ApcStatePointer[1] = ApcState //新进程环境中要执行的APC对象
KAPC_STATE结构
代码语言:javascript复制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结构
代码语言:javascript复制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
代码语言:javascript复制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
6.2.APC插入
有两种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函数参数
代码语言:javascript复制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
struct.h
代码语言:javascript复制#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
代码语言:javascript复制#include <ntifs.h>
#include "struct.h"
VOID NormalRoutineFunc(
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
DbgPrintEx(77, 0, "[db]:NormalRoutineFuncrn");
//设置事件
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--------------rn", PsGetCurrentProcessId());
DbgPrintEx(77, 0, "[db]:kernelRoutineFuncrn");
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--------------rn", PsGetCurrentProcessId());
//初始化apc
KeInitializeApc(pApc, eThread, OriginalApcEnvironment,
kernelRoutineFunc, NULL, NormalRoutineFunc, KernelMode, (PVOID)1);
//插入apc
BOOLEAN is = KeInsertQueueApc(pApc, pEvent, NULL, 0);
DbgPrintEx(77, 0, "[db]:-----------------------rn");
if (!is)
{
ExFreePool(pApc);
ExFreePool(pEvent);
}
else
{
KeWaitForSingleObject(pEvent, Executive, KernelMode, FALSE, NULL);
DbgPrintEx(77, 0, "[db]:-----------111111111------------rn");
ExFreePool(pEvent);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
结果
代码语言:javascript复制[db]:---------main pid = 4--------------
[db]:-----------------------
[db]:---------kernelRoutineFunc pid = 1372--------------
[db]:kernelRoutineFunc
[db]:NormalRoutineFunc
[db]:-----------111111111------------
6.4.用户APC
6.4.1.32位系统
ApcTest.cpp
代码语言:javascript复制#include <stdio.h>
#include <Windows.h>
VOID Test(PVOID param1, PVOID param2, PVOID param3)
{
printf("Apc被执行rn");
}
int main()
{
printf("%d,Test = %xrn", GetCurrentThreadId(), Test);
system("pause");
while (1)
{
printf("--------------------rn");
Sleep(100000);
}
return 0;
}
stuct.c
代码语言:javascript复制#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
代码语言:javascript复制#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--------------rn", PsGetCurrentProcessId());
DbgPrintEx(77, 0, "[db]:kernelRoutineFuncrn");
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--------------rn", 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]:-----------------------rn");
if (!is)
{
ExFreePool(pApc);
ExFreePool(pEvent);
}
else
{
DbgPrintEx(77, 0, "[db]:-----------111111111------------rn");
ExFreePool(pEvent);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
6.4.2.64位系统
stuct.c
代码语言:javascript复制#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
代码语言:javascript复制#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--------------rn", PsGetCurrentProcessId());
DbgPrintEx(77, 0, "[db]:kernelRoutineFuncrn");
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--------------rn", 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]:-----------------------rn");
if (!is)
{
ExFreePool(pApc);
ExFreePool(pEvent);
}
else
{
DbgPrintEx(77, 0, "[db]:-----------111111111------------rn");
ExFreePool(pEvent);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}