APC注入

APC机制

线程是不能被杀死 挂起和恢复的,线程在执行的时候自己占据着CPU,别人怎么可能控制他呢?举个极端的例子,如果不调用API,屏蔽中断,并保证代码不出现异常,线程将永久占据CPU。所以说线程如果想结束,一定是自己执行代码把自己杀死,不存在别人把线程结束的情况。

那如果想改变一个线程的行为该怎么办?可以给他提供一个函数,让他自己去调用,这个函数就是APC,即异步过程调用

image-20240304230529013

对于内核APC,APC函数的插入和执行并不是同一个线程,具体点说:在A线程中向B线程插入一个APC,插入的动作是在A线程中完成的,但什么时候执行则由B线程决定。所以叫异步过程调用。

线程切换时,在SwapContext函数即将执行完成的时候,会判断当前是否有要执行的内核APC,接着将判断的结果存到eax,然后返回,接着找到上一层函数KiSwapContext函数,这个函数也没有对APC进行处理,而是继续返回,到父函数,会判断KiSwapContext的返回值,也就是判断当前是否有要处理的内核APC,如果有,则调用KiDeliverApc进行处理。

系统调用中断或者异常(_KiServiceExit),会判断是否有要执行的用户APC,如果有的话则会调用KiDeliverApc函数进行处理,此时KiDeliverApc第一个参数为1,代表执行用户APC和内核APC。当要执行用户APC之前,先要执行内核APC。

对于应用APC,当产生系统调用、中断或者异常,线程在返回用户空间前都会调用_KiServiceExit函数,在_KiServiceExit函数里会判断是否有要执行的用户APC,如果有则调用KiDeliverApc函数进行处理。

有用户APC要执行的话,就意味着线程要提前返回到用户空间去执行,而且返回的位置不是线程进入内核时的位置,而是返回到真正执行APC的位置每处理一个用户APC就会涉及到:内核—>用户空间—>再回到内核空间。进入内核前,当前上下文会被临时保存以待恢复。

APC注入

dllmain

注入的dll弹一个messageBox就行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
MessageBox(NULL, L"Inject", L"Success", 0);
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

KAPC

创建好设备对象和链接对象,设计派遣历程

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
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
UNREFERENCED_PARAMETER(RegisterPath);
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT DeviceObject = NULL;
UNICODE_STRING DeviceObjectName;
UNICODE_STRING DeviceLinkName;
ULONG i;
DriverObject->DriverUnload = DriverUnload;

//创建设备对象名称
RtlInitUnicodeString(&DeviceObjectName, DEVICE_OBJECT_NAME);

//创建设备对象
Status = IoCreateDevice(DriverObject, NULL,
&DeviceObjectName,
FILE_DEVICE_UNKNOWN,
0, FALSE,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
//创建设备连接名称
RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
//将设备连接名称与设备名称关联
Status = IoCreateSymbolicLink(&DeviceLinkName, &DeviceObjectName);
if (!NT_SUCCESS(Status))
{
IoDeleteDevice(DeviceObject);
return Status;
}
//设计符合我们代码的派遣历程
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = PassThroughDispatch; //函数指针
}
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlThroughDispatch;

}

派遣例程

默认直接通过

1
2
3
4
5
6
7
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS; //LastError()
Irp->IoStatus.Information = 0; //ReturnLength
IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给Io管理器
return STATUS_SUCCESS;
}

IRP_MJ_DEVICE_CONTROL自定义

解析IRP堆栈内容,筛选IO控制码,CTL_APC_INJECTION就调用注入函数

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
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp)
{
NTSTATUS Status;
ULONG_PTR Informaiton = 0;
PVOID InputData = NULL;
ULONG InputDataLength = 0;
PVOID OutputData = NULL;
ULONG OutputDataLength = 0;
ULONG IoControlCode = 0;
PEPROCESS EProcess = NULL;
PIO_STACK_LOCATION IoStackLocation = IoGetCurrentIrpStackLocation(Irp); //Irp堆栈

IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
InputData = Irp->AssociatedIrp.SystemBuffer;
OutputData = Irp->AssociatedIrp.SystemBuffer;
InputDataLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
OutputDataLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;

switch (IoControlCode)
{
case CTL_APC_INJECTION:
{
if (InputDataLength >= sizeof(INJECTION_INFORMATION) && InputData)
Status = ApcInjectionPrepare((PINJECTION_INFORMATION)InputData);
else
{
Status = STATUS_INFO_LENGTH_MISMATCH;
}

break;
}
default:
break;
}
Irp->IoStatus.Status = Status; //Ring3 GetLastError();
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给Io管理器
return Status; //Ring3 DeviceIoControl()返回值
}

