注入小结

平常用的最多的dll注入技术就是远程线程,刚刚逛看雪,看到有人写的面试的时候被问到的问题,其中就有dll注入的方法,我突然想到我开始面试的时候也被问了dll注入的方法,当时也是就只知道一个远程线程,答的也不好,然后就想把一些注入技术写个总结。接下来讲的注入技术,有ring3层的lld的远程线程和apc注入,还有ring0的apc注入,此外还有更为隐蔽的代码注入。

 先写最广泛的,也是相对简单的注入方式,远程线程。

一 ring3 dll的远程线程

    我写的涉及到x86和x64的注入,因为x64的系统本身增加了较多权限的校验,需要进行提权处理。所以先进行系统版本的校验:

typedef enum  _WIN_VERSION
{
    WindowsNT,
    Windows2000,
    WindowsXP,
    Windows2003,
    WindowsVista,
    Windows7,
    Windows8,
    WinUnknown
}WIN_VERSION;

WIN_VERSION  GetWindowsVersion()
{
    OSVERSIONINFOEX    OsVerInfoEx;
    OsVerInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    GetVersionEx((OSVERSIONINFO *)&OsVerInfoEx); // 注意转换类型
    switch (OsVerInfoEx.dwPlatformId)
    {
    case VER_PLATFORM_WIN32_NT:
        {
            if (OsVerInfoEx.dwMajorVersion <= 4 )
            {
                return WindowsNT;
            }
            if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 0)
            {
                return Windows2000;
            }

            if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 1)
            {
                return WindowsXP;
            }
            if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 2)
            {
                return Windows2003;
            }
            if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 0)
            {
                return WindowsVista;
            }

            if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 1)
            {
                return Windows7;
            }
            if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 2 )
            {
                return Windows8;
            }
            break;
        }

    default:
        {
            return WinUnknown;
        }
    }

}
获得系统版本

   x86和x64的注入最主要的不同点就是对于x64的提权处理,接下来先讲x64的提权处理。

   x64的提权主要就是用到了ntdll.dll中未导出的一个函数,RtlAdjustPrivilege(),这个函数的作用是很大的,之前的瞬间关机的代码就是用了这个函数进行提权。

/*
   .常量 SE_BACKUP_PRIVILEGE, "17", 公开
   .常量 SE_RESTORE_PRIVILEGE, "18", 公开
   .常量 SE_SHUTDOWN_PRIVILEGE, "19", 公开
   .常量 SE_DEBUG_PRIVILEGE, "20", 公开
*/

我们提升的权限就是SE_DEBUG_PRIVILEGE  20

//程序编译成64位可以注入64位 编译成32位可以注入32位
CWinApp theApp;
typedef enum  _WIN_VERSION
{
    WindowsNT,
    Windows2000,
    WindowsXP,
    Windows2003,
    WindowsVista,
    Windows7,
    Windows8,
    WinUnknown
}WIN_VERSION;

VOID InjectDll(ULONG_PTR ProcessID);
WIN_VERSION  GetWindowsVersion();
BOOL InjectDllByRemoteThread32(const TCHAR* wzDllFile, ULONG_PTR ProcessId);
WIN_VERSION  WinVersion = WinUnknown;

BOOL InjectDllByRemoteThread64(const TCHAR* wzDllFile, ULONG_PTR ProcessId);
typedef long (__fastcall *pfnRtlAdjustPrivilege64)(ULONG,ULONG,ULONG,PVOID);
pfnRtlAdjustPrivilege64 RtlAdjustPrivilege;


int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    cout<<"查看要注入进程的ID"<<endl;   
    ULONG_PTR ProcessID = 0;
    WinVersion = GetWindowsVersion();
    printf("Input ProcessID\r\n");
    cin>>ProcessID;
    InjectDll(ProcessID);
    return 0;
}

VOID InjectDll(ULONG_PTR ProcessID)
{
    CString strPath32 = L"MessageBox32.dll";   //32位dll注入32位系统
    CString strPath64 = L"MessageBox64.dll";
    if (ProcessID == 0)
    {
        return;
    }
    if (PathFileExists(strPath32)&&PathFileExists(strPath64))
    {
        switch(WinVersion)
        {
        case Windows7:   //这里用的是Win7 x64 sp1
            {

                WCHAR wzPath[MAX_PATH] = {0};
                GetCurrentDirectory(260,wzPath);
                wcsncat_s(wzPath, L"\\", 2);
                wcsncat_s(wzPath, strPath64.GetBuffer(), strPath64.GetLength());//dll完整路径
                strPath32.ReleaseBuffer();
                if (!InjectDllByRemoteThread64(wzPath,ProcessID))
                    printf("Inject Fail\r\n");
                else printf ("Inject Success\r\n");
                break;
            }

        case WindowsXP:  //WinXp x86 sp3
            {
                WCHAR wzPath[MAX_PATH] = {0};
                GetCurrentDirectory(260,wzPath);
                wcsncat_s(wzPath, L"\\", 2);
                wcsncat_s(wzPath, strPath32.GetBuffer(), strPath32.GetLength());

                strPath32.ReleaseBuffer();
                if (!InjectDllByRemoteThread32(wzPath,ProcessID))
                    printf("Inject Fail\r\n");            
                else printf("Inject Success\r\n");
                break;
            }
        }
    
    }    
}



