注入技术-远程线程DLL注入

selph
selph
发布于 2024-08-11 / 73 阅读
0
0

注入技术-远程线程DLL注入

原理介绍

远程线程指的是跨进程,可以在别的进程里创建线程

用到的API函数是 CreateRemoteThread

使用 CreateRemoteThread 创建远程线程的时候,需要提供目标进程的句柄,这个可以通过OpenProcess得到,然后还需要一个函数地址和一个参数地址

这个函数地址是在目标进程里创建线程执行的函数,而这个函数只能带有一个参数

说也巧,加载DLL的函数 LoadLibrary 刚好也只要一个参数,那就是DLL文件的路径

那么,我们只要找到 LoadLibrary 函数的地址,然后把DLL文件的路径写入到目标进程内存空间中去,得到参数的地址,就可以实现创建远程线程的DLL注入了

注意

这里先提一下一个可能会遇到的坑,DLL注入过程中,如果目标进程是64位的,那么我们写的程序也得是64位的,注入的DLL也必须是64位的,不然注入会失败!!

CreateRemoteThread 函数介绍

HANDLE
WINAPI
CreateRemoteThread(
    _In_ HANDLE hProcess,        //目标进程句柄
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,    //安全属性一般NULL
    _In_ SIZE_T dwStackSize,    //缺省堆栈大小 一般NULL
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,    //函数地址指针
    _In_opt_ LPVOID lpParameter,    //函数参数
    _In_ DWORD dwCreationFlags,        //创建后线程的状态,0表示立即执行,暂停CREATE_SUSPENDED需要调用ResumeThread去恢复
    _Out_opt_ LPDWORD lpThreadId    //用来接收新创建线程的线程ID,可以为NULL
    );

这个函数需要用到的进程句柄、函数地址、参数地址这三个参数需要我们提前去获得

获得进程ID

通过OpenProcess获得即可

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
if (hProcess == NULL) {
    return -1;
}

进程PID可以通过任务管理器自行查看

当然也可以通过代码来获取,比如通过FindWindow根据窗口名获取窗口句柄,然后使用GetWindowThreadProcessId来获取进程PID,这里就不细讲了

获取函数地址

由于kernel32.dll中的函数地址在windows上是固定的位置,所以在本进程中获取到的地址在别的进程中也适用:

//获取LoadLibraryA函数地址
PTHREAD_START_ROUTINE load_start_addr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");

获取参数地址

这里需要打开进程,向目标进程申请一片地址空间,然后向这个空间写入dll文件的路径

//打开进程,获取进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
if (hProcess == NULL) {
    return -1;
}

//向目标进程申请空间,返回首地址指针
int nDllSize = strlen(dll) + 1;
LPVOID pDllAddr = VirtualAllocEx(hProcess, NULL, nDllSize, MEM_COMMIT, PAGE_READWRITE);
if (pDllAddr == NULL) {
    CloseHandle(hProcess);
    return -1;
}

//向目标进程申请的空间写入dll绝对路径
DWORD dwWriteNum = 0;
WriteProcessMemory(hProcess, pDllAddr, dll, nDllSize, &dwWriteNum);

如果不知道dll路径有没有成功写入目标进程,可通过CE软件打开目标进程查看内存来判断

DLL注入

接下来调用CreateRemoteThread进行dll注入即可

//DLL注入
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, load_start_addr, pDllAddr, 0, NULL);

//等待目标dll的加载结束
WaitForSingleObject(hThread, INFINITE);

//关闭刚刚创建的句柄
CloseHandle(hThread);
CloseHandle(hProcess);

效果演示

image-20201007145131935

卸载注入的DLL

卸载注入的DLL,和注入DLL的原理相同,都是通过创建远程线程去执行一个只有一个参数的函数来进行

卸载用到的函数是:FreeLibrary,参数为模块句柄,这个模块句柄可通过对目标进程创建快照,然后遍历其中的模块来获得,关于创建快照遍历模块相关内容,可从我以前的笔记中找到,这里就不再啰嗦了。

FreeLibrary 函数介绍

BOOL
WINAPI
FreeLibrary(
    _In_ HMODULE hLibModule    //模块句柄,可通过遍历获得
    );

代码实现

void CInjectDllDlg::UnInjectDll(DWORD dwPid, LPCSTR szDllName)
{
    // TODO: 在此处添加实现代码.
    if (dwPid == 0 || strlen(szDllName) == 0)
    {
        return;
    }

    //创建DLL快照
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);
    MODULEENTRY32 me32;
    me32.dwSize = sizeof(me32);
	
    //这里寻找指定的dll信息,找到了后,结构体里的hModule参数里会存有dll模块句柄
    BOOL bRet = Module32First(hSnap,&me32);
    TCHAR szDllNameW[MAX_PATH] = { 0 };
    MultiByteToWideChar(CP_ACP, NULL, szDllName, -1, szDllNameW, strlen(szDllName));
    while (bRet) {
        if (lstrcmp(_wcsupr(me32.szExePath), _wcsupr(szDllNameW)) == 0) {
            break;
        }
        bRet = Module32Next(hSnap, &me32);
    }
    CloseHandle(hSnap);

    char* pFunName = "FreeLibrary";

    // 打开目标进程
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);

    if (hProcess == NULL)
    {
        return;
    }

    // 计算欲注入DLL文件完整路径的长度
    int nDllLen = strlen(szDllName) + sizeof(TCHAR);

    //获得函数的地址
    FARPROC pFunAddr = GetProcAddress(GetModuleHandle(L"kernel32.dll"), pFunName);

    // 创建远程线程
    HANDLE hThread = CreateRemoteThread(hProcess,
        NULL, 0,
        (PTHREAD_START_ROUTINE)pFunAddr,//LoadLibraryA函数
        me32.hModule,        //参数是dll地址指针
        0, NULL);
    //WaitForSingleObject(hThread, INFINITE);//等待线程执行完毕

    CloseHandle(hThread);
    CloseHandle(hProcess);
}

评论