APC注入主函数

  1. PsLookupProcessByProcessId通过目标进程ID获取EProcess
  2. PsGetProcessWow64Process返回值可以判断是不是Wow64进程。
  3. KeWaitForSingleObject返回值判断是否是僵尸进程。如果是,解引用释放。
  4. KeStackAttachProcess切换进程上下背景文。
  5. 自己实现的SeGetModuleBaseByModuleName从目标进程获取Ntdll模块基地址。这里会使用到是否是Wow64进程
  6. 自己实现的SeGetExportFunctionFromModule从目标模块获取LdrLoadDll导出函数。
  7. 初始化ShellCode
  8. 调用自己的ApcInjection开始进行注入。
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
NTSTATUS ApcInjectionPrepare(IN PINJECTION_INFORMATION InjectionInfo)
{
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS ThreadStatus = STATUS_SUCCESS;
PEPROCESS EProcess = NULL;

Status = PsLookupProcessByProcessId((HANDLE)InjectionInfo->TargetProcessID, &EProcess);
if (NT_SUCCESS(Status))
{
KAPC_STATE Apc;
UNICODE_STRING DllFullPath,NtdllFullPath;
//SET_PROCESS_PROTECTION Protection = { 0 };
PVOID NtdllModuleBase = NULL;
PVOID LdrLoadDll = NULL;
BOOLEAN IsWow64 = (PsGetProcessWow64Process(EProcess) != NULL) ? TRUE : FALSE;
LARGE_INTEGER Timeout = { 0 };

// 进程处于信号状态,中止任何操作
if (KeWaitForSingleObject(EProcess, Executive, KernelMode, FALSE, &Timeout) == STATUS_WAIT_0)
{
DbgPrint("Process Is Terminating\r\n");

if (EProcess)
ObDereferenceObject(EProcess);

return STATUS_PROCESS_IS_TERMINATING;
}

KeStackAttachProcess(EProcess, &Apc); //切换进程上下背景文

RtlInitUnicodeString(&DllFullPath, InjectionInfo->DllFullPath);
RtlInitUnicodeString(&NtdllFullPath, L"Ntdll.dll");
NtdllModuleBase = SeGetModuleBaseByModuleName(EProcess, &NtdllFullPath, IsWow64); //目标进程中获取Ntdll 信息

if (!NtdllModuleBase)
{
DbgPrint("Failed To Get Ntdll Base\r\n");
Status = STATUS_NOT_FOUND;
}

// 获取LdrLoadDll地址
if (NT_SUCCESS(Status))
{
LdrLoadDll = SeGetExportFunctionFromModule(NtdllModuleBase, "LdrLoadDll", EProcess, NULL);
if (!LdrLoadDll)
{
DbgPrint("Failed To Get LdrLoadDll Address\r\n");
Status = STATUS_NOT_FOUND;
}
}
// Call LdrLoadDll
if (NT_SUCCESS(Status))
{
SIZE_T RegionSize = 0;
PINJECTION_DATA InjectionData =
IsWow64 ? GetWow64Code(LdrLoadDll, &DllFullPath) : GetNativeCode(LdrLoadDll, &DllFullPath);


Status = ApcInjection(InjectionData, (HANDLE)InjectionInfo->TargetProcessID, NULL, NULL);

if (NT_SUCCESS(Status))
{
if (InjectionInfo->IsUnlinkModule)
{

}


ZwFreeVirtualMemory(ZwCurrentProcess(), &InjectionData, &RegionSize, MEM_RELEASE);
}
}
KeUnstackDetachProcess(&Apc);
}
else
{
DbgPrint("PsLookupProcessByProcessId() Error\r\n");
}

if (EProcess)
ObDereferenceObject(EProcess);

return Status;
}