BOOL InjectDllByRemoteThread64(const TCHAR* wzDllFile, ULONG_PTR ProcessId)
{
    if (NULL == wzDllFile || 0 == ::_tcslen(wzDllFile) || ProcessId == 0 || -1 == _taccess(wzDllFile, 0))
    {
        return FALSE;
    }
    HANDLE                 hProcess = NULL;
    HANDLE                 hThread  = NULL;
    DWORD                  dwRetVal    = 0;
    LPTHREAD_START_ROUTINE FuncAddress = NULL;
    DWORD  dwSize = 0;
    TCHAR* VirtualAddress = NULL;
    //预编译,支持Unicode
#ifdef _UNICODE
    FuncAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryW");
#else
    FuncAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryA");
#endif

    if (FuncAddress==NULL)
    {
        return FALSE;
    }

    RtlAdjustPrivilege=(pfnRtlAdjustPrivilege64)GetProcAddress((HMODULE)(FuncAddress(L"ntdll.dll")),"RtlAdjustPrivilege");

    if (RtlAdjustPrivilege==NULL)
    {
        return FALSE;
    }
        /*
        .常量 SE_BACKUP_PRIVILEGE, "17", 公开
        .常量 SE_RESTORE_PRIVILEGE, "18", 公开
        .常量 SE_SHUTDOWN_PRIVILEGE, "19", 公开
        .常量 SE_DEBUG_PRIVILEGE, "20", 公开
        */
    RtlAdjustPrivilege(20,1,0,&dwRetVal);  //19

    hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, ProcessId);

    if (NULL == hProcess)
    {
        printf("Open Process Fail\r\n");
        return FALSE;
    }

    // 在目标进程中分配内存空间
    dwSize = (DWORD)::_tcslen(wzDllFile) + 1;
    VirtualAddress = (TCHAR*)::VirtualAllocEx(hProcess, NULL, dwSize * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE);  
    if (NULL == VirtualAddress)
    {
        printf("Virtual Process Memory Fail\r\n");
        CloseHandle(hProcess);
        return FALSE;
    }

    // 在目标进程的内存空间中写入所需参数(模块名)
    if (FALSE == ::WriteProcessMemory(hProcess, VirtualAddress, (LPVOID)wzDllFile, dwSize * sizeof(TCHAR), NULL))
    {
        printf("Write Data Fail\r\n");
        VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT);
        CloseHandle(hProcess);
        return FALSE;
    }

    hThread = ::CreateRemoteThread(hProcess, NULL, 0, FuncAddress, VirtualAddress, 0, NULL);
    if (NULL == hThread)
    {
        printf("CreateRemoteThread Fail\r\n");
        VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT);
        CloseHandle(hProcess);
        return FALSE;
    }
    // 等待远程线程结束
    WaitForSingleObject(hThread, INFINITE);
    // 清理资源
    VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT);
    CloseHandle(hThread);
    CloseHandle(hProcess);
    return TRUE;

}


BOOL InjectDllByRemoteThread32(const TCHAR* wzDllFile, ULONG_PTR ProcessId)
{
    // 参数无效
    if (NULL == wzDllFile || 0 == ::_tcslen(wzDllFile) || ProcessId == 0 || -1 == _taccess(wzDllFile, 0))
    {    
        return FALSE;
    }
    HANDLE hProcess = NULL;
    HANDLE hThread  = NULL;
    DWORD dwSize = 0;
    TCHAR* VirtualAddress = NULL;
    LPTHREAD_START_ROUTINE FuncAddress = NULL;
    // 获取目标进程句柄
    hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, ProcessId);
    if (NULL == hProcess)
    {
        printf("Open Process Fail\r\n");
        return FALSE;
    }
    // 在目标进程中分配内存空间
    dwSize = (DWORD)::_tcslen(wzDllFile) + 1;
    VirtualAddress = (TCHAR*)::VirtualAllocEx(hProcess, NULL, dwSize * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE);
    if (NULL == VirtualAddress)
    {
        printf("Virtual Process Memory Fail\r\n");
        CloseHandle(hProcess);
        return FALSE;
    }
    // 在目标进程的内存空间中写入所需参数(模块名)
    if (FALSE == ::WriteProcessMemory(hProcess, VirtualAddress, (LPVOID)wzDllFile, dwSize * sizeof(TCHAR), NULL))
    {
        printf("Write Data Fail\r\n");
        VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT);
        CloseHandle(hProcess);
        return FALSE;
    }
    // 从 Kernel32.dll 中获取 LoadLibrary 函数地址
