• Dll注入:X86/X64 远程线程CreateRemoteThread 注入


    远线程注入原理是利用Windows 系统中CreateRemoteThread()这个API,其中第4个参数是准备运行的线程,我们可以将LoadLibrary()填入其中,这样就可以执行远程进程中的LoadLibrary()函数,进而将我们自己准备的DLL加载到远程进程空间中执行。

    函数原型:

    HANDLE
    WINAPI
    CreateRemoteThread(                                  
        _In_ HANDLE hProcess,                              //远程线程的句柄
        _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, //安全属性  
        _In_ SIZE_T dwStackSize,                           //栈大小
        _In_ LPTHREAD_START_ROUTINE lpStartAddress,        //进程处理函数
        _In_opt_ LPVOID lpParameter,                       //进程参数
        _In_ DWORD dwCreationFlags,                        //默认创建后的状态
        _Out_opt_ LPDWORD lpThreadId                       //所创建的线程的ID
        );

    注入过程:

    1.提权

    2.根据进程ID打开对方进程OpenProcess,得到进程句柄

    3.根据进程句柄在目标进程中申请内存VirtualAllocEx

    4.在目标进程中刚刚申请的内存空间中写入所需参数(Dll的完整路径)WriteProcessMemory

    5.用GetProcAddress得到LoadLibraryW的模块加载地址

    6.启动远程线程CreateRemoteThread,并在第四参数传入该线程需要执行的函数名(即loadlibrary)

    BOOL  InjectDllByRemoteThread(ULONG32 ulProcessID, WCHAR* wzDllFullPath)
    {
        HANDLE  ProcessHandle = NULL;
        ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ulProcessID);
    
        if (ProcessHandle==NULL)
        {
            return FALSE;
        }
    
        WCHAR* VirtualAddress = NULL;
    
        ULONG32 ulDllLength = (ULONG32)_tcslen(wzDllFullPath) + 1;
    
        VirtualAddress = (WCHAR*)VirtualAllocEx(ProcessHandle, NULL,
                                                ulDllLength * sizeof(WCHAR),
                                                MEM_COMMIT, PAGE_READWRITE);
    
    
        if (VirtualAddress==NULL)
        {
            CloseHandle(ProcessHandle);
            return FALSE;
        }
    
        // 在目标进程的内存空间中写入所需参数(模块名)
        if (!WriteProcessMemory(ProcessHandle, VirtualAddress, (LPVOID)wzDllFullPath, ulDllLength * sizeof(WCHAR), NULL))
        {
            VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT);
            CloseHandle(ProcessHandle);
            return FALSE;
        }
    
    
        LPTHREAD_START_ROUTINE FunctionAddress = NULL;
    
        FunctionAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryW");
    
        HANDLE ThreadHandle = INVALID_HANDLE_VALUE;
        //启动远程线程
        ThreadHandle = ::CreateRemoteThread(ProcessHandle, NULL, 0, FunctionAddress, VirtualAddress, 0, NULL);
        if (ThreadHandle==FALSE)
        {
            VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT);
            CloseHandle(ProcessHandle);
            return FALSE;
        }
    
        // 等待远程线程结束
        WaitForSingleObject(ThreadHandle, INFINITE);
        // 清理
        VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT);
        CloseHandle(ThreadHandle);
        CloseHandle(ProcessHandle);
        return TRUE;
    }
    BOOL EnableDebugPrivilege()
    {
    
        HANDLE TokenHandle = NULL;
        TOKEN_PRIVILEGES TokenPrivilege;
        LUID uID;
    
        //打开权限令牌
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))  
        {
            return FALSE;
        }
    
        if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &uID))
        {
    
            CloseHandle(TokenHandle);
            TokenHandle = INVALID_HANDLE_VALUE;
            return FALSE;
        }
    
        TokenPrivilege.PrivilegeCount = 1;
        TokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        TokenPrivilege.Privileges[0].Luid = uID;
    
        //在这里我们进行调整权限
        if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
        {
            CloseHandle(TokenHandle);
            TokenHandle = INVALID_HANDLE_VALUE;
            return  FALSE;
        }
    
        CloseHandle(TokenHandle);
        TokenHandle = INVALID_HANDLE_VALUE;
        return TRUE;
    }

    但是如此有个问题,如果目标进程为32位,但是注入了一个64的Dll则会出问题。

    为此我们应当判断目标进程的位数。

    我们可以通过解析exe文件(magic数)判断进程是x64还是x86。

    根据PE知识,所有的PE文件必须以一个DOS MZ header开始,其实它是一个IMAGE_DOS_HEADER类型的结构,

    //DOS头
    typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
        WORD   e_magic;                     // Magic number   ‘MZ’
        WORD   e_cblp;                      // Bytes on last page of file
        WORD   e_cp;                        // Pages in file
        WORD   e_crlc;                      // Relocations
        WORD   e_cparhdr;                   // Size of header in paragraphs
        WORD   e_minalloc;                  // Minimum extra paragraphs needed
        WORD   e_maxalloc;                  // Maximum extra paragraphs needed
        WORD   e_ss;                        // Initial (relative) SS value
        WORD   e_sp;                        // Initial SP value
        WORD   e_csum;                      // Checksum
        WORD   e_ip;                        // Initial IP value
        WORD   e_cs;                        // Initial (relative) CS value
        WORD   e_lfarlc;                    // File address of relocation table
        WORD   e_ovno;                      // Overlay number
        WORD   e_res[4];                    // Reserved words
        WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
        WORD   e_oeminfo;                   // OEM information; e_oemid specific
        WORD   e_res2[10];                  // Reserved words
        LONG   e_lfanew;                    // File address of new exe header   NT头偏移
      } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

    这是PE文件的大致结构,而我们需要的magic成员在_IMAGE_NT_HEADERS结构体中的选项头中_IMAGE_OPTIONAL_HEADER

    _IMAGE_DOS_HEADER结构体的最后一个元素e_lfanew 便是指向_IMAGE_NT_HEADERS的偏移

    //NT头
    typedef struct _IMAGE_NT_HEADERS {
        DWORD Signature;                             //‘PE’
        IMAGE_FILE_HEADER FileHeader;            //PE文件头
        IMAGE_OPTIONAL_HEADER32 OptionalHeader;  //PE选项头
    } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
    //选项头
    typedef struct _IMAGE_OPTIONAL_HEADER {
        WORD    Magic;                     //我们需要的成员
        BYTE    MajorLinkerVersion;
        BYTE    MinorLinkerVersion;
        DWORD   SizeOfCode;
        DWORD   SizeOfInitializedData;
        DWORD   SizeOfUninitializedData;
        DWORD   AddressOfEntryPoint;
        DWORD   BaseOfCode;
        DWORD   BaseOfData;
        DWORD   ImageBase;
        DWORD   SectionAlignment;
        DWORD   FileAlignment;
        WORD    MajorOperatingSystemVersion;
        WORD    MinorOperatingSystemVersion;
        WORD    MajorImageVersion;
        WORD    MinorImageVersion;
        WORD    MajorSubsystemVersion;
        WORD    MinorSubsystemVersion;
        DWORD   Win32VersionValue;
        DWORD   SizeOfImage;
        DWORD   SizeOfHeaders;
        DWORD   CheckSum;
        WORD    Subsystem;
        WORD    DllCharacteristics;
        DWORD   SizeOfStackReserve;
        DWORD   SizeOfStackCommit;
        DWORD   SizeOfHeapReserve;
        DWORD   SizeOfHeapCommit;
        DWORD   LoaderFlags;
        DWORD   NumberOfRvaAndSizes;
        IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
    } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

    所以判断目标进程的代码为:

    enum TargetType
    {
        WOW_86,
        WOW_64,
        WOW_ERROR
    };
    
    
    //通过解析exe文件(magic数)判断进程是x64还是x86
    TargetType GetWowByReadFile(ULONG32  ulProcessID)
    {
        HANDLE  ProcessHandle = INVALID_HANDLE_VALUE;
        ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ulProcessID);
    
        if (ProcessHandle == NULL)
        {
            return WOW_ERROR;
        }
        //获得Exe模块基地址
        ULONG64 ulModuleBaseAddress = (ULONG64)GetModuleBaseAddressByProcessHandle(ProcessHandle);
    
        if (ulModuleBaseAddress == NULL)
        {
            CloseHandle(ProcessHandle);
            return WOW_ERROR;
        }
    
        IMAGE_DOS_HEADER   DosHeader = { 0 };
        //读取Dos头
        if (ReadProcessMemory(ProcessHandle, (PVOID)ulModuleBaseAddress, &DosHeader, sizeof(IMAGE_DOS_HEADER), NULL) == FALSE)
        {
            CloseHandle(ProcessHandle);
            return WOW_ERROR;
        }
    
        WORD  wMagic = 0;
        //模块加载基地址+Dos头部e_lfanew成员(PE头相对于文件的偏移 4字节)+标准PE头+4字节
        if (ReadProcessMemory(ProcessHandle, (PVOID)(ulModuleBaseAddress + DosHeader.e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER)), &wMagic, sizeof(WORD), NULL) == FALSE)
        {
            CloseHandle(ProcessHandle);
            return WOW_ERROR;
        }
    
        CloseHandle(ProcessHandle);
        if (wMagic == 0x20b)//x64
        {
            return WOW_64;
        }
        else if (wMagic == 0x10b)//x86
        {
            return WOW_86;
        }
        else
        {
            return WOW_ERROR;
        }
    }

    结合这个功能,主函数为:

    int main()
    { 
        if (EnableDebugPrivilege() == FALSE)
        {
            return 0;
        }
        ULONG32 ulProcessID = 0;
        printf("Input A ProcessID to Inject:
    ");
        scanf_s("%d", &ulProcessID, sizeof(ULONG32));
    
        DWORD iOk = GetWowByReadFile(ulProcessID);
        switch (iOk)
        {
        case WOW_64:
            if (InjectDllByRemoteThread(ulProcessID, L"InjectDll.dll"))
            {
                printf("Inject Success!
    ");
                break;
            }
        case WOW_86:
            if (InjectDllByRemoteThread(ulProcessID, L"InjectTest32.dll"))
            {
                printf("Inject Success!
    ");
                break;
            }
        default:
            break;
        }
        return 0;
    }
  • 相关阅读:
    服务器数据库不用开通远程连接通过工具在本地连接操作的方法
    怎么搜索同类网站
    Java三行代码搞定MD5加密,测试5c短信网关的demo
    iOS检测用户截屏并获取所截图片
    tomcat输出servlet-api.jar
    从svn资源库目录checkout出maven项目方法
    Maven打包pom里面配置exclude 排除掉环境相关的配置文件
    PHP获取毫秒时间戳,利用microtime()函数
    阿里云OneinStack,Linux下tomcat命令
    阿里云OneinStack数据库相关
  • 原文地址:https://www.cnblogs.com/HsinTsao/p/6528053.html
Copyright © 2020-2023  润新知