Windows 注入

平常用的最多的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注入写上。

你可能感兴趣的:(windows)