#ifdef _UNICODE
    FuncAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryW");
#else
    FuncAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryA");
#endif

    if (NULL == FuncAddress)
    {
        printf("Get LoadLibrary Fail\r\n");
        VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT);
        CloseHandle(hProcess);
        return false;
    }

    // 创建远程线程调用 LoadLibrary
    hThread = ::CreateRemoteThread(hProcess, NULL, 0, FuncAddress, VirtualAddress, 0, NULL);
    if (NULL == hThread)
    {
        printf("CreateRemoteThread Fail\r\n");
        VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT);
        CloseHandle(hProcess);
        return FALSE;
    }

    // 等待远程线程结束
    WaitForSingleObject(hThread, INFINITE);
    // 清理
    VirtualFreeEx(hProcess, VirtualAddress, dwSize, MEM_DECOMMIT);
    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
}

WIN_VERSION  GetWindowsVersion()
{
    OSVERSIONINFOEX    OsVerInfoEx;
    OsVerInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    GetVersionEx((OSVERSIONINFO *)&OsVerInfoEx); // 注意转换类型
    switch (OsVerInfoEx.dwPlatformId)
    {
    case VER_PLATFORM_WIN32_NT:
        {
            if (OsVerInfoEx.dwMajorVersion <= 4 )
            {
                return WindowsNT;
            }
            if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 0)
            {
                return Windows2000;
            }

            if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 1)
            {
                return WindowsXP;
            }
            if (OsVerInfoEx.dwMajorVersion == 5 && OsVerInfoEx.dwMinorVersion == 2)
            {
                return Windows2003;
            }
            if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 0)
            {
                return WindowsVista;
            }

            if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 1)
            {
                return Windows7;
            }
            if (OsVerInfoEx.dwMajorVersion == 6 && OsVerInfoEx.dwMinorVersion == 2 )
            {
                return Windows8;
            }
            break;
        }

    default:
        {
            return WinUnknown;
        }
    }

}

 

二 ring3 apc注入

APC注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,其具体流程如下:
    1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断。
    2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。
    3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。

#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <TlHelp32.h>

#include <iostream>
#include <string>
using namespace std;

#define DEF_BUF_SIZE 1024

typedef long (__fastcall *pfnRtlAdjustPrivilege64)(ULONG,ULONG,ULONG,PVOID);
pfnRtlAdjustPrivilege64 RtlAdjustPrivilege;

// 用于存储注入模块DLL的路径全名
char szDllPath[DEF_BUF_SIZE] = {0} ;

// 使用APC机制向指定ID的进程注入模块
BOOL InjectModuleToProcessById ( DWORD dwProcessId )
{
    DWORD    dwRet = 0 ;
    BOOL    bStatus = FALSE ;
    LPVOID    lpData = NULL ;
    UINT    uLen = strlen(szDllPath) + 1;
#ifdef _WIN64   // x64 OpenProcess提权操作
     RtlAdjustPrivilege=(pfnRtlAdjustPrivilege64)GetProcAddress((HMODULE)(FuncAddress(L"ntdll.dll")),"RtlAdjustPrivilege");

    if (RtlAdjustPrivilege==NULL)
    {
        return FALSE;
    }
        /*
        .常量 SE_BACKUP_PRIVILEGE, "17", 公开
        .常量 SE_RESTORE_PRIVILEGE, "18", 公开
        .常量 SE_SHUTDOWN_PRIVILEGE, "19", 公开
        .常量 SE_DEBUG_PRIVILEGE, "20", 公开
        */
    RtlAdjustPrivilege(20,1,0,&dwRetVal);  //19
#endif
    // 打开目标进程
    HANDLE hProcess = OpenProcess ( PROCESS_ALL_ACCESS, FALSE, dwProcessId ) ;
    if ( hProcess )
    {
        // 分配空间
        lpData = VirtualAllocEx ( hProcess, NULL, uLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE ) ;
        if ( lpData )
        {
            // 写入需要注入的模块路径全名
            bStatus = WriteProcessMemory ( hProcess, lpData, szDllPath, uLen, &dwRet ) ;
        }
        CloseHandle ( hProcess ) ;
    }

    if ( bStatus == FALSE )
        return FALSE ;

    // 创建线程快照
    THREADENTRY32 te32 = { sizeof(THREADENTRY32) } ;
    HANDLE hThreadSnap = CreateToolhelp32Snapshot ( TH32CS_SNAPTHREAD, 0 ) ;
    if ( hThreadSnap == INVALID_HANDLE_VALUE ) 
        return FALSE ; 

    bStatus = FALSE ;
    // 枚举所有线程
    if ( Thread32First ( hThreadSnap, &te32 ) )
    {
        do{
            // 判断是否目标进程中的线程
            if ( te32.th32OwnerProcessID == dwProcessId )
            {
                // 打开线程
                HANDLE hThread = OpenThread ( THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID ) ;
                if ( hThread )
                {
                    // 向指定线程添加APC
                    DWORD dwRet = QueueUserAPC ( (PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)lpData ) ;
                    if ( dwRet > 0 )
                        bStatus = TRUE ;
                    CloseHandle ( hThread ) ;
                }
            } 

        }while ( Thread32Next ( hThreadSnap, &te32 ) ) ;
    }

    CloseHandle ( hThreadSnap ) ;
    return bStatus;
}