从目标进程通过模块名称获取模块基地址

  1. 检查EProcess
  2. 对WOW64和64位进程分别获取指定的PEB,并等待Loader初始化,然后再模块列表中搜索指定的模块名称,如果找到就返回基地址。注意WOW64需要转UNICODE
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
PVOID SeGetModuleBaseByModuleName(IN PEPROCESS EProcess, IN PUNICODE_STRING ModuleName, IN BOOLEAN IsWow64)
{

if (EProcess == NULL)
return NULL;
__try
{
LARGE_INTEGER Timeout = { 0 };
Timeout.QuadPart = -250ll * 10 * 1000; //250 msec.

// Wow64 进程
if (IsWow64)
{
PPEB32 Peb32 = (PPEB32)PsGetProcessWow64Process(EProcess);
if (Peb32 == NULL)
{
DbgPrint("PsGetProcessWow64Process() Error\r\n");
return NULL;
}

// 等待加载器
for (INT i = 0; !Peb32->Ldr && i < 10; i++)
{
DbgPrint("Loader Not Intialiezd\r\n");
KeDelayExecutionThread(KernelMode, TRUE, &Timeout);
}

// 没有加载
if (!Peb32->Ldr)
{
DbgPrint("Loader Was Not Intialiezd In Time\r\n");
return NULL;
}

// 在InLoadOrderModuleList链表中查找
for (PLIST_ENTRY32 ListEntry = (PLIST_ENTRY32)((PPEB_LDR_DATA32)Peb32->Ldr)->InLoadOrderModuleList.Flink;
ListEntry != &((PPEB_LDR_DATA32)Peb32->Ldr)->InLoadOrderModuleList;
ListEntry = (PLIST_ENTRY32)ListEntry->Flink)
{
UNICODE_STRING v1;
PLDR_DATA_TABLE_ENTRY32 LdrDataTableEntry32 = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);

RtlUnicodeStringInit(&v1, (PWCH)LdrDataTableEntry32->BaseDllName.Buffer);

if (RtlCompareUnicodeString(&v1, ModuleName, TRUE) == 0)
return (PVOID)LdrDataTableEntry32->DllBase;
}
}
else
{
PPEB64 Peb64 = PsGetProcessPeb(EProcess);
if (!Peb64)
{
DbgPrint("PsGetProcessPeb() Error\r\n");
return NULL;
}

// 等待加载器
for (INT i = 0; !Peb64->Ldr && i < 10; i++)
{
DbgPrint("Loader Not Intialiezd\r\n");
KeDelayExecutionThread(KernelMode, TRUE, &Timeout);
}

// 没有加载
if (!Peb64->Ldr)
{
DbgPrint("Loader Was Not Intialiezd In Time\r\n");
return NULL;
}

// 在InLoadOrderModuleList链表中查找
for (PLIST_ENTRY64 ListEntry = (PLIST_ENTRY64)((PPEB_LDR_DATA64)Peb64->Ldr)->InLoadOrderModuleList.Flink;
ListEntry != &((PPEB_LDR_DATA64)Peb64->Ldr)->InLoadOrderModuleList;
ListEntry = (PLIST_ENTRY64)ListEntry->Flink)
{
PLDR_DATA_TABLE_ENTRY64 LdrDataTableEntry64 = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY64, InLoadOrderLinks);
if (RtlCompareUnicodeString(&LdrDataTableEntry64->BaseDllName, ModuleName, TRUE) == 0)
return LdrDataTableEntry64->DllBase;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
DbgPrint("Exception:Code: 0x%X\n", GetExceptionCode());
}

return NULL;
}

从目标模块获取目标函数导出地址

PE文件分析

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
PVOID SeGetExportFunctionFromModule(IN PVOID ModuleBase, IN PCCHAR FunctionName, IN PEPROCESS EProcess, IN PUNICODE_STRING ModuleName)
{
PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)ModuleBase;
PIMAGE_NT_HEADERS32 ImageNtHeader32 = NULL;
PIMAGE_NT_HEADERS64 ImageNtHeader64 = NULL;
PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL;
ULONG ImageExportDirectoryLength = 0;
ULONG_PTR FunctionAddress = 0;

