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

Posted HsinTsao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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:\\r\\n");
    scanf_s("%d", &ulProcessID, sizeof(ULONG32));

    DWORD iOk = GetWowByReadFile(ulProcessID);
    switch (iOk)
    {
    case WOW_64:
        if (InjectDllByRemoteThread(ulProcessID, L"InjectDll.dll"))
        {
            printf("Inject Success!\\r\\n");
            break;
        }
    case WOW_86:
        if (InjectDllByRemoteThread(ulProcessID, L"InjectTest32.dll"))
        {
            printf("Inject Success!\\r\\n");
            break;
        }
    default:
        break;
    }
    return 0;
}

 

以上是关于Dll注入:X86/X64 远程线程CreateRemoteThread 注入的主要内容,如果未能解决你的问题,请参考以下文章

Windows x86/ x64 Ring3层注入Dll总结

32位程序注入64位DLL到64位进程

sandbox

executable file and DLL

远程线程DLL注入, 如何释放DLL和结束DLL的线程

VS不同编译器,x86,x64,动态库,静态库交叉引用问题