首页
论坛
课程
招聘
[原创]CreateRemoteThread和RtlCreateUserThread进程创建之后注入DLL(简单的注入器)
2020-4-18 22:58 4579

[原创]CreateRemoteThread和RtlCreateUserThread进程创建之后注入DLL(简单的注入器)

2020-4-18 22:58
4579

1. CreateRemoteThread

本文参考前辈的文章https://www.cnblogs.com/wf751620780/,对原理有了很大了解

首先CreateRemoteThread函数的原型如下,它很像CreateThread函数。不同的是,前者是远程创建线程,后者是在自己的进程下创建线程。我们用前者来注入DLL:

HANDLE WINAPI CreateRemoteThread(
  _In_  HANDLE                 hProcess,
  _In_  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  _In_  SIZE_T                 dwStackSize,
  _In_  LPTHREAD_START_ROUTINE lpStartAddress,
  _In_  LPVOID                 lpParameter,
  _In_  DWORD                  dwCreationFlags,
  _Out_ LPDWORD                lpThreadId
);

两个函数的区别就是第一个句柄行参,hProcess表示创建的新线程属于哪一个进程。

注入的过程如下:

(1).用VirtualAllocEx函数在目标进程的地址空间中分配一块足够大的内存用于保存被注入的dll的路径。
(2).用WriteProcessMemory函数把本进程中保存dll路径的内存中的数据拷贝到第(1)步得到的目标进程的内存中。
(3).用GetProcAddress函数获得LoadLibraryW函数的起始地址。LoadLibraryW函数位于Kernel32.dll中。
(4).用CreateRemoteThread函数让目标进程执行LoadLibraryW来加载被注入的dll。函数结束将返回载入dll后的模块句柄。
(5).用VirtualFreeEx释放第(1)步开辟的内存。
在需要卸载dll时我们可以在上述第(5)步的基础上继续执行以下步骤:
(6).用GetProcAddress函数获得FreeLibrary函数的起始地址。FreeLibrary函数位于Kernel32.dll中。
(7).用CreateRemoteThread函数让目标进程执行FreeLibrary来卸载被注入的dll。(其参数是第(4)步返回的模块句柄)。
如果不在上述步骤基础上执行操作,卸载dll时你需要这么做:
(1).获得被注入的dll在目标进程的模块句柄。
(2).重复上述步骤的第(6)、(7)两步。

需要明确几点,首先用的调用dll函数是LoadLibraryW,因为不管是LoadLibrary还是LoadLibraryA,翻译到底层用的都是宽字符函数LoadLibraryW。第二使用GetProcAddress获得LoadLibraryW函数起始位置。

第三,DLL的绝对路径需要放在用WriteProcessMemory写到目标程序的内存空间下面,而分配这个内存空间使用的函数是VirtualAllocEx

这两个函数原型是:

LPVOID WINAPI VirtualAllocEx(
  _In_     HANDLE hProcess,
  _In_opt_ LPVOID lpAddress,
  _In_     SIZE_T dwSize,
  _In_     DWORD  flAllocationType,
  _In_     DWORD  flProtect
);

BOOL WINAPI WriteProcessMemory(
  _In_  HANDLE  hProcess,
  _In_  LPVOID  lpBaseAddress,
  _In_  LPCVOID lpBuffer,
  _In_  SIZE_T  nSize,
  _Out_ SIZE_T  *lpNumberOfBytesWritten
);

运行环境win10+vs2019。代码如下:

#include "windows.h"
#include "stdio.h"
#include "tlhelp32.h"
#include "io.h"
#include "tchar.h"

//判断某模块(dll)是否在相应的进程中
//dwPID         进程的PID
//szDllPath     查询的dll的完整路径
BOOL CheckDllInProcess(DWORD dwPID, LPCTSTR szDllPath)
{
    BOOL                    bMore = FALSE;
    HANDLE                  hSnapshot = INVALID_HANDLE_VALUE;
    MODULEENTRY32           me = { sizeof(me), };

    if (INVALID_HANDLE_VALUE ==
        (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)))//获得进程的快照
    {
        _tprintf(L"CheckDllInProcess() : CreateToolhelp32Snapshot(%d) failed!!! [%d]\n",
            dwPID, GetLastError());
        return FALSE;
    }
    bMore = Module32First(hSnapshot, &me);//遍历进程内得的所有模块
    for (; bMore; bMore = Module32Next(hSnapshot, &me))
    {
        if (!_tcsicmp(me.szModule, szDllPath) || !_tcsicmp(me.szExePath, szDllPath))//模块名或含路径的名相符
        {
            CloseHandle(hSnapshot);
            return TRUE;
        }
    }
    CloseHandle(hSnapshot);
    return FALSE;
}

