• 注入小结


    平常用的最多的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
    ");
        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
    ");
                    else printf ("Inject Success
    ");
                    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
    ");            
                    else printf("Inject Success
    ");
                    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
    ");
            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
    ");
            CloseHandle(hProcess);
            return FALSE;
        }
    
        // 在目标进程的内存空间中写入所需参数(模块名)
        if (FALSE == ::WriteProcessMemory(hProcess, VirtualAddress, (LPVOID)wzDllFile, dwSize * sizeof(TCHAR), NULL))
        {
            printf("Write Data Fail
    ");
            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
    ");
            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
    ");
            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
    ");
            CloseHandle(hProcess);
            return FALSE;
        }
        // 在目标进程的内存空间中写入所需参数(模块名)
        if (FALSE == ::WriteProcessMemory(hProcess, VirtualAddress, (LPVOID)wzDllFile, dwSize * sizeof(TCHAR), NULL))
        {
            printf("Write Data Fail
    ");
            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
    ");
            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
    ");
            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("
    Error: Unable to connect to the driver (%d)
    ",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 "WindowsSystem32
    tdll.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
    ");
    }
    
    
    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;
    }
  • 相关阅读:
    python while循环语句
    Python if条件语句
    推荐一款java的验证码组件——kaptcha
    Linux硬链接和符号链接(转)
    推荐一款好用的java反编译软件——JavaDecompiler
    数据库性能优化之冗余字段的作用
    通过设置代理,解决服务器禁止抓取,报“java.io.IOException: Server returned HTTP response code: 403 for URL”错误的方法
    浏览器在同域名下有并发加载的限制
    XCODE快捷键和功能汇总篇(不断更新)
    队列应用场景,自己实现队列
  • 原文地址:https://www.cnblogs.com/lanrenxinxin/p/4662364.html
Copyright © 2020-2023  润新知