if (ModuleBase == NULL)
return NULL;

// 不是PE文件
if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;

ImageNtHeader32 = (PIMAGE_NT_HEADERS32)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);
ImageNtHeader64 = (PIMAGE_NT_HEADERS64)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);

// 不是PE文件
if (ImageNtHeader32->Signature != IMAGE_NT_SIGNATURE)
return NULL;

// 64位
if (ImageNtHeader32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);
ImageExportDirectoryLength = ImageNtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
}
// 32位
else
{
ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);
ImageExportDirectoryLength = ImageNtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
}

PUSHORT AddressOfNameOrdinals = (PUSHORT)(ImageExportDirectory->AddressOfNameOrdinals + (ULONG_PTR)ModuleBase);
PULONG AddressOfNames = (PULONG)(ImageExportDirectory->AddressOfNames + (ULONG_PTR)ModuleBase);
PULONG AddressOfFunctions = (PULONG)(ImageExportDirectory->AddressOfFunctions + (ULONG_PTR)ModuleBase);

for (ULONG i = 0; i < ImageExportDirectory->NumberOfFunctions; ++i)
{
USHORT Index = 0xFFFF;
PCHAR v1 = NULL;

// 按索引查找
if ((ULONG_PTR)FunctionName <= 0xFFFF)
{
Index = (USHORT)i;
}
// 按名字查找
else if ((ULONG_PTR)FunctionName > 0xFFFF && i < ImageExportDirectory->NumberOfNames)
{
v1 = (PCHAR)(AddressOfNames[i] + (ULONG_PTR)ModuleBase);
Index = AddressOfNameOrdinals[i];
}
// 参数错误
else
return NULL;

if (((ULONG_PTR)FunctionName <= 0xFFFF && (USHORT)((ULONG_PTR)FunctionName) == Index + ImageExportDirectory->Base) ||
((ULONG_PTR)FunctionName > 0xFFFF && strcmp(v1, FunctionName) == 0))
{
FunctionAddress = AddressOfFunctions[Index] + (ULONG_PTR)ModuleBase;

// 检查是不是一个递归的导出 FuntionAddress = //Dll.Sub_4
if (FunctionAddress >= (ULONG_PTR)ImageExportDirectory && FunctionAddress <= (ULONG_PTR)ImageExportDirectory
+ ImageExportDirectoryLength)
{
WCHAR v1[256] = { 0 };
ANSI_STRING ForwarderString = { 0 };
ANSI_STRING ForwarderFunctionName = { 0 };

UNICODE_STRING ForwarderModuleName = { 0 }; //maxLength Length wchar*
ULONG DelimIdx = 0;
PVOID ForwardModuleBase = NULL;
PVOID ForwardFunctionAddress = NULL;

// 系统镜像不支持
if (EProcess == NULL)
return NULL;


RtlInitAnsiString(&ForwarderString, (PCSZ)FunctionAddress); //FuntionAddress = //Dll.Sub_4........
RtlInitEmptyUnicodeString(&ForwarderModuleName, v1, sizeof(v1));

RtlAnsiStringToUnicodeString(&ForwarderModuleName, &ForwarderString, FALSE);
for (ULONG j = 0; j < ForwarderModuleName.Length / sizeof(WCHAR); j++)
{
if (ForwarderModuleName.Buffer[j] == L'.')
{
ForwarderModuleName.Length = (USHORT)(j * sizeof(WCHAR));
ForwarderModuleName.Buffer[j] = L'\0';
DelimIdx = j;
break;
}
}

// 获取下一个模块的导出地址(递归的调用自己)
RtlInitAnsiString(&ForwarderFunctionName, ForwarderString.Buffer + DelimIdx + 1);
RtlAppendUnicodeToString(&ForwarderModuleName, L".dll");
ForwardModuleBase = SeGetModuleBaseByModuleName(EProcess, &ForwarderModuleName, PsGetProcessWow64Process(EProcess) != NULL);
ForwardFunctionAddress = SeGetExportFunctionFromModule(ForwardModuleBase,
ForwarderFunctionName.Buffer, EProcess, &ForwarderModuleName);

return ForwardFunctionAddress;
}

break;
}
}