//向指定的进程注入相应的模块
//dwPID         目标进程的PID
//szDllPath     被注入的dll的完整路径
BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
    HANDLE                  hProcess = NULL;//保存目标进程的句柄
    LPVOID                  pRemoteBuf = NULL;//目标进程开辟的内存的起始地址
    DWORD                   dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);//开辟的内存的大小
    LPTHREAD_START_ROUTINE  pThreadProc = NULL;//loadLibreayW函数的起始地址
    HMODULE                 hMod = NULL;//kernel32.dll模块的句柄
    BOOL                    bRet = FALSE;
    if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))//打开目标进程,获得句柄
    {
        _tprintf(L"目标进程打开失败  OpenProcess(%d)!!! [%d]\n",
            dwPID, GetLastError());
        goto INJECTDLL_EXIT;
    }
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
        MEM_COMMIT, PAGE_READWRITE);//在目标进程空间开辟一块内存
    if (pRemoteBuf == NULL)
    {
        _tprintf(L"分配空间失败VirtualAllocEx() failed!!! [%d]\n",
            GetLastError());
        goto INJECTDLL_EXIT;
    }
    if (!WriteProcessMemory(hProcess, pRemoteBuf,
        (LPVOID)szDllPath, dwBufSize, NULL))//向开辟的内存复制dll的路径
    {
        _tprintf(L"向目标空间复制路径失败 WriteProcessMemory() failed!!! [%d]\n",
            GetLastError());
        goto INJECTDLL_EXIT;
    }
    hMod = GetModuleHandle(L"kernel32.dll");//获得本进程kernel32.dll的模块句柄
    if (hMod == NULL)
    {
        _tprintf(L"InjectDll() : GetModuleHandle(\"kernel32.dll\") failed!!! [%d]\n",
            GetLastError());
        goto INJECTDLL_EXIT;
    }
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");//获得LoadLibraryW函数的起始地址
    if (pThreadProc == NULL)
    {
        _tprintf(L"InjectDll() : GetProcAddress(\"LoadLibraryW\") failed!!! [%d]\n",
            GetLastError());
        goto INJECTDLL_EXIT;
    }
    if (!CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL))//执行远程线程
    {
        _tprintf(L"InjectDll() : MyCreateRemoteThread() failed!!!\n");
        goto INJECTDLL_EXIT;
    }
INJECTDLL_EXIT:
    bRet = CheckDllInProcess(dwPID, szDllPath);//确认结果
    if (pRemoteBuf) {
        MessageBox(NULL,L"注入成功",L"注入成功",NULL);
        VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
    }
    if (hProcess)
        CloseHandle(hProcess);
    return bRet;
}

//让指定的进程卸载相应的模块
//dwPID         目标进程的PID
//szDllPath     被注入的dll的完整路径,注意:路径不要用“/”来代替“\\”
BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
    BOOL                    bMore = FALSE, bFound = FALSE, bRet = FALSE;
    HANDLE                  hSnapshot = INVALID_HANDLE_VALUE;
    HANDLE                  hProcess = NULL;
    MODULEENTRY32           me = { sizeof(me), };
    LPTHREAD_START_ROUTINE  pThreadProc = NULL;
    HMODULE                 hMod = NULL;
    TCHAR                   szProcName[MAX_PATH] = { 0, };
    if (INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)))
    {
        _tprintf(L"EjectDll() : CreateToolhelp32Snapshot(%d) failed!!! [%d]\n",
            dwPID, GetLastError());
        goto EJECTDLL_EXIT;
    }
    bMore = Module32First(hSnapshot, &me);
    for (; bMore; bMore = Module32Next(hSnapshot, &me))//查找模块句柄
    {
        if (!_tcsicmp(me.szModule, szDllPath) ||
            !_tcsicmp(me.szExePath, szDllPath))
        {
            bFound = TRUE;
            break;
        }
    }
    if (!bFound)
    {
        _tprintf(L"EjectDll() : There is not %s module in process(%d) memory!!!\n",
            szDllPath, dwPID);
        goto EJECTDLL_EXIT;
    }
    if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
    {
        _tprintf(L"EjectDll() : OpenProcess(%d) failed!!! [%d]\n",
            dwPID, GetLastError());
        goto EJECTDLL_EXIT;
    }
    hMod = GetModuleHandle(L"kernel32.dll");
    if (hMod == NULL)
    {
        _tprintf(L"EjectDll() : GetModuleHandle(\"kernel32.dll\") failed!!! [%d]\n",
            GetLastError());
        goto EJECTDLL_EXIT;
    }
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "FreeLibrary");
    if (pThreadProc == NULL)
    {
        _tprintf(L"EjectDll() : GetProcAddress(\"FreeLibrary\") failed!!! [%d]\n",
            GetLastError());
        goto EJECTDLL_EXIT;
    }
    if (!CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL))
    {
        _tprintf(L"EjectDll() : MyCreateRemoteThread() failed!!!\n");
        goto EJECTDLL_EXIT;
    }
    bRet = TRUE;
