返回顶部
扩大
缩小

Zhang_derek

六、APC

6.1.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

  • SaveApcState

//没有挂靠的时候
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

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函数参数

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

#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位系统

ApcTest.cpp

#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;
}

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");
	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编辑  收藏  举报

导航