return (PVOID)FunctionAddress;
}

获取ShellCode

WOW64

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
typedef struct _INJECTION_DATA
{
UCHAR ShellCode[0x200];
union
{
UNICODE_STRING DllFullPath;
UNICODE_STRING32 DllFullPath32;
};
BOOLEAN IsUnlinkModule; // Unlink module after injection
wchar_t BufferData[488]; //?? APC使用
PVOID ModuleBase; //??
ULONG CallComplete;
}INJECTION_DATA, *PINJECTION_DATA;

PINJECTION_DATA GetWow64Code(IN PVOID LdrLoadDll, IN PUNICODE_STRING DllFullPath)
{
NTSTATUS Status = STATUS_SUCCESS;
PINJECTION_DATA RemoteBufferData = NULL;
SIZE_T RemoteBufferLength = PAGE_SIZE;

UCHAR ShellCode[] =
{
0x68, 0, 0, 0, 0, // push ModuleHandle offset +1
0x68, 0, 0, 0, 0, // push ModuleFileName offset +6
0x6A, 0, // push Flags
0x6A, 0, // push PathToFile
0xE8, 0, 0, 0, 0, // call LdrLoadDll offset +15
0xBA, 0, 0, 0, 0, // mov edx, COMPLETE_OFFSET offset +20
0xC7, 0x02, 0x7E, 0x1E, 0x37, 0xC0, // mov [edx], CALL_COMPLETE //给APC使用
0xC2, 0x04, 0x00 // ret 4
};

Status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &RemoteBufferData, 0, &RemoteBufferLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NT_SUCCESS(Status))
{
// 准备完整路径
PUNICODE_STRING32 v1 = &RemoteBufferData->DllFullPath32;
v1->Length = DllFullPath->Length;
v1->MaximumLength = DllFullPath->MaximumLength;
v1->Buffer = (ULONG)(ULONG_PTR)RemoteBufferData->BufferData;
memcpy((PVOID)v1->Buffer, DllFullPath->Buffer, DllFullPath->Length);

// 准备ShellCode
memcpy(RemoteBufferData->ShellCode, ShellCode, sizeof(ShellCode));

// 填充结构所需要的参数
*(ULONG*)((PUCHAR)RemoteBufferData + 1) = (ULONG)(ULONG_PTR)&RemoteBufferData->ModuleBase;
*(ULONG*)((PUCHAR)RemoteBufferData + 6) = (ULONG)(ULONG_PTR)v1;
*(ULONG*)((PUCHAR)RemoteBufferData + 15) = (ULONG)((ULONG_PTR)LdrLoadDll - ((ULONG_PTR)RemoteBufferData + 15) - 5 + 1);
*(ULONG*)((PUCHAR)RemoteBufferData + 20) = (ULONG)(ULONG_PTR)&RemoteBufferData->CallComplete;

return RemoteBufferData;
}
return NULL;
}

x64

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
PINJECTION_DATA GetNativeCode(IN PVOID LdrLoadDll, IN PUNICODE_STRING DllFullPath)
{
NTSTATUS Status = STATUS_SUCCESS;
PINJECTION_DATA RemoteBufferData = NULL;
SIZE_T RemoteBufferLength = PAGE_SIZE;

// Code
UCHAR ShellCode[] =
{
0x48, 0x83, 0xEC, 0x28, // sub rsp, 0x28
0x48, 0x31, 0xC9, // xor rcx, rcx
0x48, 0x31, 0xD2, // xor rdx, rdx
0x49, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov r8, ModuleFileName offset +12
0x49, 0xB9, 0, 0, 0, 0, 0, 0, 0, 0, // mov r9, ModuleHandle offset +28
0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov rax, LdrLoadDll offset +32
0xFF, 0xD0, // call rax
0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0, // mov rdx, COMPLETE_OFFSET offset +44
0xC7, 0x02, 0x7E, 0x1E, 0x37, 0xC0, // mov [rdx], CALL_COMPLETE //APC使用
0x48, 0x83, 0xC4, 0x28, // add rsp, 0x28
0xC3 // ret
};

Status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &RemoteBufferData, 0, &RemoteBufferLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NT_SUCCESS(Status))
{
// Copy path
PUNICODE_STRING v1 = &RemoteBufferData->DllFullPath;
v1->Length = 0;
v1->MaximumLength = sizeof(RemoteBufferData->BufferData);
v1->Buffer = RemoteBufferData->BufferData;

RtlUnicodeStringCopy(v1, DllFullPath);

// Copy code
memcpy(RemoteBufferData->ShellCode, ShellCode, sizeof(ShellCode));

// Fill stubs
*(ULONGLONG*)((PUCHAR)RemoteBufferData + 12) = (ULONGLONG)v1;
*(ULONGLONG*)((PUCHAR)RemoteBufferData + 22) = (ULONGLONG)&RemoteBufferData->ModuleBase;
*(ULONGLONG*)((PUCHAR)RemoteBufferData + 32) = (ULONGLONG)LdrLoadDll;
*(ULONGLONG*)((PUCHAR)RemoteBufferData + 44) = (ULONGLONG)&RemoteBufferData->CallComplete;

return RemoteBufferData;
}