EJECTDLL_EXIT:
    if (hProcess)
        CloseHandle(hProcess);
    if (hSnapshot != INVALID_HANDLE_VALUE)
        CloseHandle(hSnapshot);
    return bRet;
}

int main()
{
    InjectDll(19220, L"S:\\panny_dll.dll");
    EjectDll(19220, L"S:\\panny_dll.dll");
    return 0;
}

我们启动一个demo并获得它的pid:

然后获得我们需要注入的DLL的绝对路径,开始攻击:

用的查询注入是否成功的方法是用进程快照搜索进程空间是否有目标dll的方式,同反调试技术,运行就会弹出注入成功框:

同时Process Explore可以看到目标dll:

 2. RtlCreateUserThread

 不同于前面直接调用win api,  RtlCreateUserThread是CreateRemoteThread的底层实现,所以使用RtlCreateUserThread的原理是和使用CreateRemoteThread的原理是一样的,这个函数可以实现跨会话创建线程。唯一的问题就是:当我们在Vista 之前的操作系统调用此函数所创建的线程并没有通知给csrss 进程,它没有完整的初始化,在调用一些网络或者其它的系统函数的时候他可能返回失败,而通过CreateRemoteThread 创建的线程没有任何问题,在Vista+的操作系统上调用此函数也没有问题。因此我们需要判断系统版本,当目标系统为Vista-的时候,我们应该通过RtlCreateUserThread创建一个挂起的线程,然后调用CsrClientCallServer通知csrss 这个线程的创建,然后csrss 做相应的记录及初始化工作之后,我们再恢复线程的执行。最后,我们新创建的线程必须自己调用ExitThread退出,否则也会产生崩溃的情况。

LoadLibraryW函数调用原型:

typedef DWORD(WINAPI* pRtlCreateUserThread)(    //函数原型
    IN HANDLE                     ProcessHandle,
    IN PSECURITY_DESCRIPTOR     SecurityDescriptor,
    IN BOOL                     CreateSuspended,
    IN ULONG                    StackZeroBits,
    IN OUT PULONG                StackReserved,
    IN OUT PULONG                StackCommit,
    IN LPVOID                    StartAddress,
    IN LPVOID                    StartParameter,
    OUT HANDLE                     ThreadHandle,
    OUT LPVOID                    ClientID
    );

LoadLibraryW

函数shellcode:

 BYTE StaticShellCode[31] = { 0xE8,0,0,0,0,  // call (5字节)           4
        0x5D,           // pop ebp              5
        0x8B,0xC5,  // mov eax,ebp          7
        0x83,0xC0,0x1A, // add eax,1a       10
        0x50,       // push eax             11
        0xB8,0,0,0,0,   // mov eax,LoadLibraryW 16
        0xFF,0xD0,  // call eax             18
        0x6A,0,     // push 0               20
        0xB8,0,0,0,0,   // mov eax,ExitThread   25
        0xFF,0xD0,  // call eax             27
        0xC3,   // ret 4                28
        0};

代码如下:

(整体逻辑一样,就不详细备注)

#include <Windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <iostream>
#include <string>
DWORD findPidByName(const char* pname) //获得pid函数,对上面的查找进行改进
{
    HANDLE h;
    PROCESSENTRY32 procSnapshot;
    h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    procSnapshot.dwSize = sizeof(PROCESSENTRY32);

    do
    {
        if (!_stricmp(procSnapshot.szExeFile, pname))
        {
            DWORD pid = procSnapshot.th32ProcessID;
            CloseHandle(h);
#ifdef _DEBUG
            printf(("[+]进程地址: %ld\n"), pid);
#endif
            return pid;
        }
    } while (Process32Next(h, &procSnapshot));

    CloseHandle(h);
    return 0;
}

typedef DWORD(WINAPI* pRtlCreateUserThread)(    //函数申明
    IN HANDLE                     ProcessHandle,
    IN PSECURITY_DESCRIPTOR     SecurityDescriptor,
    IN BOOL                     CreateSuspended,
    IN ULONG                    StackZeroBits,
    IN OUT PULONG                StackReserved,
    IN OUT PULONG                StackCommit,
    IN LPVOID                    StartAddress,
    IN LPVOID                    StartParameter,
    OUT HANDLE                     ThreadHandle,
    OUT LPVOID                    ClientID
    );

