六、APC

2022-09-29 11:53:01 浏览数 (1)

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
代码语言:javascript复制
//没有挂靠的时候
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;
}

0 人点赞