return NULL;
}

开始APC注入

  1. 获取一个线程,取出EThread
  2. 插入线程的APC队列中
  3. 等待完成
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
NTSTATUS ApcInjection(IN PINJECTION_DATA InjectionData, IN HANDLE TargetProcessID)
{
//最后两个参数可以
NTSTATUS Status = STATUS_SUCCESS;
PETHREAD EThread = NULL;

// 获取活跃线程(主线程)
Status = SeLookupProcessThread(TargetProcessID, &EThread);

if (NT_SUCCESS(Status))
{
// 插入APC队列
Status = SeQueueUserApc(EThread, InjectionData, NULL, NULL, NULL, TRUE); //内核情景分析 358

// 等待完成
if (NT_SUCCESS(Status)) //该代码待定
{
LARGE_INTEGER Interval = { 0 };
Interval.QuadPart = -(5LL * 10 * 1000);

// 等待
for (ULONG i = 0; InjectionData->CallComplete != CALL_COMPLETE && i < 10000; i++)
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
}
}
else
DbgPrint("Failed To Locate Thread\r\n");

if (EThread)
ObDereferenceObject(EThread);

return Status;
}

获取活跃线程

  1. 获取进程的线程列表
  2. 过滤当前线程,并在SystemProcessInfo里取出一个线程ID获取EThread
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
58
59
60
61
62
63
64
65
66
67
68
NTSTATUS SeLookupProcessThread(IN HANDLE ProcessID, OUT PETHREAD* EThread)
{
NTSTATUS Status = STATUS_SUCCESS;
PVOID BufferData = ExAllocatePool(NonPagedPool, 1024 * 1024);
PSYSTEM_PROCESS_INFORMATION SystemProcessInfo = (PSYSTEM_PROCESS_INFORMATION)BufferData;

if (EThread == NULL)
return STATUS_INVALID_PARAMETER;

if (!SystemProcessInfo)
{
DbgPrint("Failed To Allocate Memory For Process List\r\n");
return STATUS_NO_MEMORY;
}

// 获取进程的线程列表
Status = ZwQuerySystemInformation(SystemProcessInformation, SystemProcessInfo, 1024 * 1024, NULL);
if (!NT_SUCCESS(Status))
{
ExFreePool(BufferData);
return Status;
}

// 找到目标线程
if (NT_SUCCESS(Status))
{
Status = STATUS_NOT_FOUND;
for (;;)
{
if (SystemProcessInfo->UniqueProcessId == ProcessID)
{
Status = STATUS_SUCCESS;
break;
}
else if (SystemProcessInfo->NextEntryOffset)
SystemProcessInfo = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)SystemProcessInfo + SystemProcessInfo->NextEntryOffset);
else
break;
}
}


if (NT_SUCCESS(Status))
{
Status = STATUS_NOT_FOUND;

for (ULONG i = 0; i < SystemProcessInfo->NumberOfThreads; i++)
{
// 跳过当前线程
if (
SystemProcessInfo->Threads[i].ClientId.UniqueThread == PsGetCurrentThread())
{
continue;
}

// 线程ID获取EThread
Status = PsLookupThreadByThreadId(SystemProcessInfo->Threads[i].ClientId.UniqueThread, EThread);
break;
}
}
else
DbgPrint("Failed To Locate Process\r\n");