int _tmain(int argc, _TCHAR* argv[])
{
    // 取得当前工作目录路径
    GetCurrentDirectoryA ( DEF_BUF_SIZE, szDllPath ) ;

    // 生成注入模块DLL的路径全名
    strcat ( szDllPath, "\\DLLSample.dll" ) ;

    DWORD dwProcessId = 0 ;
    // 接收用户输入的目标进程ID
    while ( cout << "请输入目标进程ID:" && cin >> dwProcessId && dwProcessId > 0 ) 
    {
        BOOL bRet = InjectModuleToProcessById ( dwProcessId ) ;
        cout << (bRet ? "注入成功!":"注入失败!") << endl ;
    }
    return 0;
}

 

三ring3层的远程线程的代码注入

代码是成功,但是每次运行就explorer直接崩溃。我开始还以为是我程序写的有问题,后来用WinDbg Attach到explorer进程调试,才发现问题,就是函数调用的问题,在汇编中call调用函数时不是绝对地址,机器码是相对偏移,这就涉及到函数的便宜重新重定位的问题,得不偿失,代价太大。仅仅以学习为目的的话,可以考虑用0day中非常经典的类似于GetProcAddress()函数实现的191个字节的shellcode来获取函数地址。先放上代码吧,在Windbg中对explorer中的RemoteCodeAddr下断点,是可以执行到那的,然后执行到call MessageBox就崩溃,就是函数的偏移的问题。

#include "stdafx.h"
#include <iostream>
#include <Windows.h>
using namespace std;


typedef long (__fastcall *pfnRtlAdjustPrivilege64)(ULONG,ULONG,ULONG,PVOID);
pfnRtlAdjustPrivilege64 RtlAdjustPrivilege;


static DWORD WINAPI MyFunc (LPVOID pData)
{
    //do something
    //...
    //pData输入项可以是任何类型值
//    ::MessageBoxA(NULL,(char*)pData,"INJECT",0); //不能直接调用
    //调用函数时要考虑call RVA  都是相对的偏移,
    return *(DWORD*)pData;
}


static void AfterMyFunc (void) {
}

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD cbCodeSize = (ULONG_PTR)AfterMyFunc-(ULONG_PTR)MyFunc +0x100;
    DWORD ProcessId = 0;
    cin>>ProcessId;

#ifdef _WIN64   // x64 OpenProcess提权操作
     RtlAdjustPrivilege=(pfnRtlAdjustPrivilege64)GetProcAddress((HMODULE)(GetModuleHandle(L"ntdll.dll")),"RtlAdjustPrivilege");

    if (RtlAdjustPrivilege==NULL)
    {
        return FALSE;
    }
        /*
        .常量 SE_BACKUP_PRIVILEGE, "17", 公开
        .常量 SE_RESTORE_PRIVILEGE, "18", 公开
        .常量 SE_SHUTDOWN_PRIVILEGE, "19", 公开
        .常量 SE_DEBUG_PRIVILEGE, "20", 公开
        */
    DWORD dwReturnVal;
    RtlAdjustPrivilege(20,1,0,&dwReturnVal);  //19
#endif
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, ProcessId);
    //申请放置代码的内存
    PVOID RemoteCodeAddr = (PVOID)VirtualAllocEx( hProcess, 0, cbCodeSize,
        MEM_COMMIT,
        PAGE_EXECUTE_READWRITE );
    WriteProcessMemory( hProcess, RemoteCodeAddr, &MyFunc, cbCodeSize, NULL);

    char szBuffer[] = "HelloWorld";
    PVOID RemoteDataAddr = (PVOID)VirtualAllocEx( hProcess, 0, sizeof(szBuffer),
        MEM_COMMIT,
        PAGE_READWRITE );
    WriteProcessMemory( hProcess, RemoteDataAddr, szBuffer, sizeof(szBuffer), NULL);

    HANDLE hThread = ::CreateRemoteThread(hProcess, NULL, 0, 
        (LPTHREAD_START_ROUTINE) RemoteCodeAddr,
        RemoteDataAddr, 0 , NULL);
    DWORD h;
    if (hThread)
    {
        ::WaitForSingleObject( hThread, INFINITE );
        ::CloseHandle( hThread );
    }
    //cout<<dwSizeOfMyFunc<<endl;
    return 0;
}

 

四ring0的apc注入

