首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >DLL远程线程注入

DLL远程线程注入

作者头像
YanXia
发布2023-04-07 10:10:50
发布2023-04-07 10:10:50
9680
举报
文章被收录于专栏:YX’blogYX’blog

0x00前言

RT,最近正在学习DLL注入。尝试写篇总结

0x01正文

什么是远程线程注入?

远程线程注入是指一个进程在另一个进程中创建线程的技术。

前置的一些知识

CreateToolhelp32Snapshot函数 https://learn.microsoft.com/zh-cn/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot 获取指定进程的快照,以及这些进程使用的堆、模块和线程。(也就是说,我们可以利用这个函数来获取进程的PID)

代码语言:javascript
复制
HANDLE CreateToolhelp32Snapshot(
  [in] DWORD dwFlags,
  [in] DWORD th32ProcessID
);

PROCESSENTRY32结构体 https://learn.microsoft.com/zh-cn/windows/win32/api/tlhelp32/ns-tlhelp32-processentry32 用来存放快照进程信息的一个结构体。(存放进程信息和调用成员输出进程信息)用来Process32First指向第一个进程信息,并将进程信息抽取到PROCESSENTRY32中。用Process32Next指向下一条进程信息。 引用所需包含的头文件:#include"tlhelp32.h"

代码语言:javascript
复制
typedef struct tagPROCESSENTRY32 {
  DWORD     dwSize;
  DWORD     cntUsage;
  DWORD     th32ProcessID;
  ULONG_PTR th32DefaultHeapID;
  DWORD     th32ModuleID;
  DWORD     cntThreads;
  DWORD     th32ParentProcessID;
  LONG      pcPriClassBase;
  DWORD     dwFlags;
  CHAR      szExeFile[MAX_PATH];
} PROCESSENTRY32;

Process32First函数 https://learn.microsoft.com/zh-cn/windows/win32/api/tlhelp32/nf-tlhelp32-process32first 检索有关系统快照中遇到的第一个进程的信息。

代码语言:javascript
复制
BOOL Process32First(
  [in]      HANDLE           hSnapshot,
  [in, out] LPPROCESSENTRY32 lppe
);

Process32Next函数 https://learn.microsoft.com/zh-cn/windows/win32/api/tlhelp32/nf-tlhelp32-process32next Process32Next是一个进程获取函数,当我们利用函数CreateToolhelp32Snapshot()获得当前运行进程的快照后,我们可以利用Process32Next函数来获得下一个进程的句柄。

代码语言:javascript
复制
BOOL Process32Next(
  [in]  HANDLE           hSnapshot,
  [out] LPPROCESSENTRY32 lppe
);

OpenProcess函数 https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess 打开现有的本地进程

代码语言:javascript
复制
HANDLE OpenProcess(
  [in] DWORD dwDesiredAccess,
  [in] BOOL  bInheritHandle,
  [in] DWORD dwProcessId
);

VirtualAllocEx函数 https://learn.microsoft.com/zh-cn/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex 在指定进程的虚拟地址空间内保留、提交、或更改内存的状态

代码语言:javascript
复制
LPVOID VirtualAllocEx(
  [in]           HANDLE hProcess,
  [in, optional] LPVOID lpAddress,
  [in]           SIZE_T dwSize,
  [in]           DWORD  flAllocationType,
  [in]           DWORD  flProtect
);

WriteProcessMemory函数 https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory 在指定的进程中将数据写入内存区域,要写入的整个区域必须可访问,否则操作失败

代码语言:javascript
复制
BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);

GetProcAddress函数 https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress 从指定的动态链接库(DLL)检索导出函数(也称为过程)或变量的地址。

代码语言:javascript
复制
FARPROC GetProcAddress(
  [in] HMODULE hModule,
  [in] LPCSTR  lpProcName
);

CreateRemoteThread函数 https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread 在另一个进程的虚拟地址空间中创建运行的线程

代码语言:javascript
复制
HANDLE 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
);

LoadLibrary函数(有A系/W系(分别代表了ascii码和Unicode码)) https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw 将指定的模块加载到调用进程的地址空间中。指定的 模块可能导致加载其他模块。

代码语言:javascript
复制
HMODULE LoadLibraryW(
  [in] LPCWSTR lpLibFileName
);

如何获取PID

代码语言:javascript
复制
DWORD GetProcess(LPCTSTR lpProcessName) { //获取进程的句柄
    DWORD processId=0;
    PROCESSENTRY32 p32;
    HANDLE processAll = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//获取到系统全部进程
    if (processAll == INVALID_HANDLE_VALUE) //CreateToolhelp32Snapshot函数失败返回值
    {
        printf("CreateToolhelp32Snapshot,error:%d",GetLastError());
        return 0;
    }
    p32.dwSize = sizeof(PROCESSENTRY32);// 所有快照进程信息的大小
    Process32First(processAll, &p32);//从进程中获取倒
    do {
        if (!lstrcmp(p32.szExeFile , lpProcessName)) {//szExeFile为进程的可执行文件的名称
            processId = p32.th32ProcessID;
            break;
        }
    } while (Process32Next(processAll, &p32));//获取下一个进程句柄
    CloseHandle(processAll); //关闭句柄
    return processId;
}

远程线程注入实现原理

