0x00前言

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

0x01正文

什么是远程线程注入?

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

前置的一些知识

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

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"

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
检索有关系统快照中遇到的第一个进程的信息。

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函数来获得下一个进程的句柄。

BOOL Process32Next(
  [in]  HANDLE           hSnapshot,
  [out] LPPROCESSENTRY32 lppe
);

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

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
在指定进程的虚拟地址空间内保留、提交、或更改内存的状态

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
在指定的进程中将数据写入内存区域,要写入的整个区域必须可访问,否则操作失败

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)检索导出函数(也称为过程)或变量的地址。

FARPROC GetProcAddress(
  [in] HMODULE hModule,
  [in] LPCSTR  lpProcName
);

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

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
将指定的模块加载到调用进程的地址空间中。指定的 模块可能导致加载其他模块。

HMODULE LoadLibraryW(
  [in] LPCWSTR lpLibFileName
);

如何获取PID

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;
}

79725-viqem3w6za.png

远程线程注入实现原理

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。

实现远程线程的代码

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
61050-mfp0b7nu0jc.png

实现效果&&完整代码

80088-1r3elirvf1s.png

80656-g2ef4bcjnej.png

#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地址");
}

发表评论