DWORD RtlCreateUserThread(LPCSTR pszLibFile, DWORD dwProcessId)
{
    pRtlCreateUserThread RtlCreateUserThread = NULL;
    HANDLE  hRemoteThread = NULL;

    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); //打开目标进程pid
    if (hProcess == NULL)
    {
        printf("[-] Error: Could not open process for PID (%d).\n", dwProcessId);
        exit(1);
    }

    LPVOID LoadLibraryAddress = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");  //load函数地址
    if (LoadLibraryAddress == NULL)
    {
        printf(("[-] Error: Could not find LoadLibraryA function inside kernel32.dll library.\n"));
        exit(1);
    }

    RtlCreateUserThread = (pRtlCreateUserThread)GetProcAddress(GetModuleHandle(("ntdll.dll")), ("RtlCreateUserThread"));//获取Rtl函数地址
    if (RtlCreateUserThread == NULL)
    {
        exit(1);
    }

#ifdef _DEBUG
    printf(("[+]RtlCreateUserThread函数地址: 0x%08x\n"), (UINT)RtlCreateUserThread);
    printf(("[+]LoadLibraryA函数地址: 0x%08x\n"), (UINT)LoadLibraryAddress);
#endif

    DWORD dwSize = (strlen(pszLibFile) + 1) * sizeof(char);

    LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);//开辟一段地址
    if (lpBaseAddress == NULL)
    {
        printf(("[-] Error: Could not allocate memory inside PID (%d).\n"), dwProcessId);
        exit(1);
    }

    BOOL bStatus = WriteProcessMemory(hProcess, lpBaseAddress, pszLibFile, dwSize, NULL);  //目标dll写入
    if (bStatus == 0)
    {
        printf(("[-] Error: Could not write any bytes into the PID (%d) address space.\n"), dwProcessId);
        return(1);
    }

    bStatus = (BOOL)RtlCreateUserThread(
        hProcess,
        NULL,
        0,
        0,
        0,
        0,
        LoadLibraryAddress,  //shellcode
        lpBaseAddress,
        &hRemoteThread,
        NULL);
    if (bStatus < 0)
    {
        printf(("[-]注入失败\n"));
        return(1);
    }
    else
    {
        printf(("[+]注入成功...\n"));
        WaitForSingleObject(hRemoteThread, INFINITE);

        CloseHandle(hProcess);
        VirtualFreeEx(hProcess, lpBaseAddress, dwSize, MEM_RELEASE);
        return(0);
    }

    return(0);
}
int main()
{
    const char* name = ("菜单dome.exe");

    DWORD pId = findPidByName(name);
    LPCSTR location = ("S:\\panny_dll.dll");

    RtlCreateUserThread(location, pId);
}

还是运行菜单dome.exe,:

 (其实变蓝就已经在提示有dll注入了)





《0day安全 软件漏洞分析技术(第二版)》第三次再版印刷预售开始!

收藏
点赞0
打赏
分享
最新回复 (9)
雪    币: 55
活跃值: 活跃值 (152)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
killpy 活跃值 2 2020-4-19 08:41
2
0
辛苦了
雪    币: 4088
活跃值: 活跃值 (245)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kxzpy 活跃值 2020-4-19 11:55
3
0
注入之后,能干些啥呢?
雪    币: 4165
活跃值: 活跃值 (154)
能力值: ( LV8,RANK:135 )
在线值:
发帖
回帖
粉丝
菜鸟m号 活跃值 1 2020-4-19 18:50
4
0
kxzpy 注入之后,能干些啥呢?
实在抱歉,我这个只是写个demo了解这两个函数注入的原理,没有增加实质内容,权当学习笔记了。
雪    币: 297
活跃值: 活跃值 (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gaoliangk 活跃值 2020-4-19 23:46
5
0
感谢了
雪    币: 774
活跃值: 活跃值 (41)
能力值: ( LV12,RANK:280 )
在线值:
发帖
回帖
粉丝
Ox9A82 活跃值 3 2020-4-21 01:17
6
0
建议自己映射
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wx_。胡圈圈 。 活跃值 2020-4-21 13:30
7
0
CreateRemoteThread之后不需要等待吗?WaitForSingleObject?
雪    币: 4165
活跃值: 活跃值 (154)
能力值: ( LV8,RANK:135 )
在线值:
发帖
回帖
粉丝
菜鸟m号 活跃值 1 2020-4-21 16:45
8
0
Ox9A82 建议自己映射
好的前辈
上传的附件:
雪    币: 4165
活跃值: 活跃值 (154)
能力值: ( LV8,RANK:135 )
在线值:
发帖
回帖
粉丝
菜鸟m号 活跃值 1 2020-4-21 16:45
9
0
wx_。胡圈圈 。 CreateRemoteThread之后不需要等待吗?WaitForSingleObject?
并没有涉及到进程之间的互斥,只是创建一个线程插入目标dll
雪    币: 175
活跃值: 活跃值 (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
我的研究 活跃值 2020-7-24 16:40
10
0
demo 源码呢
游客
登录 | 注册 方可回帖
返回