dll远程线程注入的核心是CreateRemoteThread函数,利用该函数可以在个进程空间中创建一个线程。从CreateRemoteThread需要的传递的参数中可知,我们需要目标进程空间的多线程函数地址,以及多线程参数。也就是说我们可以把LoadLibrary函数的地址给作为多线程函数的地址(LoadLibrary函数是用来动态加载DLL的),然后将一个DLL的地址作为多线程的参数。这样就可以成功在目标的空间中利用CreateRemoteThread创建一个多线程。 但是由于Windows引入了基址随机化ASLR安全机制,每次开机or在不同的系统中,系统DLL的加载基址都不一样,也就是说DLL的导出函数地址也都不一样。不过,像(kernel32,ntdll)的加载基地址在系统启动后是固定不变的,也就是说在任何一个程序调用它们的地址都一样,导出函数地址也一致,所以自己程序中的LoadLibrary函数与其他程序的LoadLibrary函数地址也一致,所以我们可以直接用GetProcAddress函数去获取LoadLibrary的地址。 所以说,我们可以先用VirtualAllocEx函数在对方的进程中申请一块内存,然后用WriteProcessMemory函数将指定DLL写入到目标的进程空间中,然后利用GetProcAddress函数去获取LoadLibrary的地址,最后利用CreateRemoteThread函数创建线程并注入进目标的进程当中,最后等待线程结束后释放DLL空间并关闭线程。这样就实现了远程线程注入DLL。

实现远程线程的代码

代码语言:javascript
复制
DWORD CreatRemoteThreadInjectDll(DWORD pid, LPCTSTR DllName) {
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);//打开注入进程获取进程句柄
    if (hProcess == NULL) {
        printf("OpenProcess error\n");
        return FALSE;
    }
    DWORD size = (lstrlen(DllName) + 1) * sizeof(TCHAR);
    LPVOID pAllocMemory = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT, PAGE_READWRITE);//为注入的进程申请内存
    if (pAllocMemory == NULL) {
        printf("VirtualAllocEx error\n");
        return FALSE;
    }
    BOOL WPM = WriteProcessMemory(hProcess, pAllocMemory, DllName, size, NULL); //写入内存
    if (WPM == 0) {
        printf("WPM error!\n");
        return FALSE;
    }
    FARPROC pThread = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibrary");//获取LoadLibrary的地址
    if (pThread == NULL) {
        printf("GetProcAddress error \n");
        return FALSE;
    }
    //创建远程注入线程
    HANDLE hRemoteThred = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pThread, pAllocMemory, 0, NULL);
    if (hRemoteThred == NULL) {
        printf("CreateRemoteThread error \n");
        return FALSE;
    }
    WaitForSingleObject(hRemoteThred, -1);//等待线程结束
    VirtualFreeEx(hProcess, pAllocMemory, size, MEM_DECOMMIT);//释放DLL空间
    CloseHandle(hProcess);
    return true;
}

dll代码

简单写来个弹窗dll

实现效果&&完整代码

代码语言:javascript
复制
#include<stdio.h>
#include <Windows.h>
#include <string.h>
#include <TlHelp32.h>

DWORD GetProcess(LPCTSTR lpProcessName) { //获取进程的句柄
    DWORD processId=0;
    PROCESSENTRY32 p32;
    HANDLE processAll = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//获取到系统全部进程
    if (processAll == INVALID_HANDLE_VALUE) //CreateToolhelp32Snapshot函数失败返回值
    {
        printf("CreateToolhelp32Snapshot,error:%d",GetLastError());
        return 0;
    }
    p32.dwSize = sizeof(PROCESSENTRY32);// 所有快照进程信息的大小
    Process32First(processAll, &p32);//从进程中获取倒
    do {
        if (!lstrcmp(p32.szExeFile , lpProcessName)) {//szExeFile为进程的可执行文件的名称
            processId = p32.th32ProcessID;
            break;
        }
    } while (Process32Next(processAll, &p32));//获取下一个进程句柄
    CloseHandle(processAll); //关闭句柄
    return processId;
}

DWORD CreatRemoteThreadInjectDll(DWORD pid, LPCTSTR DllName) {
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);//打开注入进程获取进程句柄
    if (hProcess == NULL) {
        printf("OpenProcess error\n");
        return FALSE;
    }
    DWORD size = (lstrlen(DllName) + 1) * sizeof(TCHAR);
    LPVOID pAllocMemory = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT, PAGE_READWRITE);//为注入的进程申请内存
    if (pAllocMemory == NULL) {
        printf("VirtualAllocEx error\n");
        return FALSE;
    }
    BOOL WPM = WriteProcessMemory(hProcess, pAllocMemory, DllName, size, NULL); //写入内存
    if (pAllocMemory == 0) {
        printf("WPM error!\n");
        return FALSE;
    }
    FARPROC pThread = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibrary");//获取LoadLibrary的地址
    if (pThread == NULL) {
        printf("GetProcAddress error \n");
        return FALSE;
    }
    //创建远程注入线程
    HANDLE hRemoteThred = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pThread, pAllocMemory, 0, NULL);
    if (hRemoteThred == NULL) {
        printf("CreateRemoteThread error \n");
        return FALSE;
    }
    WaitForSingleObject(hRemoteThred, -1);//等待线程结束
    VirtualFreeEx(hProcess, pAllocMemory, size, MEM_DECOMMIT);//释放DLL空间
    CloseHandle(hProcess);
    return true;
}
int main(int argc, TCHAR* argv[]){
    DWORD PID = GetProcess(L"notepad.exe");
    //printf("notepad 的进程为:%d",PID);
    CreatRemoteThreadInjectDll(PID, L"dll地址");
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023年03月19日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00前言
  • 0x01正文
    • 什么是远程线程注入?
    • 前置的一些知识
    • 如何获取PID
    • 远程线程注入实现原理
    • 实现远程线程的代码
    • dll代码
  • 实现效果&&完整代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档