#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <WinIoCtl.h>
using namespace std;




#define CTL_KEINJECTAPC \
    CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)



typedef struct _INJECT_INFO
{
    ULONG ProcessId;
    wchar_t DllName[1024];
}INJECT_INFO,*PINJECT_INFO;

int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hFile;
    INJECT_INFO InjectInfo;
    hFile=CreateFile(L"\\\\.\\DriverLink",
        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());
        return -1;
    }

    memset(&InjectInfo,0,sizeof(INJECT_INFO));
    scanf("%d",&(InjectInfo.ProcessId));
    wscanf(L"%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;
}





#include <ntifs.h>
#include <devioctl.h>
#include <ntimage.h>

#endif    


#define DEVICE_NAME       L"\\Device\\DriverDevice"
#define LINK_NAME       L"\\DosDevices\\DriverLink"

#define CTL_KEINJECTAPC \
    CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)

typedef struct _SYSTEM_THREAD_INFORMATION
{
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER CreateTime;
    ULONG WaitTime;
    PVOID StartAddress;
    CLIENT_ID ClientId;
    KPRIORITY Priority;
    LONG BasePriority;
    ULONG ContextSwitches;
    ULONG ThreadState;
    KWAIT_REASON WaitReason;
}SYSTEM_THREAD_INFORMATION,*PSYSTEM_THREAD_INFORMATION;

typedef struct _SYSTEM_PROCESS_INFO
{
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    LARGE_INTEGER WorkingSetPrivateSize;
    ULONG HardFaultCount;
    ULONG NumberOfThreadsHighWatermark;
    ULONGLONG CycleTime;
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER KernelTime;
    UNICODE_STRING ImageName;
    KPRIORITY BasePriority;
    HANDLE UniqueProcessId;
    HANDLE InheritedFromUniqueProcessId;
    ULONG HandleCount;
    ULONG SessionId;
    ULONG_PTR UniqueProcessKey;
    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;
    ULONG PageFaultCount;
    SIZE_T PeakWorkingSetSize;
    SIZE_T WorkingSetSize;
    SIZE_T QuotaPeakPagedPoolUsage;
    SIZE_T QuotaPagedPoolUsage;
    SIZE_T QuotaPeakNonPagedPoolUsage;
    SIZE_T QuotaNonPagedPoolUsage;
    SIZE_T PagefileUsage;
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER ReadOperationCount;
    LARGE_INTEGER WriteOperationCount;
    LARGE_INTEGER OtherOperationCount;
    LARGE_INTEGER ReadTransferCount;
    LARGE_INTEGER WriteTransferCount;
    LARGE_INTEGER OtherTransferCount;
    SYSTEM_THREAD_INFORMATION Threads[1];
}SYSTEM_PROCESS_INFO,*PSYSTEM_PROCESS_INFO;

typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;

    union
    {
        LIST_ENTRY HashLinks;

        struct
        {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };

    union
    {
        ULONG TimeDateStamp;
        PVOID LoadedImports;
    };

    struct _ACTIVATION_CONTEXT * EntryPointActivationContext;
    PVOID PatchInformation;
    LIST_ENTRY ForwarderLinks;
    LIST_ENTRY ServiceTagLinks;
    LIST_ENTRY StaticLinks;
}LDR_DATA_TABLE_ENTRY,*PLDR_DATA_TABLE_ENTRY;


typedef struct _INJECT_INFO
{
    ULONG ProcessId;
    wchar_t DllName[1024];
}INJECT_INFO,*PINJECT_INFO;

typedef NTSTATUS (*PLDR_LOAD_DLL)(PWSTR,PULONG,PUNICODE_STRING,PVOID*);

typedef struct _KINJECT
{
    UNICODE_STRING DllName;
    wchar_t Buffer[1024];
    PLDR_LOAD_DLL LdrLoadDll;
    PVOID DllBase;
    ULONG Executed;
}KINJECT,*PKINJECT;

typedef enum _KAPC_ENVIRONMENT
{
    OriginalApcEnvironment,
    AttachedApcEnvironment,
    CurrentApcEnvironment,
    InsertApcEnvironment
}KAPC_ENVIRONMENT,*PKAPC_ENVIRONMENT;

typedef VOID (NTAPI *PKNORMAL_ROUTINE)(
    PVOID NormalContext,
    PVOID SystemArgument1,
    PVOID SystemArgument2
    );

typedef VOID KKERNEL_ROUTINE(
    PRKAPC Apc,
    PKNORMAL_ROUTINE *NormalRoutine,
    PVOID *NormalContext,
    PVOID *SystemArgument1,
    PVOID *SystemArgument2
    );

typedef KKERNEL_ROUTINE (NTAPI *PKKERNEL_ROUTINE);

typedef VOID (NTAPI *PKRUNDOWN_ROUTINE)(
    PRKAPC Apc
    );

