APC 基本概念及APC注入的实现(Ring3 + Ring0)—-实现
Ring3
- 原理
利用QueueUserAPC 函数把APC添加到指定可警告线程的APC队列。
其中QueueUserAPC 函数及APC函数声明如下:
DWORD WINAPI QueueUserAPC(
_In_ PAPCFUNC pfnAPC,
_In_ HANDLE hThread,
_In_ ULONG_PTR dwData
);
VOID CALLBACK APCProc(
_In_ ULONG_PTR dwParam
);
- 核心函数
BOOL MyQueueUserAPC(CONST WCHAR* szDllPath,DWORD ProcessId)
{
BOOL bRet = FALSE;
HANDLE hTargetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,ProcessId);
PVOID RemoteBufferBase = NULL;
if(RemoteBufferBase = VirtualAllocEx(hTargetProcessHandle, nullptr, (wcslen(szDllPath)+1)*sizeof(WCHAR), MEM_COMMIT, PAGE_EXECUTE_READWRITE))
{
if(WriteProcessMemory(hTargetProcessHandle, RemoteBufferBase, szDllPath, (wcslen(szDllPath)+1)*sizeof(WCHAR), NULL))
{
THREADENTRY32 te = { 0 };
te.dwSize = sizeof(te);
HANDLE hThreadSnap = CreateToolhelp32Snapshot ( TH32CS_SNAPTHREAD, 0 ) ;
HANDLE ThreadHandle = nullptr;
if (Thread32First(hThreadSnap,&te))
{
do
{
if (te.th32OwnerProcessID == ProcessId) // 找到属于我们进程的线程
{
if( ThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID))
{
if (QueueUserAPC((PAPCFUNC)LoadLibraryW, ThreadHandle, (ULONG_PTR)RemoteBufferBase) == 0)
{
printf("QueueUserAPC:%d\n",GetLastError());
continue;
}
bRet = TRUE;
printf("ThreadId:%d\n",te.th32ThreadID);
CloseHandle(ThreadHandle);
}
}
} while (Thread32Next(hThreadSnap,&te));
}
else
{
printf("ThreadFirst:%d\n",GetLastError());
}
} // WriteMemory
else
{
printf("WriteProcessMemory:%d\n",GetLastError());
}
} // VirtualAlloc
else
{
printf("VirtualAlloc:%d\n",GetLastError());
}
return bRet;
}
Ring0
- 原理
其实Ring0 注入的实现原理与Ring3 是一样的,不过Ring0 需要直接操作系统结构体的成员,需要在应用层申请内存空间等等操作。具体实现的话,可以有两种思路,一种是有应用层的控制,这种的话,其实可以在应用层做好大部分的工作,然后Ring0 层单纯的进行APC 例程的挂载就可以了。第二种就是纯Ring0,需要自己做全部的事情。
单纯的Ring0 实现
- 声明部分
#ifndef CXX_PURERING0APCINJECT_H
#define CXX_PURERING0APCINJECT_H
#include <ntifs.h>
#include <devioctl.h>
#define ActiveProcessLinks_OffsetOf_Eprocess 0x88
#define ThreadListHead_OffsetOf_Eprocess 0x190
#define ThreadListEntry_OffsetOf_Ethread 0x22c
#define Alertable_OffsetOf_Kthread_Ethread 0x164
#define UserApcPending_OffsetOf_KapcState_Kthread_Ethread (0x16 + 0x34)
VOID DriverUnload(IN PDRIVER_OBJECT pDriverObj);
void PureRing0ApcInject(CHAR* szDllPath,char* szTargetProcess);
extern PCHAR PsGetProcessImageFileName(PEPROCESS EProcess);
NTSTATUS FindTargetProcessAndThread(char* szProcessName,PEPROCESS *ppProcess,PETHREAD * ppThread);
VOID ApcRoutine(IN PCHAR DllPath,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2);
VOID ApcRountieEnd(VOID);
typedef enum _KAPC_ENVIRONMENT
{
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment
}KAPC_ENVIRONMENT;
NTKERNELAPI
VOID
KeInitializeApc(OUT PRKAPC Apc,
IN PRKTHREAD Thread,
IN KAPC_ENVIRONMENT Environment,
IN PKKERNEL_ROUTINE KernelRoutine,
IN OPTIONAL PKRUNDOWN_ROUTINE RundownRoutine,
IN OPTIONAL PKNORMAL_ROUTINE NormalRoutine,
IN OPTIONAL KPROCESSOR_MODE ProcessorMode,
IN OPTIONAL PVOID NormalContext);
NTKERNELAPI
VOID
KeInsertQueueApc(IN PKAPC Apc,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2,
IN KPRIORITY Increment);
VOID
KernelApcRoutine(PKAPC Apc,
PKNORMAL_ROUTINE *NormAlRoutine,
IN OUT PVOID *NormAlContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2);
#endif
- 实现部分—-查找目标进程和线程
NTSTATUS FindTargetProcessAndThread(char* szProcessName,PEPROCESS *ppProcess,PETHREAD * ppThread)
{
NTSTATUS bRet = -1;
PEPROCESS Current = NULL;
PLIST_ENTRY ActiveProcessLinks = NULL;
PCHAR ProcessName = NULL;
PLIST_ENTRY ThreadListHead = NULL;
PLIST_ENTRY ThreadListEntry = NULL;
PETHREAD CurrentThread = NULL;
PETHREAD BeginThread = NULL;
*ppProcess = NULL;
*ppThread = NULL;
do
{
Current = PsGetCurrentProcess(); //System
while (1)
{
ProcessName = PsGetProcessImageFileName(Current);
if (_stricmp(ProcessName,szProcessName) == 0)
{
*ppProcess = Current;
break;
}
ActiveProcessLinks = ((PLIST_ENTRY)((ULONG)Current + ActiveProcessLinks_OffsetOf_Eprocess));
Current = (PEPROCESS)(((ULONG)ActiveProcessLinks->Flink) - ActiveProcessLinks_OffsetOf_Eprocess);
if (Current == PsGetCurrentProcess())
{
break;
}
}
if(ppProcess == NULL)
{
break;
}
ThreadListHead = (PLIST_ENTRY)((ULONG)*ppProcess + ThreadListHead_OffsetOf_Eprocess);
CurrentThread = (PETHREAD)((ULONG)ThreadListHead->Flink - ThreadListEntry_OffsetOf_Ethread);
BeginThread = CurrentThread;
while (1)
{
DbgPrint("CurrentThread:%p\n",CurrentThread);
// 找到目标线程,即可警告的等待状态的线程
if (*(unsigned char*)((ULONG)CurrentThread + Alertable_OffsetOf_Kthread_Ethread) == 1)
{
*ppThread = CurrentThread;
bRet = STATUS_SUCCESS;
break;
}
ThreadListEntry = (PLIST_ENTRY)((ULONG)CurrentThread + ThreadListEntry_OffsetOf_Ethread);
CurrentThread = (PETHREAD)((ULONG)ThreadListEntry->Flink - ThreadListEntry_OffsetOf_Ethread);
if (CurrentThread == BeginThread)
{
break;
}
}
} while (0);
return bRet;
}
- 实现部分—–完成注入操作
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT pDriverObj, IN PUNICODE_STRING pRegistryString)
{
pDriverObj->DriverUnload = DriverUnload; // DriverUnload 函数什么都不做
PureRing0ApcInject("D:\\SimpleDll.dll","explorer.exe");// 直接进行注入操作
return STATUS_SUCCESS;
}
// 这个函数中不能有相对跳转之类的函数,必须是EIP 无关的指令,我们采用纯手写
__declspec(naked)
VOID ApcRoutine(IN PCHAR DllPath,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
__asm
{
// 自己去实现堆栈平衡
push ebp;
mov ebp,esp;
pushad;
jmp end;
start:
pop edx; // edx = "GetProcAddress
// 下面的操作通过PEB->InInitializationOrderModuleList->_LDR_DATA_TABLE_ENTRY->DllBase
// 得到进程第一模块,然后遍历模块找到kernel32.dll 基地址(X86 下模块名为小写双子,本机Win10 是大写双子,需要额外注意目标进程的模块名)
mov eax,fs:[0x30]; // PEB
mov eax,[eax + 0x0C]; // _PEB_LDR_DATA
/*
0:001> .process
Implicit process is now 7ffd9000
0:001> dt _peb 7ffd9000
ntdll!_PEB
...
+0x00c Ldr : 0x001a1e90 _PEB_LDR_DATA
*/
mov eax,[eax + 0x1C];
/*
0:001> dt _PEB_LDR_DATA 0x001a1e90
ntdll!_PEB_LDR_DATA
...
+0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x1a1f28 - 0x1a3040 ]
...
kd> dt _LDR_DATA_TABLE_ENTRY
nt!_LDR_DATA_TABLE_ENTRY
...
+0x010 InInitializationOrderLinks : _LIST_ENTRY
+0x018 DllBase : Ptr32 Void
...
*/
mov eax,[eax]; // 第一个模块的ListEntry 结构的地址
search_Kernel32Dll:
push edi; // AddressOfNames
push ecx; // 将我们函数个数压入栈中保存
mov edi,dword ptr [eax+20h] // 模块名
lea esi,[edx+0x1C]; // 自己构建的"GetProcAddress"的函数地址
mov ecx,0x0000000C
repe cmps word ptr[esi],word ptr[edi];
je found_kernel32
mov eax,[eax]
pop ecx;
pop edi;
loop search_Kernel32Dll;
found_kernel32:
pop ecx;
pop edi;
mov ebx,[eax+8h]
mov esi,dword ptr[ebx + 0x3C];
mov esi,dword ptr[ebx + esi + 0x78]; // 导出目录 RVA
add esi,ebx; // 导出目录
mov edi,dword ptr[esi + 0x20]; // AddressOfNames RVA
add edi,ebx; // AddressOfNames
mov ecx,dword ptr [esi+0x14]; // NumberOfFunctions
push ebp; // 保存当前堆栈
xor ebp,ebp;
push esi; // 导出目录
search_GetProcAddress:
push edi; // AddressOfNames
push ecx; // 将我们函数个数压入栈中保存
mov edi,dword ptr[edi]; // AddressOfNames[i] RVA
add edi,ebx; // 函数名
mov esi,edx; // 自己构建的"GetProcAddress"的函数地址
mov ecx,0xE;
repe cmps byte ptr[esi],byte ptr[edi];
je found;
pop ecx;
pop edi;
add edi,4;
inc ebp; // 保存一个索引
loop search_GetProcAddress;
found:
pop ecx;
pop edi;
pop esi; // 导出目录
mov ecx,ebp;
//sub ecx,dword ptr[esi + 0x10] // ecx = ImageExportDirectory->Base
mov eax,dword ptr[esi + 0x24]; // AddressOfNameOrdinals RVA
add eax,ebx; // AddressOfNameOrdinals
shl ecx,1 // word
add eax,ecx
xor ecx,ecx
mov cx,word ptr[eax] // Index;
mov eax,dword ptr[esi+1Ch] // AddressOfFunctions RVA
add eax,ebx; // AddressOfFunctions
shl ecx,2;
add eax,ecx;
mov eax,dword ptr[eax]; // 函数地址 RVA
add eax,ebx;
pop ebp; // 恢复堆栈
mov esi,edx;
add esi,0xF; // "LoadLibraryA"
push esi;
push ebx; //GetProcAddress(HMODULE,FuncName)
call eax;
push DllPath;
call eax;
mov eax , DllPath
sub eax, 1
mov byte ptr [eax],1
popad;
mov esp,ebp;
pop ebp;
ret;
end:
call start; // 这个操作后[esp] 为 下面字符串的地址
__emit 'G'
__emit 'e'
__emit 't'
__emit 'P'
__emit 'r'
__emit 'o'
__emit 'c'
__emit 'A'
__emit 'd'
__emit 'd'
__emit 'r'
__emit 'e'
__emit 's'
__emit 's'
__emit 0 // + 14
__emit 'L'
__emit 'o'
__emit 'a'
__emit 'd'
__emit 'L'
__emit 'i'
__emit 'b'
__emit 'r'
__emit 'a'
__emit 'r'
__emit 'y'
__emit 'A'
__emit 0 // + 27
__emit 'k'
__emit 0
__emit 'e'
__emit 0
__emit 'r'
__emit 0
__emit 'n'
__emit 0
__emit 'e'
__emit 0
__emit 'l'
__emit 0
__emit '3'
__emit 0
__emit '2'
__emit 0
__emit '.'
__emit 0
__emit 'd'
__emit 0
__emit 'l'
__emit 0
__emit 'l'
__emit 0
__emit 0
__emit 0
}
}
// 单传的为了得到代码段的大小
__declspec(naked)
VOID ApcRountieEnd(VOID)
{
}
void PureRing0ApcInject(CHAR* szDllPath,char* szTargetProcess)
{
PEPROCESS pTargetProcess = NULL;
PETHREAD pTargetThread = NULL;
ULONG_PTR dwApcInjectCodeSize = 0;
ULONG_PTR dwApcTotalSize = 0;
ULONG size = 0;
ULONG_PTR lpTargetAddress = NULL;
PKEVENT pKevent = NULL;
PKAPC pKapc = NULL;
int iCount = 0;
LARGE_INTEGER delay;
DbgPrint("TargetProcess:%s\nDllPath:%S\n",szTargetProcess,szDllPath);
do
{
if(NT_SUCCESS(FindTargetProcessAndThread(szTargetProcess,&pTargetProcess,&pTargetThread)))
{
DbgPrint("找到了目标进程和目标线程:%p\t%p\n",pTargetProcess,pTargetThread);
dwApcInjectCodeSize = (ULONG_PTR)ApcRountieEnd - (ULONG_PTR)ApcRoutine;
dwApcTotalSize = dwApcInjectCodeSize + (strlen(szDllPath)+1) * sizeof(char) + 1;
dwApcTotalSize = ((dwApcTotalSize + 64 * 1024 - 1) / (64 * 1024) )* (64 * 1024);// 大小必须是64KB 对齐的,否则蓝屏
DbgPrint("代码大小:%d\n总大小:%d\n",dwApcInjectCodeSize,dwApcTotalSize);
pKevent = ExAllocatePool(NonPagedPool,sizeof(KEVENT));
KeAttachProcess(pTargetProcess);
if(!NT_SUCCESS(ZwAllocateVirtualMemory(NtCurrentProcess(),&lpTargetAddress,0,dwApcTotalSize,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE)))
{
DbgPrint("申请内存失败\n");
KeDetachProcess();
break;
}
DbgPrint("申请内存成功:%p\n",lpTargetAddress);
memcpy((void*)lpTargetAddress,(void*)ApcRoutine,dwApcInjectCodeSize);
strcpy((CHAR*)(lpTargetAddress + dwApcInjectCodeSize + 1),szDllPath);
*(char*)(lpTargetAddress + dwApcInjectCodeSize) = 0;// 这个字节用于标志APC 例程是否执行完毕
*(char*)(lpTargetAddress + dwApcInjectCodeSize + strlen(szDllPath) + 1) = 0;
DbgPrint("%s\n",lpTargetAddress + dwApcInjectCodeSize + 1);
DbgPrint("KeInitializeApc:%p KeInsertQueueApc:%p\n",(void*)KeInitializeApc,(void*)KeInsertQueueApc);
DbgPrint("下面将进行注入\n");
DbgPrint("KeInsertQueueApc:%p",(ULONG_PTR)KeInsertQueueApc);
KeInitializeEvent(pKevent,NotificationEvent,FALSE);
//将 KAPC_STATE->UserApcPending = TRUE,这样才可以进行注入操作
*(unsigned char*)((ULONG)pTargetThread + UserApcPending_OffsetOf_KapcState_Kthread_Ethread) = 1;
pKapc=(PKAPC)ExAllocatePool(NonPagedPool,sizeof(KAPC));
KeInitializeApc(pKapc,
pTargetThread, //目标进程主线程
OriginalApcEnvironment, //目标apcz状态
KernelApcRoutine, //内核apc总入口
NULL, //Rundown Rounine=NULL
(PKNORMAL_ROUTINE)(lpTargetAddress), //用户空间的总apc
UserMode, //插入到用户apc队列
(PVOID)(lpTargetAddress + dwApcInjectCodeSize + 1)); // 自己的apc队列
// 插入apc队列
KeInsertQueueApc(pKapc,pKevent,NULL,IO_NO_INCREMENT);
// 注入成功后等待该事件才能退出
KeWaitForSingleObject(
pKevent,
Executive,
KernelMode,
FALSE,
NULL
);
delay.QuadPart = -100*10000;
while(*(char*)(lpTargetAddress + dwApcInjectCodeSize) == 0)
{
DbgPrint("%d\t等待APC 例程执行完毕,可以释放内存了\n",iCount++);
KeDelayExecutionThread(KernelMode,FALSE,&delay); //等待apc执行
}
DbgPrint("注入成功,退出驱动\r\n");
size = 0;
// 一定要释放内存,否则当进程退出的时候程序将崩溃
ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&lpTargetAddress,&size,MEM_RELEASE);
KeDetachProcess();
// 释放pKevent 的内存
ExFreePool(pKevent);
}
else
{
if(pTargetProcess == NULL)
{
DbgPrint("没有找到 EPROCESS\r\n");
}
if(pTargetThread == NULL)
{
DbgPrint("没有找到 ETHREAD\r\n");
}
}
} while (0);
}
// APC 执行成功将执行该函数
VOID
KernelApcRoutine(PKAPC Apc,
PKNORMAL_ROUTINE *NormAlRoutine,
IN OUT PVOID *NormAlContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2)
{
PKEVENT pEvent;
// 释放APC 触发事件
pEvent = (PKEVENT)*SystemArgument1;
KeSetEvent(pEvent,IO_NO_INCREMENT,FALSE);
ExFreePool(Apc);
DbgPrint("进入到KernelApcRoutine 函数\n");
}
VOID
DriverUnload(IN PDRIVER_OBJECT pDriverObj)
{
return;
}
包含Ring3 控制的 Ring0 层实现
Ring3 层代码
#include <iostream>
#include <Windows.h>
#include <WinIoCtl.h>
using namespace std;
#define LINK_NAME L"\\\\.\\DriverLinkAPC"
#define CTL_KEINJECTAPC \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)
typedef struct _INJECT_INFO
{
ULONG ProcessId;
char DllName[1024];
}INJECT_INFO,*PINJECT_INFO;
int main()
{
HANDLE hFile;
INJECT_INFO InjectInfo;
hFile=CreateFileW(LINK_NAME,
GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,0,NULL);
if(hFile==INVALID_HANDLE_VALUE)
{
printf("\nError: Unable to connect to the driver (%d)\n",GetLastError());
getchar();
getchar();
return -1;
}
memset(&InjectInfo,0,sizeof(INJECT_INFO));
scanf("%d",&(InjectInfo.ProcessId));
scanf("%s",InjectInfo.DllName);
DWORD dwReturnSize = 0;
DWORD dwRet = 0;
dwRet = DeviceIoControl(hFile,CTL_KEINJECTAPC, //
&InjectInfo,
sizeof(INJECT_INFO),
NULL,
NULL,
&dwReturnSize,
NULL);
CloseHandle(hFile);
return 0;
}
Rin0 层代码
- 声明部分
#ifndef CXX_APCINJECTWITHRING3_H
#define CXX_APCINJECTWITHRING3_H
#include <ntifs.h>
#include <devioctl.h>
typedef struct _INJECT_INFO
{
ULONG ProcessId;
char szDllName[1024];
}INJECT_INFO,*PINJECT_INFO;
#define DEVICE_NAME L"\\Device\\DriverDeviceAPC"
#define LINK_NAME L"\\DosDevices\\DriverLinkAPC"
#define CTL_KEINJECTAPC \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)
NTSTATUS DriverDispatch(PDEVICE_OBJECT DeviceObject,PIRP Irp);
#define ActiveProcessLinks_OffsetOf_Eprocess 0x88
#define ThreadListHead_OffsetOf_Eprocess 0x190
#define ThreadListEntry_OffsetOf_Ethread 0x22c
#define Alertable_OffsetOf_Kthread_Ethread 0x164
#define UserApcPending_OffsetOf_KapcState_Kthread_Ethread (0x16 + 0x34)
VOID DriverUnload(IN PDRIVER_OBJECT pDriverObj);
NTSTATUS DefaultPassThrough(PDEVICE_OBJECT DeviceObject,PIRP Irp);
NTSTATUS InjectApcWithRing3(PINJECT_INFO pInjectInfor);
extern PCHAR PsGetProcessImageFileName(PEPROCESS EProcess);
NTSTATUS FindTargetProcessAndThread(ULONG dwProcessId,PEPROCESS *ppProcess,PETHREAD * ppThread);
VOID ApcRoutine(IN PCHAR DllPath,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2);
VOID ApcRountieEnd(VOID);
typedef enum _KAPC_ENVIRONMENT
{
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment
}KAPC_ENVIRONMENT;
NTKERNELAPI
VOID
KeInitializeApc(OUT PRKAPC Apc,
IN PRKTHREAD Thread,
IN KAPC_ENVIRONMENT Environment,
IN PKKERNEL_ROUTINE KernelRoutine,
IN OPTIONAL PKRUNDOWN_ROUTINE RundownRoutine,
IN OPTIONAL PKNORMAL_ROUTINE NormalRoutine,
IN OPTIONAL KPROCESSOR_MODE ProcessorMode,
IN OPTIONAL PVOID NormalContext);
NTKERNELAPI
VOID
KeInsertQueueApc(IN PKAPC Apc,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2,
IN KPRIORITY Increment);
VOID
KernelApcRoutine(PKAPC Apc,
PKNORMAL_ROUTINE *NormAlRoutine,
IN OUT PVOID *NormAlContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2);
#endif
实现部分
#ifndef CXX_APCINJECTWITHRING3_H
# include "ApcInjectWithRing3.h"
#endif
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING pRegistryPath)
{
NTSTATUS Status;
PDEVICE_OBJECT DeviceObject;
UNICODE_STRING uniDeviceName;
UNICODE_STRING uniLinkName;
int i;
RtlInitUnicodeString(&uniDeviceName,DEVICE_NAME);
RtlInitUnicodeString(&uniLinkName,LINK_NAME);
for (i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++)
{
DriverObject->MajorFunction[i] = DefaultPassThrough;
}
DriverObject->DriverUnload = DriverUnload;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDispatch;
//创建设备对象
Status = IoCreateDevice(DriverObject,0,&uniDeviceName,FILE_DEVICE_UNKNOWN,0,FALSE,&DeviceObject);
if (!NT_SUCCESS(Status))
{
DbgPrint("IoCreateDevice 失败\n");
return Status;
}
Status = IoCreateSymbolicLink(&uniLinkName,&uniDeviceName);
if (!NT_SUCCESS(Status))
{
DbgPrint("IoCreateSymbolicLink 失败\n");
IoDeleteDevice(DeviceObject);
return Status;
}
return STATUS_SUCCESS;
}
// 这个函数中不能有相对跳转之类的函数,必须是EIP 无关的指令,我们采用纯手写
__declspec(naked)
VOID ApcRoutine(IN PCHAR DllPath,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
__asm
{
// 自己去实现堆栈平衡
push ebp;
mov ebp,esp;
pushad;
jmp end;
start:
pop edx; // edx = "GetProcAddress
// 下面的操作通过PEB->InInitializationOrderModuleList->_LDR_DATA_TABLE_ENTRY->DllBase
// 得到进程第一模块即kernel32.dll 的基地址
mov eax,fs:[0x30]; // PEB
mov eax,[eax + 0x0C]; // _PEB_LDR_DATA
/*
0:001> .process
Implicit process is now 7ffd9000
0:001> dt _peb 7ffd9000
ntdll!_PEB
...
+0x00c Ldr : 0x001a1e90 _PEB_LDR_DATA
*/
mov eax,[eax + 0x1C];
/*
0:001> dt _PEB_LDR_DATA 0x001a1e90
ntdll!_PEB_LDR_DATA
...
+0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x1a1f28 - 0x1a3040 ]
...
kd> dt _LDR_DATA_TABLE_ENTRY
nt!_LDR_DATA_TABLE_ENTRY
...
+0x010 InInitializationOrderLinks : _LIST_ENTRY
+0x018 DllBase : Ptr32 Void
...
*/
mov eax,[eax]; // 第一个模块的ListEntry 结构的地址
search_Kernel32Dll:
push edi; // AddressOfNames
push ecx; // 将我们函数个数压入栈中保存
mov edi,dword ptr [eax+20h] // 模块名
lea esi,[edx+0x1C]; // 自己构建的"GetProcAddress"的函数地址
mov ecx,0x0000000C
repe cmps word ptr[esi],word ptr[edi];
je found_kernel32
mov eax,[eax]
pop ecx;
pop edi;
loop search_Kernel32Dll;
found_kernel32:
pop ecx;
pop edi;
mov ebx,[eax+8h]
mov esi,dword ptr[ebx + 0x3C];
mov esi,dword ptr[ebx + esi + 0x78]; // 导出目录 RVA
add esi,ebx; // 导出目录
mov edi,dword ptr[esi + 0x20]; // AddressOfNames RVA
add edi,ebx; // AddressOfNames
mov ecx,dword ptr [esi+0x14]; // NumberOfFunctions
push ebp; // 保存当前堆栈
xor ebp,ebp;
push esi; // 导出目录
search_GetProcAddress:
push edi; // AddressOfNames
push ecx; // 将我们函数个数压入栈中保存
mov edi,dword ptr[edi]; // AddressOfNames[i] RVA
add edi,ebx; // 函数名
mov esi,edx; // 自己构建的"GetProcAddress"的函数地址
mov ecx,0xE;
repe cmps byte ptr[esi],byte ptr[edi];
je found;
pop ecx;
pop edi;
add edi,4;
inc ebp; // 保存一个索引
loop search_GetProcAddress;
found:
pop ecx;
pop edi;
pop esi; // 导出目录
mov ecx,ebp;
//sub ecx,dword ptr[esi + 0x10] // ecx = ImageExportDirectory->Base
mov eax,dword ptr[esi + 0x24]; // AddressOfNameOrdinals RVA
add eax,ebx; // AddressOfNameOrdinals
shl ecx,1 // word
add eax,ecx
xor ecx,ecx
mov cx,word ptr[eax] // Index;
mov eax,dword ptr[esi+1Ch] // AddressOfFunctions RVA
add eax,ebx; // AddressOfFunctions
shl ecx,2;
add eax,ecx;
mov eax,dword ptr[eax]; // 函数地址 RVA
add eax,ebx;
pop ebp; // 恢复堆栈
mov esi,edx;
add esi,0xF; // "LoadLibraryA"
push esi;
push ebx; //GetProcAddress(HMODULE,FuncName)
call eax;
push DllPath;
call eax;
mov eax , DllPath
sub eax, 1
mov byte ptr [eax],1
popad;
mov esp,ebp;
pop ebp;
ret;
end:
call start; // 这个操作后[esp] 为 下面字符串的地址
__emit 'G'
__emit 'e'
__emit 't'
__emit 'P'
__emit 'r'
__emit 'o'
__emit 'c'
__emit 'A'
__emit 'd'
__emit 'd'
__emit 'r'
__emit 'e'
__emit 's'
__emit 's'
__emit 0 // + 14
__emit 'L'
__emit 'o'
__emit 'a'
__emit 'd'
__emit 'L'
__emit 'i'
__emit 'b'
__emit 'r'
__emit 'a'
__emit 'r'
__emit 'y'
__emit 'A'
__emit 0 // + 27
__emit 'k'
__emit 0
__emit 'e'
__emit 0
__emit 'r'
__emit 0
__emit 'n'
__emit 0
__emit 'e'
__emit 0
__emit 'l'
__emit 0
__emit '3'
__emit 0
__emit '2'
__emit 0
__emit '.'
__emit 0
__emit 'd'
__emit 0
__emit 'l'
__emit 0
__emit 'l'
__emit 0
__emit 0
__emit 0
}
}
__declspec(naked)
VOID ApcRountieEnd(VOID)
{
}
NTSTATUS DefaultPassThrough(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS InjectApcWithRing3(PINJECT_INFO pInjectInfor)
{
PEPROCESS pTargetProcess = NULL;
PETHREAD pTargetThread = NULL;
ULONG_PTR dwApcInjectCodeSize = 0;
ULONG_PTR dwApcTotalSize = 0;
ULONG size = 0;
ULONG_PTR lpTargetAddress = NULL;
PKEVENT pKevent = NULL;
PKAPC pKapc = NULL;
int iCount = 0;
LARGE_INTEGER delay;
do
{
if(NT_SUCCESS(FindTargetProcessAndThread(pInjectInfor->ProcessId,&pTargetProcess,&pTargetThread)))
{
DbgPrint("找到了目标进程和目标线程:%p\t%p\n",pTargetProcess,pTargetThread);
dwApcInjectCodeSize = (ULONG_PTR)ApcRountieEnd - (ULONG_PTR)ApcRoutine;
dwApcTotalSize = dwApcInjectCodeSize + (strlen(pInjectInfor->szDllName)+1) * sizeof(char) + 1;
dwApcTotalSize = ((dwApcTotalSize + 64 * 1024 - 1) / (64 * 1024) )* (64 * 1024);// 大小必须是64KB 对齐的,否则蓝屏
DbgPrint("代码大小:%d\n总大小:%d\n",dwApcInjectCodeSize,dwApcTotalSize);
pKevent = ExAllocatePool(NonPagedPool,sizeof(KEVENT));
KeAttachProcess(pTargetProcess);
if(!NT_SUCCESS(ZwAllocateVirtualMemory(NtCurrentProcess(),&lpTargetAddress,0,dwApcTotalSize,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE)))
{
DbgPrint("申请内存失败\n");
KeDetachProcess();
break;
}
DbgPrint("申请内存成功:%p\n",lpTargetAddress);
memcpy((void*)lpTargetAddress,(void*)ApcRoutine,dwApcInjectCodeSize);
strcpy((CHAR*)(lpTargetAddress + dwApcInjectCodeSize + 1),pInjectInfor->szDllName);
*(char*)(lpTargetAddress + dwApcInjectCodeSize) = 0;
*(char*)(lpTargetAddress + dwApcInjectCodeSize + strlen(pInjectInfor->szDllName) + 1) = 0;
DbgPrint("%s\n",lpTargetAddress + dwApcInjectCodeSize + 1);
DbgPrint("KeInitializeApc:%p KeInsertQueueApc:%p\n",(void*)KeInitializeApc,(void*)KeInsertQueueApc);
DbgPrint("下面将进行注入\n");
DbgPrint("KeInsertQueueApc:%p",(ULONG_PTR)KeInsertQueueApc);
KeInitializeEvent(pKevent,NotificationEvent,FALSE);
// KAPC_STATE->UserApcPending = TRUE,这样才可以进行注入操作
*(unsigned char*)((ULONG)pTargetThread + UserApcPending_OffsetOf_KapcState_Kthread_Ethread) = 1;
pKapc=(PKAPC)ExAllocatePool(NonPagedPool,sizeof(KAPC));
KeInitializeApc(pKapc,
pTargetThread, //目标进程主线程
OriginalApcEnvironment, //目标apcz状态
KernelApcRoutine, //内核apc总入口
NULL, //Rundown Rounine=NULL
(PKNORMAL_ROUTINE)(lpTargetAddress), //用户空间的总apc
UserMode, //插入到用户apc队列
(PVOID)(lpTargetAddress + dwApcInjectCodeSize + 1)); // 自己的apc队列
// 插入apc队列
KeInsertQueueApc(pKapc,pKevent,NULL,IO_NO_INCREMENT);
KeWaitForSingleObject(
pKevent,
Executive,
KernelMode,
FALSE,
NULL
);
delay.QuadPart = -100*10000;
while(*(char*)(lpTargetAddress + dwApcInjectCodeSize) == 0)
{
DbgPrint("%d\t等待APC 例程执行完毕,可以释放内存了\n",iCount++);
KeDelayExecutionThread(KernelMode,FALSE,&delay); //等待apc执行
}
DbgPrint("注入成功,退出驱动\r\n");
size = 0;
ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&lpTargetAddress,&size,MEM_RELEASE);
KeDetachProcess();
ExFreePool(pKevent);
return STATUS_SUCCESS;
}
else
{
if(pTargetProcess == NULL)
{
DbgPrint("没有找到 EPROCESS\r\n");
}
if(pTargetThread == NULL)
{
DbgPrint("没有找到 ETHREAD\r\n");
}
}
} while (0);
return -1;
}
VOID
KernelApcRoutine(PKAPC Apc,
PKNORMAL_ROUTINE *NormAlRoutine,
IN OUT PVOID *NormAlContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2)
{
PKEVENT pEvent;
pEvent = (PKEVENT)*SystemArgument1;
KeSetEvent(pEvent,IO_NO_INCREMENT,FALSE);
ExFreePool(Apc);
DbgPrint("进入到KernelApcRoutine 函数\n");
}
VOID
DriverUnload(IN PDRIVER_OBJECT pDriverObj)
{
UNICODE_STRING uniLinkName;
PDEVICE_OBJECT CurrentDeviceObject;
PDEVICE_OBJECT NextDeviceObject;
RtlInitUnicodeString(&uniLinkName,LINK_NAME);
IoDeleteSymbolicLink(&uniLinkName);
if (pDriverObj->DeviceObject!=NULL)
{
CurrentDeviceObject = pDriverObj->DeviceObject;
while(CurrentDeviceObject!=NULL)
{
NextDeviceObject = CurrentDeviceObject->NextDevice;
IoDeleteDevice(CurrentDeviceObject);
CurrentDeviceObject = NextDeviceObject;
}
}
DbgPrint("UnloadDriver\r\n");
}
NTSTATUS FindTargetProcessAndThread(ULONG dwProcessId,PEPROCESS *ppProcess,PETHREAD * ppThread)
{
NTSTATUS bRet = -1;
PEPROCESS Current = NULL;
PLIST_ENTRY ActiveProcessLinks = NULL;
PCHAR ProcessName = NULL;
PLIST_ENTRY ThreadListHead = NULL;
PLIST_ENTRY ThreadListEntry = NULL;
PETHREAD CurrentThread = NULL;
PETHREAD BeginThread = NULL;
*ppProcess = NULL;
*ppThread = NULL;
do
{
if(!NT_SUCCESS(PsLookupProcessByProcessId(dwProcessId,ppProcess)))
{
DbgPrint("找不到目标进程\n");
break;
}
DbgPrint("目标进程名称:%s\n",(ULONG_PTR)*ppProcess + 0x174);
ThreadListHead = (PLIST_ENTRY)((ULONG)*ppProcess + ThreadListHead_OffsetOf_Eprocess);
CurrentThread = (PETHREAD)((ULONG)ThreadListHead->Flink - ThreadListEntry_OffsetOf_Ethread);
BeginThread = CurrentThread;
while (1)
{
DbgPrint("CurrentThread:%p\n",CurrentThread);
if (*(unsigned char*)((ULONG)CurrentThread + Alertable_OffsetOf_Kthread_Ethread) == 1)
{
*ppThread = CurrentThread;
bRet = STATUS_SUCCESS;
break;
}
ThreadListEntry = (PLIST_ENTRY)((ULONG)CurrentThread + ThreadListEntry_OffsetOf_Ethread);
CurrentThread = (PETHREAD)((ULONG)ThreadListEntry->Flink - ThreadListEntry_OffsetOf_Ethread);
if (CurrentThread == BeginThread)
{
break;
}
}
} while (0);
return bRet;
}
NTSTATUS DriverDispatch(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
PIO_STACK_LOCATION io;
PINJECT_INFO InjectInfo;
NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpSp;
PVOID InputBuffer = NULL;
PVOID OutputBuffer = NULL;
ULONG_PTR InputSize = 0;
ULONG_PTR OutputSize = 0;
ULONG_PTR IoControlCode = 0;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
InputBuffer = OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
InputSize = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
OutputSize = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
switch(IoControlCode)
{
case CTL_KEINJECTAPC:
InjectInfo=(PINJECT_INFO)InputBuffer;
if(!InjectInfo)
{
Status=STATUS_INSUFFICIENT_RESOURCES;
break;
}
if(!InjectApcWithRing3(InjectInfo))
{
Status=STATUS_UNSUCCESSFUL;
break;
}
Status=STATUS_SUCCESS;
Irp->IoStatus.Information=0;
break;
default:
Status=STATUS_INVALID_DEVICE_REQUEST;
break;
}
Irp->IoStatus.Status=Status;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return Status;
}