if (BufferData)
ExFreePool(BufferData);

return Status;
}

插入目标线程APC队列

  1. 分配APC内存
  2. 初始化APC并分配执行和结束回调例程。APC回调例程中可以设置强制唤醒。执行例程就是之前的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
NTSTATUS SeQueueUserApc(IN PETHREAD EThread,IN PVOID StartRoutine,IN PVOID Argument1,IN PVOID Argument2,IN PVOID Argument3,
IN BOOLEAN IsForce)
{

if (EThread == NULL)
return STATUS_INVALID_PARAMETER;

// 分配APC内存
PKAPC PrepareApc = NULL;
PKAPC ExcuteApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));

if (ExcuteApc == NULL)
{
DbgPrint("Failed To Allocate APC\r\n");
return STATUS_NO_MEMORY;
}

// 一般APC 初始化
KeInitializeApc(
ExcuteApc, (PKTHREAD)EThread,
OriginalApcEnvironment, &KernelApcExcuteCallback,
NULL, (PKNORMAL_ROUTINE)(ULONG_PTR)StartRoutine, UserMode, Argument1);

// 强制APC 初始化
if (IsForce)
{
PrepareApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
KeInitializeApc(
PrepareApc, (PKTHREAD)EThread,
OriginalApcEnvironment, &KernelApcPrepareCallback,
NULL, NULL, KernelMode, NULL
);
}

// 插入APC队列
if (KeInsertQueueApc(ExcuteApc, Argument2, Argument3, 0))
{
if (IsForce && PrepareApc)
KeInsertQueueApc(PrepareApc, NULL, NULL, 0);

return STATUS_SUCCESS;
}
else
{
DbgPrint("Failed To Insert APC\r\n");

ExFreePool(ExcuteApc);

if (PrepareApc)
ExFreePool(ExcuteApc);

return STATUS_NOT_CAPABLE;
}
}

APC完成回调例程

一般情况

释放结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
VOID KernelApcExcuteCallback(
PKAPC Apc,
PKNORMAL_ROUTINE* NormalRoutine,
PVOID* NormalContext,
PVOID* SystemArgument1,
PVOID* SystemArgument2
)
{
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);

// 当前线程正在被释放
if (PsIsThreadTerminating(PsGetCurrentThread()))
*NormalRoutine = NULL;

// 适配WOW64
if (PsGetCurrentProcessWow64Process() != NULL)
PsWrapApcWow64Thread(NormalContext, (PVOID*)NormalRoutine);

// 释放APC结构
ExFreePool(Apc);
}

强制情况

强行唤醒线程执行APC,释放结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
VOID KernelApcPrepareCallback(
PKAPC Apc,
PKNORMAL_ROUTINE* NormalRoutine,
PVOID* NormalContext,
PVOID* SystemArgument1,
PVOID* SystemArgument2
)
{
UNREFERENCED_PARAMETER(NormalRoutine);
UNREFERENCED_PARAMETER(NormalContext);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);


KeTestAlertThread(UserMode); //向目标植入可提醒状态
ExFreePool(Apc);
}

驱动卸载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING DeviceLinkName;
PDEVICE_OBJECT v1 = NULL;
PDEVICE_OBJECT DeleteDeviceObject = NULL;
RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
IoDeleteSymbolicLink(&DeviceLinkName);
DeleteDeviceObject = DriverObject->DeviceObject;
while (DeleteDeviceObject != NULL)
{
v1 = DeleteDeviceObject->NextDevice;
IoDeleteDevice(DeleteDeviceObject);
DeleteDeviceObject = v1;
}
}

Ring3

主函数外的入口

  1. 输入进程ID
  2. 获取当前可执行文件的路径,并将DllName与路径拼接起来,得到完整的DLL路径
  3. 通过链接名打开设备对象
  4. 初始化结构,发送结构体和控制码
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
#define DEVICE_LINK_NAME    L"\\??\\APCInjectionLinkName"
#define CTL_APC_INJECTION \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)