void KeInitializeApc(
    PRKAPC Apc,
    PRKTHREAD Thread,
    KAPC_ENVIRONMENT Environment,
    PKKERNEL_ROUTINE KernelRoutine,
    PKRUNDOWN_ROUTINE RundownRoutine,
    PKNORMAL_ROUTINE NormalRoutine,
    KPROCESSOR_MODE ProcessorMode,
    PVOID NormalContext
    );

BOOLEAN KeInsertQueueApc(
    PRKAPC Apc,
    PVOID SystemArgument1,
    PVOID SystemArgument2,
    KPRIORITY Increment
    );



NTSTATUS ZwQuerySystemInformation(ULONG InfoClass,PVOID Buffer,ULONG Length,PULONG ReturnLength);
LPSTR PsGetProcessImageFileName(PEPROCESS Process);

NTSTATUS DefaultPassThrough(PDEVICE_OBJECT  DeviceObject,PIRP Irp);
void UnloadDriver(PDRIVER_OBJECT DriverObject);
NTSTATUS DriverDispatch(PDEVICE_OBJECT DeviceObject,PIRP Irp);



ULONG ApcStateOffset; 
PLDR_LOAD_DLL LdrLoadDll; 


NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING pRegistryPath)
{
    NTSTATUS Status;
    PDEVICE_OBJECT DeviceObject;
    PEPROCESS Process;
    PETHREAD Thread;
    PKAPC_STATE ApcState;

    PVOID KdVersionBlock,NtdllBase;
    PULONG ptr,Functions,Names;
    PUSHORT Ordinals;

    PLDR_DATA_TABLE_ENTRY MmLoadedUserImageList,ModuleEntry;
    ULONG i;

    PIMAGE_DOS_HEADER pIDH;
    PIMAGE_NT_HEADERS pINH;
    PIMAGE_EXPORT_DIRECTORY pIED;

    UNICODE_STRING   uniDeviceName;
    UNICODE_STRING   uniLinkName;

    RtlInitUnicodeString(&uniDeviceName,DEVICE_NAME);

    RtlInitUnicodeString(&uniLinkName,LINK_NAME);

    for (i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++)
    {
        DriverObject->MajorFunction[i] = DefaultPassThrough;
    }
    DriverObject->DriverUnload = UnloadDriver;

    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDispatch;

    //创建设备对象
    Status = IoCreateDevice(DriverObject,0,&uniDeviceName,FILE_DEVICE_UNKNOWN,0,FALSE,&DeviceObject);

    if (!NT_SUCCESS(Status))
    {

        return Status;
    }

    Status = IoCreateSymbolicLink(&uniLinkName,&uniDeviceName);

    if (!NT_SUCCESS(Status))
    {
        IoDeleteDevice(DeviceObject);

        return Status;
    }

    //使当前线程运行在第一个处理器上
    KeSetSystemAffinityThread(1);
    KdVersionBlock=(PVOID)__readfsdword(0x34); //得到KdVersionBlock
    KeRevertToUserAffinityThread();//恢复线程运行的处理器
    MmLoadedUserImageList=*(PLDR_DATA_TABLE_ENTRY*)((PUCHAR)KdVersionBlock+0x228); // Get the MmLoadUserImageList

    /*
    kd> !pcr
    KPCR for Processor 0 at 83f3ec00:


    kd> dt _kpcr 83f3ec00
    +0x034 KdVersionBlock   : 0x83f3dc00 Void

    kd> dd 0x83f3dc00+0x228
    83f3de28  83f5de38 00000000 83e5dfa8 00000000
    83f3de38  00000000 00000000 83f7d8c0 00000000
    83f3de48  83f7d560 00000000 83f5d84c 00000000


    kd> dd 83f5de38
    83f5de38  8706b1e8 877cb660 00000000 00000000
    83f5de48  00000000 00000000 00040107 00000000
    83f5de58  865d0690 865d0690 c0403188 0007ff7e

    kd> dt _LDR_DATA_TABLE_ENTRY 8706b1e8
    nt!_LDR_DATA_TABLE_ENTRY
    +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x8713b4e0 - 0x83f5de38 ]
    +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
    +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
    +0x018 DllBase          : 0x77ce0000 Void
    +0x01c EntryPoint       : (null) 
    +0x020 SizeOfImage      : 0x13c000
    +0x024 FullDllName      : _UNICODE_STRING "\Windows\System32\ntdll.dll"
    +0x02c BaseDllName      : _UNICODE_STRING ""
    +0x034 Flags            : 0
    +0x038 LoadCount        : 1
    +0x03a TlsIndex         : 0
    +0x03c HashLinks        : _LIST_ENTRY [ 0x0 - 0x1490d9 ]
    +0x03c SectionPointer   : (null) 
    +0x040 CheckSum         : 0x1490d9
    +0x044 TimeDateStamp    : 0
    +0x044 LoadedImports    : (null) 
    +0x048 EntryPointActivationContext : (null) 
    +0x04c PatchInformation : (null) 
    +0x050 ForwarderLinks   : _LIST_ENTRY [ 0x0 - 0x0 ]
    +0x058 ServiceTagLinks  : _LIST_ENTRY [ 0x0 - 0x57005c ]
    +0x060 StaticLinks      : _LIST_ENTRY [ 0x6e0069 - 0x6f0064 ]
    +0x068 ContextInformation : 0x00730077 Void
    +0x06c OriginalBase     : 0x53005c
    +0x070 LoadTime         : _LARGE_INTEGER 0x650074`00730079

    */
    DbgPrint("KdVersionBlock address: %#x",KdVersionBlock);
    DbgPrint("MmLoadedUserImageList address: %#x",MmLoadedUserImageList);

    ModuleEntry=(PLDR_DATA_TABLE_ENTRY)MmLoadedUserImageList->InLoadOrderLinks.Flink; //第一模块
    NtdllBase=ModuleEntry->DllBase; //ntdll基地址

    DbgPrint("ntdll base address: %#x",NtdllBase);

    pIDH=(PIMAGE_DOS_HEADER)NtdllBase;
    pINH=(PIMAGE_NT_HEADERS)((PUCHAR)NtdllBase+pIDH->e_lfanew);
    pIED=(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)NtdllBase+pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

    Functions=(PULONG)((PUCHAR)NtdllBase+pIED->AddressOfFunctions);
    Names=(PULONG)((PUCHAR)NtdllBase+pIED->AddressOfNames);

    Ordinals=(PUSHORT)((PUCHAR)NtdllBase+pIED->AddressOfNameOrdinals);

    //搜索LdrLoadDll
    for(i=0;i<pIED->NumberOfNames;i++)
    {
        if(!strcmp((char*)NtdllBase+Names[i],"LdrLoadDll"))
        {
            LdrLoadDll=(PLDR_LOAD_DLL)((PUCHAR)NtdllBase+Functions[Ordinals[i]]);
            break;
        }
    }

    DbgPrint("LdrLoadDll address: %#x",LdrLoadDll);

    Process=PsGetCurrentProcess();
    Thread=PsGetCurrentThread();

    ptr=(PULONG)Thread;

    //确定ApcState在EThread中的偏移
    for(i=0;i<512;i++)
    {
        if(ptr[i]==(ULONG)Process)
        {
            ApcState=CONTAINING_RECORD(&ptr[i],KAPC_STATE,Process); 
            ApcStateOffset=(ULONG)ApcState-(ULONG)Thread; 
            break;
        }
    }

    DbgPrint("ApcState offset: %#x",ApcStateOffset);
    DbgPrint("DLL injection driver loaded.");
    return STATUS_SUCCESS;
}




