0%

在内核中使用APC注入DLL到R3进程(2)

简介

另外还可以在内核中使用APC的方式进行注入,这里参照Github中的例子进行修改
内核注入DLL到R3进程:https://github.com/wbenny/injdrv
简化后的代码(推荐):https://github.com/GoodstudyChina/APC-injection-x86-x64

工作原理

使用 PsSetCreateProcessNotifyRoutineExPsSetLoadImageNotifyRoutine 两个通知。
在进程通知中,记录路径、PID、已加载模块、是否要注入等信息到链表中,在模块通知中,遍历
链表并更新对应已加载模块信息,如果所需的模块已全部加载完毕(触发通知时,模块尚未加载),
根据相关标志位信息进行注入(注意只需要注入一次)

注入的内容为APC函数格式的shellcode指令,因为当前线程就在目标进程中,所以不需要切换环境,
直接使用 ZwAllocateVirtualMemory 申请可读写执行权限的内存,保存相关shellcode信息,然后
使用 KeInsertQueueApc 插入到APC队列中,最后调用 KeTestAlertThread 进入alertable状态

封装APC函数

关于APC所使用的进程环境设置如下所示,一般总是使用 OriginalApcEnvironment 原始环境,
当进程 KeAttachProcess/KeDetachProcess 时,APC环境就会发生变化,可以参考WRK源码

1
2
3
4
5
6
typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment = 0, // 原始的进程环境
AttachedApcEnvironment, // 挂靠后的进程环境
CurrentApcEnvironment, // 当前环境
InsertApcEnvironment // 被插入时的环境
} KAPC_ENVIRONMENT, *PKAPC_ENVIRONMENT;

关于APC函数参数中功能函数类型的定义

1
2
3
4
5
6
7
8
9
10
typedef VOID(NTAPI *PKNORMAL_ROUTINE)(
PVOID NormalContext,
PVOID SystemArgument1,
PVOID SystemArgument2);
typedef VOID(NTAPI* PKKERNEL_ROUTINE)(
PRKAPC Apc,
PKNORMAL_ROUTINE *NormalRoutine,
PVOID *NormalContext,
PVOID *SystemArgument1,
PVOID *SystemArgument2);

插入APC的函数封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
VOID NTAPI InjpApcKernelRoutine(
IN PKAPC Apc,
IN OUT PKNORMAL_ROUTINE* NormalRoutine,
IN OUT PVOID* NormalContext,
IN OUT PVOID* SystemArgument1,
IN OUT PVOID* SystemArgument2)
{
UNREFERENCED_PARAMETER(NormalRoutine);
UNREFERENCED_PARAMETER(NormalContext);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
if (Apc) ExFreePool(Apc);
}

NTSTATUS NTAPI InjpInsertQueueApc(
IN KPROCESSOR_MODE ApcMode,
IN PKNORMAL_ROUTINE NormalRoutine,
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
PKAPC Apc = NULL;
BOOLEAN Result = FALSE;
Apc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
if (!Apc)
return STATUS_INSUFFICIENT_RESOURCES;
KeInitializeApc(Apc, // Apc
PsGetCurrentThread(), // Thread
OriginalApcEnvironment, // Environment
&InjpApcKernelRoutine, // KernelRoutine
NULL, // RundownRoutine
NormalRoutine, // NormalRoutine
ApcMode, // ApcMode
NormalContext); // NormalContext
Result = KeInsertQueueApc(Apc, // Apc
SystemArgument1, // SystemArgument1
SystemArgument2, // SystemArgument2
0); // Increment
if (!Result)
{
ExFreePool(Apc);
return STATUS_THREAD_IS_TERMINATING;
}
return STATUS_SUCCESS;
}

注入的函数

相关shellcode请自行生成,注意32位系统与64位系统所用shellcode不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
typedef struct _INJECT_CONTEXT {
BYTE user_mode_normal_routine[512]; // shellcode代码
WCHAR dll_full_path[260]; // 注入的DLL全路径
CHAR procedure_name[64]; // DLL导出的函数名称
ULONG_PTR ldr_load_dll; // 当前进程LdrLoadDll地址
ULONG_PTR ldr_get_procedure_address; // 当前进程LdrGetProcedureAddress地址
} INJECT_CONTEXT, *PINJECT_CONTEXT;

NTSTATUS NTAPI InjpAPCInject()
{
BOOLEAN IsWow64 = FALSE;
NTSTATUS Status = STATUS_SUCCESS;
PINJECT_CONTEXT InjectContext = NULL;
SIZE_T InjectContextSize = sizeof(INJECT_CONTEXT);
PVOID ApcContext = NULL;
PVOID ApcRoutine = NULL;
// 被系统保护的进程不注入
if (PsIsProtectedProcess(PsGetCurrentProcess()))
return STATUS_UNSUCCESSFUL;
// 检测当前进程是否是Wow程序
IsWow64 = PsGetProcessWow64Process(PsGetCurrentProcess());
// 申请可读写执行的内存
Status = ZwAllocateVirtualMemory(
NtCurrentProcess(), (PVOID*)&InjectContext, 0, &InjectContextSize,
MEM_TOP_DOWN | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!NT_SUCCESS(Status))
return STATUS_INSUFFICIENT_RESOURCES;
// 复制相关信息
ApcContext = InjectContext;
ApcRoutine = InjectContext->user_mode_normal_routine;
if (IsWow64)
RtlCopyMemory(ApcRoutine, ShellCodeWow64, sizeof(ShellCodeWow64));
else
RtlCopyMemory(ApcRoutine, ShellCode, sizeof(ShellCode));
wcscpy_s(InjectContext->dll_full_path, 260, DllFullPath);
strcpy_s(InjectContext->procedure_name, 64, "FuncName");
InjectContext->ldr_load_dll = LdrLoadDll;
InjectContext->ldr_get_procedure_address = LdrGetProcedureAddress;
// 处理Wow64地址转换
if (IsWow64)
PsWrapApcWow64Thread(&ApcContext, &ApcRoutine);
// 插入用户APC函数
Status = InjpInsertQueueApc(
UserMode, (PKNORMAL_ROUTINE)ApcRoutine, ApcContext, NULL, NULL);
if (!NT_SUCCESS(Status))
{
// 插入APC失败 释放空间
ZwFreeVirtualMemory(NtCurrentProcess(),
(PVOID*)&InjectContext, &InjectContextSize, MEM_RELEASE);
}
else
{
// 插入APC成功 主动触发alertable
KeTestAlertThread(UserMode);
}
return Status;
}