typedef struct _INJECTION_INFORMATION_
{
wchar_t DllFullPath[MAX_PATH];// 当前DLL完整路径
ULONG TargetProcessID;// 目标进程ID
UCHAR IsUnlinkModule; // 是否卸载模块
}INJECTION_INFORMATION, *PINJECTION_INFORMATION;

VOID ApcInjection()
{
NTSTATUS Status;
ULONG TargetProcessID = 0;
cin >> TargetProcessID;// 输入进程ID

const wchar_t* DllName = L"Dll.dll";
wstring DllFullPath;
// 获取当前可执行文件的路径,并将DllName与路径拼接起来,得到完整的DLL路径
DllFullPath = GetExeDirectory() + L"\\" + DllName; //选择编译
CControlDevice Object;
// 打开设备对象
if (!Object.SeOpenDeviceObject(DEVICE_LINK_NAME))
{
return;
}
INJECTION_INFORMATION InjectionInfo = { 0 };
InjectionInfo.TargetProcessID = TargetProcessID;
InjectionInfo.IsUnlinkModule = TRUE;
wcscpy_s(InjectionInfo.DllFullPath, DllFullPath.c_str());

// 发送INJECTION_INFORMATION结构体和控制码
Status = Object.SeDeviceIoControl(&InjectionInfo,sizeof(INJECTION_INFORMATION),CTL_APC_INJECTION);
if (!NT_SUCCESS(Status))
{
std::cout << "CTL_APC_INJECTION Error\r\n";
}
else
{
std::cout << "CTL_APC_INJECTION Succeeded\r\n";
}

printf("Input AnyKey To Exit\r\n");

getchar();
getchar();

}

Ring3操作驱动方法类

1
2
3
4
5
6
7
8
9
10
class CControlDevice
{
public:
CControlDevice();
~CControlDevice();
BOOL CControlDevice::SeOpenDeviceObject(WCHAR* DeviceLinkName);
NTSTATUS CControlDevice::SeDeviceIoControl(PVOID BufferData, ULONG BufferLength, ULONG IoControlCode);
private:
HANDLE m_DeviceObject = INVALID_HANDLE_VALUE;
};

获取Exe的完整名字

获取当前第一模块(可执行文件)路径,在路径中去掉文件名,再做一些转换

1
2
3
4
5
6
7
wstring GetExeDirectory()
{
wchar_t BufferData[MAX_PATH] = { 0 };
DWORD BufferLength = ARRAYSIZE(BufferData);
GetModuleFileNameW(NULL, BufferData, BufferLength);
return GetParentDirectory(BufferData);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
wstring GetParentDirectory(const std::wstring& BufferData)
{
if (BufferData.empty())
return BufferData;

auto v1 = BufferData.rfind(L'\\');
if (v1 == BufferData.npos)
v1 = BufferData.rfind(L'/');

if (v1 != BufferData.npos)
return BufferData.substr(0, v1);
else
return BufferData;
}

打开设备对象

通过链接对象获取驱动设备对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BOOL CControlDevice::SeOpenDeviceObject(WCHAR* DeviceLinkName)
{
//通过LinkName 获得驱动设备对象
if (m_DeviceObject != INVALID_HANDLE_VALUE)
return TRUE;

// Try to open handle to existing driver
m_DeviceObject = CreateFileW(
DeviceLinkName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);

if (m_DeviceObject != INVALID_HANDLE_VALUE)
return TRUE;
return FALSE;
}

发送IO控制码

最重要的DeviceIoControl

1
2
3
4
5
6
7
8
9
10
11
12
13
NTSTATUS CControlDevice::SeDeviceIoControl(PVOID BufferData,ULONG BufferLength,ULONG IoControlCode)
{

DWORD ReturnLength = 0;

if (m_DeviceObject == INVALID_HANDLE_VALUE)
return STATUS_DEVICE_DOES_NOT_EXIST;

if (!DeviceIoControl(m_DeviceObject, IoControlCode, BufferData, BufferLength, NULL, 0, &ReturnLength, NULL))
return STATUS_UNSUCCESSFUL;

return STATUS_SUCCESS;
}

32位区别基本仅在ShellCode只有32位一种。