NTSTATUS DefaultPassThrough(PDEVICE_OBJECT  DeviceObject,PIRP Irp)
{
    Irp->IoStatus.Information = 0;
    Irp->IoStatus.Status = STATUS_SUCCESS;

    IoCompleteRequest(Irp,IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}


void UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING  uniLinkName;
    PDEVICE_OBJECT  CurrentDeviceObject;
    PDEVICE_OBJECT  NextDeviceObject;

    RtlInitUnicodeString(&uniLinkName,LINK_NAME);

    IoDeleteSymbolicLink(&uniLinkName);

    if (DriverObject->DeviceObject!=NULL)
    {
        CurrentDeviceObject = DriverObject->DeviceObject;

        while(CurrentDeviceObject!=NULL)
        {
            NextDeviceObject  = CurrentDeviceObject->NextDevice;
            IoDeleteDevice(CurrentDeviceObject);

            CurrentDeviceObject = NextDeviceObject;
        }
    }
    DbgPrint("UnloadDriver\r\n");
}


void NTAPI InjectDllApc(PVOID NormalContext,PVOID SystemArgument1,PVOID SystemArgument2)
{
    PKINJECT inject=(PKINJECT)NormalContext;

    inject->LdrLoadDll(NULL,NULL,&inject->DllName,&inject->DllBase);
    inject->Executed=TRUE;
}


void NTAPI KernelRoutine(PKAPC apc,PKNORMAL_ROUTINE* NormalRoutine,PVOID* NormalContext,\
    PVOID* SystemArgument1,PVOID* SystemArgument2)
{
    ExFreePool(apc);
}

