码迷,mamicode.com
首页 > Windows程序 > 详细

Windows 注入

时间:2015-07-21 06:42:15      阅读:670      评论:0      收藏:0      [点我收藏+]

标签:

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

标签:

原文地址:http://www.cnblogs.com/lanrenxinxin/p/4662364.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!