BOOLEAN InjectDll(PINJECT_INFO InjectInfo)
{
    PEPROCESS Process;
    PETHREAD Thread;
    PKINJECT mem;
    ULONG size;
    PKAPC_STATE ApcState;
    PKAPC apc;
    PVOID buffer;
    PSYSTEM_PROCESS_INFO pSpi;
    LARGE_INTEGER delay;
    buffer=ExAllocatePool(NonPagedPool,1024*1024); 
    if(!buffer)
    {
        DbgPrint("Error: Unable to allocate memory for the process thread list.");
        return FALSE;
    }

    //5    SystemProcessInformation,
    if(!NT_SUCCESS(ZwQuerySystemInformation(5,buffer,1024*1024,NULL)))
    {
        DbgPrint("Error: Unable to query process thread list.");

        ExFreePool(buffer);
        return FALSE;
    }

    pSpi=(PSYSTEM_PROCESS_INFO)buffer;

    //找到目标进程
    while(pSpi->NextEntryOffset)
    {
        if(pSpi->UniqueProcessId==InjectInfo->ProcessId)
        {
            DbgPrint("Target thread found. TID: %d",pSpi->Threads[0].ClientId.UniqueThread);
            break;
        }

        pSpi=(PSYSTEM_PROCESS_INFO)((PUCHAR)pSpi+pSpi->NextEntryOffset);
    }

    // 引用目标进程EProcess,
    if(!NT_SUCCESS(PsLookupProcessByProcessId(InjectInfo->ProcessId,&Process)))
    {
        DbgPrint("Error: Unable to reference the target process.");
        ExFreePool(buffer);
        return FALSE;
    }

    DbgPrint("Process name: %s",PsGetProcessImageFileName(Process));
    DbgPrint("EPROCESS address: %#x",Process);

    //目标进程主线程
    if(!NT_SUCCESS(PsLookupThreadByThreadId(pSpi->Threads[0].ClientId.UniqueThread,&Thread)))
    {
        DbgPrint("Error: Unable to reference the target thread.");
        ObDereferenceObject(Process); 
        ExFreePool(buffer); 
        return FALSE;
    }

    DbgPrint("ETHREAD address: %#x",Thread);

    ExFreePool(buffer); 
    //切入到目标进程
    KeAttachProcess(Process); 

    mem=NULL;
    size=4096;

    //在目标进程申请内存
    if(!NT_SUCCESS(ZwAllocateVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,0,&size,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE)))
    {
        DbgPrint("Error: Unable to allocate memory in the target process.");
        KeDetachProcess(); 

        ObDereferenceObject(Process);
        ObDereferenceObject(Thread); 
        return FALSE;
    }

    DbgPrint("Memory allocated at %#x",mem);
    mem->LdrLoadDll=LdrLoadDll; 
    wcscpy(mem->Buffer,InjectInfo->DllName); 
    RtlInitUnicodeString(&mem->DllName,mem->Buffer); 
    ApcState=(PKAPC_STATE)((PUCHAR)Thread+ApcStateOffset); 
    ApcState->UserApcPending=TRUE;   
    memcpy((PKINJECT)(mem+1),InjectDllApc,(ULONG)KernelRoutine-(ULONG)InjectDllApc); 
    DbgPrint("APC code address: %#x",(PKINJECT)(mem+1));

    //申请apc对象
    apc=(PKAPC)ExAllocatePool(NonPagedPool,sizeof(KAPC)); 

    if(!apc)
    {
        DbgPrint("Error: Unable to allocate the APC object.");
        size=0;
        ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE);  
        KeDetachProcess();
        ObDereferenceObject(Process); 
        ObDereferenceObject(Thread); 
        return FALSE;
    }

    KeInitializeApc(apc,
        Thread,    //目标进程主线程
        OriginalApcEnvironment,   //目标apcz状态
        KernelRoutine,  //内核apc总入口
        NULL,       //Rundown Rounine=NULL
        (PKNORMAL_ROUTINE)((PKINJECT)mem+1),   //用户空间的总apc
        UserMode,   //插入到用户apc队列
        mem); // 自己的apc队列

    DbgPrint("Inserting APC to target thread");

    // 插入apc队列
    if(!KeInsertQueueApc(apc,NULL,NULL,IO_NO_INCREMENT))
    {
        DbgPrint("Error: Unable to insert APC to target thread.");
        size=0;
        ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); 
        KeDetachProcess(); 
        ObDereferenceObject(Process); 
        ObDereferenceObject(Thread); 
        ExFreePool(apc); 
        return FALSE;
    }

    delay.QuadPart=-100*10000;
    while(!mem->Executed)
    {
        KeDelayExecutionThread(KernelMode,FALSE,&delay);  //等待apc执行 
    }
    if(!mem->DllBase)
    {
        DbgPrint("Error: Unable to inject DLL into target process.");
        size=0;
        ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE);
        KeDetachProcess();
        ObDereferenceObject(Process);
        ObDereferenceObject(Thread);
        return FALSE;
    }

    DbgPrint("DLL injected at %#x",mem->DllBase);
    size=0;
    ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&mem,&size,MEM_RELEASE); 
    ObDereferenceObject(Process); 
    ObDereferenceObject(Thread); 
    return TRUE;
}

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(!InjectDll(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;
}

 

posted on 2015-07-21 01:24  _懒人  阅读(7344)  评论(2编辑  收藏  举报

导航